diff --git a/README.md b/README.md index 80eb0d3..5dec0fc 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,9 @@ Once the first none-empty line is unrecognized (no command found), the parser wi This can be repeated multiple times but will only be used when Method == POST. The Content-Type header is set automatically too. +`Compress[ed]` +: Adds support for gzip,deflate compression to the connection. + `Call ` : This prepares the actual call by providing the URL to be called. Variables in the form of `@` are replaced accordingly. diff --git a/URestemplateApp.pas b/URestemplateApp.pas index b0dd428..0767d88 100644 --- a/URestemplateApp.pas +++ b/URestemplateApp.pas @@ -24,11 +24,25 @@ interface uses Classes, SysUtils, CustApp, - IniFiles, fphttpclient, jsonparser, JTemplate, - UFilter; + IniFiles, jsonparser, JTemplate, + UFilter, IdHTTP, IdMultipartFormData; type + { TRestHTTP } + + TRestHTTP = class(TIdHTTP) + public + procedure Perform(AMethod, AURL: String; ARequest, AResponse: TStream); + end; + + { TRestRequest } + + TRestRequest = class(TIdHTTPRequest) + public + procedure Prepare; + end; + { TRestemplateApplication } TRestemplateApplication = class(TCustomApplication) @@ -38,9 +52,10 @@ type FSessionIni: TIniFile; FTemplateName: String; FParser: TJTemplateParser; - FHttp: TFPHTTPClient; + FHttp: TRestHTTP; + FRequest: TRestRequest; FContent: TStringList; - FFormFields: TStringList; + FFormFields: TIdMultiPartFormDataStream; FFilters: TFilterList; FBeautify: Boolean; FURL: String; @@ -67,7 +82,8 @@ type implementation uses - strutils, crt, UCRTHelper, fpjson, DOM, XMLRead, XMLWrite, vinfo; + strutils, crt, UCRTHelper, fpjson, DOM, XMLRead, XMLWrite, vinfo, + IdCompressorZLib; type TContentType = (ctOther, ctJSON, ctXML); @@ -84,6 +100,22 @@ begin Result := (ContentType <> ctOther); end; +{ TRestRequest } + +procedure TRestRequest.Prepare; +begin + //Parse RawHeaders, since they have precendence and should not be overridden! + ProcessHeaders; +end; + +{ TRestHTTP } + +procedure TRestHTTP.Perform(AMethod, AURL: String; ARequest, AResponse: TStream + ); +begin + DoRequest(AMethod, AURL, ARequest, AResponse, []); +end; + { TRestemplateApplication } procedure TRestemplateApplication.DoRun; @@ -199,6 +231,11 @@ begin Result := True; CmdFormField(Copy(ALine, 11, Length(ALine))); end else + if AnsiStartsStr('Compress', ALine) then + begin + Result := True; + FHttp.Compressor := TIdCompressorZLib.Create(FHttp); + end else if AnsiStartsStr('Call ', ALine) then begin Result := True; @@ -255,7 +292,11 @@ begin varName := Trim(Copy(AHeader, 1, i - 1)); varValue := Trim(Copy(AHeader, i + 1, Length(AHeader))); - FHttp.AddHeader(varName, varValue); + //Special handling is required for Indy + {if SameText(varName, 'Accept-Encoding') then + FHttp.Request.AcceptEncoding := varValue + else} + FRequest.RawHeaders.AddValue(varName, varValue); end; procedure TRestemplateApplication.CmdBasicAuth(AData: String); @@ -268,8 +309,9 @@ begin while (i < Length(AData)) and (AData[i] <> separator) do Inc(i); - FHttp.UserName := Copy(AData, 2, i - 2); - FHttp.Password := Copy(AData, i + 1, Length(AData)); + FRequest.Username := Copy(AData, 2, i - 2); + FRequest.Password := Copy(AData, i + 1, Length(AData)); + FRequest.BasicAuthentication := True; end; procedure TRestemplateApplication.CmdHighlight(AData: String); @@ -278,10 +320,18 @@ begin end; procedure TRestemplateApplication.CmdFormField(AData: String); +var + i: Integer; begin + i := 1; FParser.Content := AData; FParser.Replace; - FFormFields.Add(FParser.Content); + AData := FParser.Content; + + while (i < Length(AData)) and (AData[i] <> '=') do + Inc(i); + + FFormFields.AddFormField(Copy(AData, 1, i - 1), Copy(AData, i + 1, Length(AData))); end; procedure TRestemplateApplication.ProcessCall(AURL: String); @@ -304,23 +354,24 @@ begin if FContent.Count > 0 then begin request := TMemoryStream.Create; - // Variable replacement FParser.Content := FContent.Text; FParser.Replace; FContent.Text := FParser.Content; FContent.SaveToStream(request); - request.Position := 0; - FHttp.RequestBody := request; end; try - //Special handling for formdata - if SameText('POST', FMethod) and (FFormFields.Count > 0) then - FHttp.FormPost(AURL, FFormFields, response) - else - FHttp.HTTPMethod(FMethod, AURL, response, []); + FRequest.Prepare; + FHttp.Request := FRequest; + FHttp.HTTPOptions := FHttp.HTTPOptions + [hoNoProtocolErrorException]; + if FFormFields.Size > 0 then + begin + FHttp.Request.ContentType := FFormFields.RequestContentType; + FHttp.Perform(FMethod, AURL, FFormFields, response); + end else + FHttp.Perform(FMethod, AURL, request, response); except on E: Exception do begin @@ -331,16 +382,16 @@ begin end; writeln; - writeln('Status: ', FHttp.ResponseStatusCode, ' (', FHttp.ResponseStatusText, ')'); + writeln('Status: ', FHttp.ResponseCode, ' (', FHttp.ResponseText, ')'); writeln; writeln('Headers:'); - for s in FHttp.ResponseHeaders do + for s in FHttp.Response.RawHeaders do writeln(' ', s); writeln; response.Position := 0; - if FBeautify and IdentifyContentType(FHttp.GetHeader(FHttp.ResponseHeaders, 'Content-Type'), contentType) then + if FBeautify and IdentifyContentType(FHttp.Response.ContentType, contentType) then begin case contentType of ctJSON: @@ -474,8 +525,10 @@ begin FSessionIni := TIniFile.Create(FConfigDir + 'session.ini'); FContent := TStringList.Create; - FFormFields := TStringList.Create; - FHttp := TFPHTTPClient.Create(Self); + FFormFields := TIdMultiPartFormDataStream.Create; + FHttp := TRestHTTP.Create(nil); + FRequest := TRestRequest.Create(FHttp); + FRequest.RawHeaders.AddValue('User-Agent', 'Mozilla/4.0 (compatible; restemplate ' + VersionInfo.GetProductVersionString + ')'); FFilters := TFilterList.Create; FParser := TJTemplateParser.Create; end; @@ -486,6 +539,7 @@ begin FContent.Free; FFormFields.Free; FHttp.Free; + //TODO Owned? FRequest.Free; FFilters.Free; FParser.Free; inherited Destroy; diff --git a/indy/Core/AboutBackground.bmp b/indy/Core/AboutBackground.bmp new file mode 100644 index 0000000..681b26d Binary files /dev/null and b/indy/Core/AboutBackground.bmp differ diff --git a/indy/Core/AboutIndyNET.resources b/indy/Core/AboutIndyNET.resources new file mode 100644 index 0000000..364963d Binary files /dev/null and b/indy/Core/AboutIndyNET.resources differ diff --git a/indy/Core/AboutProg.res b/indy/Core/AboutProg.res new file mode 100644 index 0000000..28e5361 Binary files /dev/null and b/indy/Core/AboutProg.res differ diff --git a/indy/Core/IconsDotNet/TIdAntiFreeze.bmp b/indy/Core/IconsDotNet/TIdAntiFreeze.bmp new file mode 100644 index 0000000..a6b50ea Binary files /dev/null and b/indy/Core/IconsDotNet/TIdAntiFreeze.bmp differ diff --git a/indy/Core/IconsDotNet/TIdCmdTCPClient.bmp b/indy/Core/IconsDotNet/TIdCmdTCPClient.bmp new file mode 100644 index 0000000..128df20 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdCmdTCPClient.bmp differ diff --git a/indy/Core/IconsDotNet/TIdCmdTCPServer.bmp b/indy/Core/IconsDotNet/TIdCmdTCPServer.bmp new file mode 100644 index 0000000..14918b4 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdCmdTCPServer.bmp differ diff --git a/indy/Core/IconsDotNet/TIdConnectionIntercept.bmp b/indy/Core/IconsDotNet/TIdConnectionIntercept.bmp new file mode 100644 index 0000000..44f2bf5 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdConnectionIntercept.bmp differ diff --git a/indy/Core/IconsDotNet/TIdICMPClient.bmp b/indy/Core/IconsDotNet/TIdICMPClient.bmp new file mode 100644 index 0000000..9957afa Binary files /dev/null and b/indy/Core/IconsDotNet/TIdICMPClient.bmp differ diff --git a/indy/Core/IconsDotNet/TIdIOHandlerStack.bmp b/indy/Core/IconsDotNet/TIdIOHandlerStack.bmp new file mode 100644 index 0000000..3dcd690 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdIOHandlerStack.bmp differ diff --git a/indy/Core/IconsDotNet/TIdIOHandlerStream.bmp b/indy/Core/IconsDotNet/TIdIOHandlerStream.bmp new file mode 100644 index 0000000..482b3de Binary files /dev/null and b/indy/Core/IconsDotNet/TIdIOHandlerStream.bmp differ diff --git a/indy/Core/IconsDotNet/TIdIPMCastClient.bmp b/indy/Core/IconsDotNet/TIdIPMCastClient.bmp new file mode 100644 index 0000000..f5912c9 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdIPMCastClient.bmp differ diff --git a/indy/Core/IconsDotNet/TIdIPMCastServer.bmp b/indy/Core/IconsDotNet/TIdIPMCastServer.bmp new file mode 100644 index 0000000..8d5ae46 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdIPMCastServer.bmp differ diff --git a/indy/Core/IconsDotNet/TIdInterceptSimLog.bmp b/indy/Core/IconsDotNet/TIdInterceptSimLog.bmp new file mode 100644 index 0000000..0badaba Binary files /dev/null and b/indy/Core/IconsDotNet/TIdInterceptSimLog.bmp differ diff --git a/indy/Core/IconsDotNet/TIdInterceptThrottler.bmp b/indy/Core/IconsDotNet/TIdInterceptThrottler.bmp new file mode 100644 index 0000000..58dd446 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdInterceptThrottler.bmp differ diff --git a/indy/Core/IconsDotNet/TIdLogDebug.bmp b/indy/Core/IconsDotNet/TIdLogDebug.bmp new file mode 100644 index 0000000..cb127a4 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdLogDebug.bmp differ diff --git a/indy/Core/IconsDotNet/TIdLogEvent.bmp b/indy/Core/IconsDotNet/TIdLogEvent.bmp new file mode 100644 index 0000000..1dbd010 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdLogEvent.bmp differ diff --git a/indy/Core/IconsDotNet/TIdLogFile.bmp b/indy/Core/IconsDotNet/TIdLogFile.bmp new file mode 100644 index 0000000..245a10c Binary files /dev/null and b/indy/Core/IconsDotNet/TIdLogFile.bmp differ diff --git a/indy/Core/IconsDotNet/TIdLogStream.bmp b/indy/Core/IconsDotNet/TIdLogStream.bmp new file mode 100644 index 0000000..a935b7e Binary files /dev/null and b/indy/Core/IconsDotNet/TIdLogStream.bmp differ diff --git a/indy/Core/IconsDotNet/TIdSchedulerOfThreadDefault.bmp b/indy/Core/IconsDotNet/TIdSchedulerOfThreadDefault.bmp new file mode 100644 index 0000000..49599f4 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdSchedulerOfThreadDefault.bmp differ diff --git a/indy/Core/IconsDotNet/TIdSchedulerOfThreadPool.bmp b/indy/Core/IconsDotNet/TIdSchedulerOfThreadPool.bmp new file mode 100644 index 0000000..e163c16 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdSchedulerOfThreadPool.bmp differ diff --git a/indy/Core/IconsDotNet/TIdServerIOHandlerStack.bmp b/indy/Core/IconsDotNet/TIdServerIOHandlerStack.bmp new file mode 100644 index 0000000..d7c5c5e Binary files /dev/null and b/indy/Core/IconsDotNet/TIdServerIOHandlerStack.bmp differ diff --git a/indy/Core/IconsDotNet/TIdSimpleServer.bmp b/indy/Core/IconsDotNet/TIdSimpleServer.bmp new file mode 100644 index 0000000..0bb4c36 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdSimpleServer.bmp differ diff --git a/indy/Core/IconsDotNet/TIdSocksInfo.bmp b/indy/Core/IconsDotNet/TIdSocksInfo.bmp new file mode 100644 index 0000000..82cab02 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdSocksInfo.bmp differ diff --git a/indy/Core/IconsDotNet/TIdTCPClient.bmp b/indy/Core/IconsDotNet/TIdTCPClient.bmp new file mode 100644 index 0000000..cec68ed Binary files /dev/null and b/indy/Core/IconsDotNet/TIdTCPClient.bmp differ diff --git a/indy/Core/IconsDotNet/TIdTCPServer.bmp b/indy/Core/IconsDotNet/TIdTCPServer.bmp new file mode 100644 index 0000000..434819a Binary files /dev/null and b/indy/Core/IconsDotNet/TIdTCPServer.bmp differ diff --git a/indy/Core/IconsDotNet/TIdThreadComponent.bmp b/indy/Core/IconsDotNet/TIdThreadComponent.bmp new file mode 100644 index 0000000..b0d4092 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdThreadComponent.bmp differ diff --git a/indy/Core/IconsDotNet/TIdTraceRoute.bmp b/indy/Core/IconsDotNet/TIdTraceRoute.bmp new file mode 100644 index 0000000..354986c Binary files /dev/null and b/indy/Core/IconsDotNet/TIdTraceRoute.bmp differ diff --git a/indy/Core/IconsDotNet/TIdUDPClient.bmp b/indy/Core/IconsDotNet/TIdUDPClient.bmp new file mode 100644 index 0000000..3a6edf6 Binary files /dev/null and b/indy/Core/IconsDotNet/TIdUDPClient.bmp differ diff --git a/indy/Core/IconsDotNet/TIdUDPServer.bmp b/indy/Core/IconsDotNet/TIdUDPServer.bmp new file mode 100644 index 0000000..284423a Binary files /dev/null and b/indy/Core/IconsDotNet/TIdUDPServer.bmp differ diff --git a/indy/Core/IdAbout.TWinForm.resources b/indy/Core/IdAbout.TWinForm.resources new file mode 100644 index 0000000..2813a4d Binary files /dev/null and b/indy/Core/IdAbout.TWinForm.resources differ diff --git a/indy/Core/IdAbout.TfrmAbout.resources b/indy/Core/IdAbout.TfrmAbout.resources new file mode 100644 index 0000000..02644d0 Binary files /dev/null and b/indy/Core/IdAbout.TfrmAbout.resources differ diff --git a/indy/Core/IdAbout.pas b/indy/Core/IdAbout.pas new file mode 100644 index 0000000..d34fe0c --- /dev/null +++ b/indy/Core/IdAbout.pas @@ -0,0 +1,37 @@ +unit IdAbout; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF DOTNET} + IdAboutDotNET; + {$ELSE} + IdAboutVCL; + {$ENDIF} + +//we have a procedure for providing a product name and version in case +//we ever want to make another product. +procedure ShowAboutBox(const AProductName, AProductName2, AProductVersion : String); +procedure ShowDlg; + +implementation + +{$IFDEF DOTNET} + //for some reason, the Winforms designer doesn't like this in the same unit + //as the class it's for + {$R 'IdAboutDotNET.TfrmAbout.resources' 'IdAboutDotNET.resx'} +{$ENDIF} + +procedure ShowAboutBox(const AProductName, AProductName2, AProductVersion : String); +begin + TfrmAbout.ShowAboutBox(AProductName, AProductName2, AProductVersion); +end; + +procedure ShowDlg; +begin + TfrmAbout.ShowDlg; +end; + +end. diff --git a/indy/Core/IdAbout.resources b/indy/Core/IdAbout.resources new file mode 100644 index 0000000..28b44dc Binary files /dev/null and b/indy/Core/IdAbout.resources differ diff --git a/indy/Core/IdAbout.resx b/indy/Core/IdAbout.resx new file mode 100644 index 0000000..1c034b9 --- /dev/null +++ b/indy/Core/IdAbout.resx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + Private + + + False + + + False + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + diff --git a/indy/Core/IdAboutDotNET.TfrmAbout.resources b/indy/Core/IdAboutDotNET.TfrmAbout.resources new file mode 100644 index 0000000..28b44dc Binary files /dev/null and b/indy/Core/IdAboutDotNET.TfrmAbout.resources differ diff --git a/indy/Core/IdAboutDotNET.pas b/indy/Core/IdAboutDotNET.pas new file mode 100644 index 0000000..7fe5e50 --- /dev/null +++ b/indy/Core/IdAboutDotNET.pas @@ -0,0 +1,332 @@ +unit IdAboutDotNET; + +interface + +uses + System.Drawing, System.Collections, System.ComponentModel, + System.Windows.Forms, System.Data; + +type + TfrmAbout = class(System.Windows.Forms.Form) + {$REGION 'Designer Managed Code'} + strict private + /// + /// Required designer variable. + /// + Components: System.ComponentModel.Container; + imgLogo: System.Windows.Forms.PictureBox; + bbtnOk: System.Windows.Forms.Button; + lblName: System.Windows.Forms.Label; + lblName2: System.Windows.Forms.Label; + lblVersion: System.Windows.Forms.Label; + lblCopyright: System.Windows.Forms.Label; + lblBuiltFor: System.Windows.Forms.Label; + lblLicense: System.Windows.Forms.Label; + lblPleaseVisitUs: System.Windows.Forms.Label; + lblURL: System.Windows.Forms.LinkLabel; + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + procedure InitializeComponent; + procedure lblURL_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); + {$ENDREGION} + strict protected + /// + /// Clean up any resources being used. + /// + procedure Dispose(Disposing: Boolean); override; + protected + { Private Declarations } + function GetProductName: string; + procedure SetProductName(const AValue: string); + function GetProductName2: string; + procedure SetProductName2(const AValue: string); + function GetVersion: string; + procedure SetVersion(const AValue: string); + function LoadBitmap(AResName: string): Bitmap; + public + constructor Create; + //we have a method for providing a product name and version in case + //we ever want to make another product. + class Procedure ShowAboutBox(const AProductName, AProductName2, AProductVersion : String); + class Procedure ShowDlg; + property ProductName : String read GetProductName write SetProductName; + property ProductName2 : String read GetProductName2 write SetProductName2; + property Version : String read GetVersion write SetVersion; + + end; + + [assembly: RuntimeRequiredAttribute(TypeOf(TfrmAbout))] + +implementation + +uses + IdDsnCoreResourceStrings, System.Diagnostics, + IdGlobal, System.Reflection, System.Resources, SysUtils; + +const + ResourceBaseName = 'IdAboutNET'; +{$R 'AboutIndyNET.resources'} + +{$AUTOBOX ON} + +{$REGION 'Windows Form Designer generated code'} +/// +/// Required method for Designer support -- do not modify +/// the contents of this method with the code editor. +/// +procedure TfrmAbout.InitializeComponent; +begin + Self.imgLogo := System.Windows.Forms.PictureBox.Create; + Self.bbtnOk := System.Windows.Forms.Button.Create; + Self.lblName := System.Windows.Forms.Label.Create; + Self.lblName2 := System.Windows.Forms.Label.Create; + Self.lblVersion := System.Windows.Forms.Label.Create; + Self.lblCopyright := System.Windows.Forms.Label.Create; + Self.lblBuiltFor := System.Windows.Forms.Label.Create; + Self.lblLicense := System.Windows.Forms.Label.Create; + Self.lblPleaseVisitUs := System.Windows.Forms.Label.Create; + Self.lblURL := System.Windows.Forms.LinkLabel.Create; + Self.SuspendLayout; + // + // imgLogo + // + Self.imgLogo.Location := System.Drawing.Point.Create(0, 0); + Self.imgLogo.Name := 'imgLogo'; + Self.imgLogo.Size := System.Drawing.Size.Create(388, 240); + Self.imgLogo.TabIndex := 0; + Self.imgLogo.TabStop := False; + // + // bbtnOk + // + Self.bbtnOk.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.bbtnOk.DialogResult := System.Windows.Forms.DialogResult.Cancel; + Self.bbtnOk.Location := System.Drawing.Point.Create(475, 302); + Self.bbtnOk.Name := 'bbtnOk'; + Self.bbtnOk.TabIndex := 0; + Self.bbtnOk.Text := 'Button1'; + // + // lblName + // + Self.lblName.Font := System.Drawing.Font.Create('Arial Black', 14.25, System.Drawing.FontStyle.Regular, + System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblName.Location := System.Drawing.Point.Create(51, 28); + Self.lblName.Name := 'lblName'; + Self.lblName.Size := System.Drawing.Size.Create(200, 101); + Self.lblName.TabIndex := 1; + Self.lblName.Text := 'Label1'; + Self.lblName.TextAlign := System.Drawing.ContentAlignment.TopCenter; + // + // lblName2 + // + Self.lblName.Font := System.Drawing.Font.Create('Arial', 14.25, System.Drawing.FontStyle.Regular, + System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblName.Location := System.Drawing.Point.Create(54, 110); + Self.lblName.Name := 'lblName'; + Self.lblName.Size := System.Drawing.Size.Create(192, 35); + Self.lblName.TabIndex := 2; + Self.lblName.Text := 'Label2'; + Self.lblName.TextAlign := System.Drawing.ContentAlignment.TopCenter; + // + // lblVersion + // + Self.lblVersion.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblVersion.Location := System.Drawing.Point.Create(300, 170); + Self.lblVersion.Name := 'lblVersion'; + Self.lblVersion.Size := System.Drawing.Size.Create(200, 17); + Self.lblVersion.TabIndex := 3; + Self.lblVersion.Text := 'Label3'; + Self.lblVersion.TextAlign := System.Drawing.ContentAlignment.TopRight; + // + // lblCopyright + // + Self.lblCopyright.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblCopyright.Location := System.Drawing.Point.Create(58, 171); + Self.lblCopyright.Name := 'lblCopyright'; + Self.lblCopyright.Size := System.Drawing.Size.Create(138, 15); + Self.lblCopyright.TabIndex := 6; + Self.lblCopyright.Text := 'Label6'; + Self.lblCopyright.TextAlign := System.Drawing.ContentAlignment.TopCenter; + // + // lblBuiltFor + // + Self.lblBuiltFor.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Right))); + Self.lblBuiltFor.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblBuiltFor.Location := System.Drawing.Point.Create(300, 188); + Self.lblBuiltFor.Name := 'lblBuiltFor'; + Self.lblBuiltFor.Size := System.Drawing.Size.Create(200, 17); + Self.lblBuiltFor.TabIndex := 4; + Self.lblBuiltFor.Text := 'Label4'; + Self.lblBuiltFor.TextAlign := System.Drawing.ContentAlignment.TopRight; + // + // lblLicense + // + Self.lblLicense.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Right))); + Self.lblLicense.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblLicense.Location := System.Drawing.Point.Create(300, 227); + Self.lblLicense.Name := 'lblLicense'; + Self.lblLicense.Size := System.Drawing.Size.Create(200, 45); + Self.lblLicense.TabIndex := 5; + Self.lblLicense.Text := 'Label5'; + Self.lblBuiltFor.TextAlign := System.Drawing.ContentAlignment.TopRight; + // + // lblPleaseVisitUs + // + Self.lblPleaseVisitUs.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblPleaseVisitUs.Location := System.Drawing.Point.Create(58, 278); + Self.lblPleaseVisitUs.Name := 'lblPleaseVisitUs'; + Self.lblPleaseVisitUs.Size := System.Drawing.Size.Create(276, 15); + Self.lblPleaseVisitUs.TabIndex := 7; + Self.lblPleaseVisitUs.Text := 'Label7'; + Self.lblPleaseVisitUs.TextAlign := System.Drawing.ContentAlignment.TopCenter; + // + // lblURL + // + Self.lblCopyright.Font := System.Drawing.Font.Create('Arial', 14.25, + System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (Byte(0))); + Self.lblURL.Location := System.Drawing.Point.Create(58, 292); + Self.lblURL.Name := 'lblURL'; + Self.lblURL.Size := System.Drawing.Size.Create(141, 15); + Self.lblURL.TabIndex := 8; + Self.lblURL.TabStop := True; + Self.lblURL.Text := 'LinkLabel8'; + Self.lblURL.TextAlign := System.Drawing.ContentAlignment.TopCenter; + Include(Self.lblURL.LinkClicked, Self.lblURL_LinkClicked); + // + // TfrmAbout + // + Self.AcceptButton := Self.bbtnOk; + Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); + Self.CancelButton := Self.bbtnOk; + Self.ClientSize := System.Drawing.Size.Create(336, 554); + Self.Controls.Add(Self.lblURL); + Self.Controls.Add(Self.lblPleaseVisitUs); + Self.Controls.Add(Self.lblCopyright); + Self.Controls.Add(Self.lblVersion); + Self.Controls.Add(Self.lblName); + Self.Controls.Add(Self.lblName2); + Self.Controls.Add(Self.lblBuiltFor); + Self.Controls.Add(Self.lblLicense); + Self.Controls.Add(Self.bbtnOk); + Self.Controls.Add(Self.imgLogo); + Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; + Self.MaximizeBox := False; + Self.MinimizeBox := False; + Self.Name := 'TfrmAbout'; + Self.ShowInTaskbar := False; + Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + Self.Text := 'WinForm'; + Self.ResumeLayout(False); +end; +{$ENDREGION} + +procedure TfrmAbout.Dispose(Disposing: Boolean); +begin + if Disposing then + begin + if Components <> nil then + Components.Dispose(); + end; + inherited Dispose(Disposing); +end; + +constructor TfrmAbout.Create; +begin + inherited Create; + // + // Required for Windows Form Designer support + // + InitializeComponent; + // + // TODO: Add any constructor code after InitializeComponent call + // + Self.Text := RSAAboutFormCaption; + lblName.Text := RSAAboutBoxTitle1; + lblName2.Text := RSAAboutBoxTitle2; + lblBuiltFor.Text := IndyFormat(RSAAboutBoxBuiltFor, ['DotNET']); + lblLicense.Text := RSAAboutBoxLicences; + lblCopyright.Text := RSAAboutBoxCopyright; + lblPleaseVisitUs.Text := RSAAboutBoxPleaseVisit; + lblURL.Text := RSAAboutBoxIndyWebsite; + lblURL.Links.Add(0, Length(RSAABoutBoxIndyWebsite), RSAAboutBoxIndyWebsite); + bbtnOk.Text := RSOk; + imgLogo.Image := LoadBitmap('AboutBackground.bmp'); +end; + +procedure TfrmAbout.SetProductName(const AValue : String); +begin + Self.lblName.Text := AValue; +end; + +procedure TfrmAbout.SetProductName2(const AValue : String); +begin + Self.lblName2.Text := AValue; +end; + +procedure TfrmAbout.SetVersion(const AValue: string); +begin + Self.lblVersion.Text := AValue; +end; + +function TfrmAbout.GetVersion: string; +begin + Result := Self.lblVersion.Text; +end; + +function TfrmAbout.GetProductName: string; +begin + Result := Self.lblName.Text; +end; + +function TfrmAbout.GetProductName2: string; +begin + Result := Self.lblName2.Text; +end; + +class procedure TfrmAbout.ShowAboutBox(const AProductName, AProductName2, + AProductVersion: String); +begin + with TfrmAbout.Create do + try + Version := IndyFormat(RSAAboutBoxVersion, [AProductVersion]); + ProductName := AProductName; + ProductName2 := AProductName2; + Text := AProductName; + ShowDialog; + finally + Dispose; + end; +end; + +class procedure TfrmAbout.ShowDlg; +begin + ShowAboutBox(RSAAboutBoxTitle1, RSAAboutBoxTitle2, gsIdVersion); +end; + +procedure TfrmAbout.lblURL_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); +var + LDest : String; +begin + LDest := e.Link.LinkData as string; + System.Diagnostics.Process.Start(LDest); + e.Link.Visited := True; +end; + +function TfrmAbout.LoadBitmap(AResName: string): Bitmap; +var + LR: System.Resources.ResourceManager; +begin + LR := System.Resources.ResourceManager.Create('AboutIndyNET', System.Reflection.Assembly.GetExecutingAssembly); + Result := (Bitmap(LR.GetObject(AResName))); +end; + +end. diff --git a/indy/Core/IdAboutDotNET.resources b/indy/Core/IdAboutDotNET.resources new file mode 100644 index 0000000..28b44dc Binary files /dev/null and b/indy/Core/IdAboutDotNET.resources differ diff --git a/indy/Core/IdAboutDotNET.resx b/indy/Core/IdAboutDotNET.resx new file mode 100644 index 0000000..fdbcece --- /dev/null +++ b/indy/Core/IdAboutDotNET.resx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + False + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + diff --git a/indy/Core/IdAboutNET.resources b/indy/Core/IdAboutNET.resources new file mode 100644 index 0000000..f3f8cdc Binary files /dev/null and b/indy/Core/IdAboutNET.resources differ diff --git a/indy/Core/IdAboutVCL.RES b/indy/Core/IdAboutVCL.RES new file mode 100644 index 0000000..078bed5 Binary files /dev/null and b/indy/Core/IdAboutVCL.RES differ diff --git a/indy/Core/IdAboutVCL.lrs b/indy/Core/IdAboutVCL.lrs new file mode 100644 index 0000000..1a74469 --- /dev/null +++ b/indy/Core/IdAboutVCL.lrs @@ -0,0 +1,5438 @@ +LazarusResources.Add('IndyAboutBkgnd','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"554 336 1130 2",'#13#10'"' + +' '#9'c #256993",'#13#10'". '#9'c #256A93",'#13#10'"+ '#9'c #256A94",'#13#10 + +'"@ '#9'c #266B95",'#13#10'"# '#9'c #266B96",'#13#10'"$ '#9'c #266C96",'#13 + +#10'"% '#9'c #266C97",'#13#10'"& '#9'c #266D97",'#13#10'"* '#9'c #276D97",' + +#13#10'"= '#9'c #276D98",'#13#10'"- '#9'c #276E98",'#13#10'"; '#9'c #276E99"' + +','#13#10'"> '#9'c #276F99",'#13#10'", '#9'c #276F9A",'#13#10'"'' '#9'c #286' + +'F9A",'#13#10'") '#9'c #286F9B",'#13#10'"! '#9'c #28709B",'#13#10'"~ '#9'c #' + +'256992",'#13#10'"{ '#9'c #256892",'#13#10'"] '#9'c #246892",'#13#10'"^ '#9 + +'c #246891",'#13#10'"/ '#9'c #246791",'#13#10'"( '#9'c #246790",'#13#10'"_ ' + +#9'c #246690",'#13#10'": '#9'c #24668F",'#13#10'"< '#9'c #23668F",'#13#10'"[' + +' '#9'c #23658E",'#13#10'"} '#9'c #23658D",'#13#10'"| '#9'c #23648D",'#13#10 + +'"1 '#9'c #23648C",'#13#10'"2 '#9'c #22638C",'#13#10'"3 '#9'c #22638B",'#13 + +#10'"4 '#9'c #22628B",'#13#10'"5 '#9'c #22628A",'#13#10'"6 '#9'c #226189",' + +#13#10'"7 '#9'c #216189",'#13#10'"8 '#9'c #216188",'#13#10'"9 '#9'c #216088"' + +','#13#10'"0 '#9'c #216087",'#13#10'"a '#9'c #215F87",'#13#10'"b '#9'c #215F' + +'86",'#13#10'"c '#9'c #205F86",'#13#10'"d '#9'c #205E86",'#13#10'"e '#9'c #2' + +'05E85",'#13#10'"f '#9'c #205D85",'#13#10'"g '#9'c #205D84",'#13#10'"h '#9'c' + +' #205D83",'#13#10'"i '#9'c #205C83",'#13#10'"j '#9'c #1F5C83",'#13#10'"k '#9 + +'c #1F5C82",'#13#10'"l '#9'c #1F5B82",'#13#10'"m '#9'c #1F5B81",'#13#10'"n ' + +#9'c #1F5A81",'#13#10'"o '#9'c #1F5A80",'#13#10'"p '#9'c #1E5A80",'#13#10'"q' + +' '#9'c #1E597F",'#13#10'"r '#9'c #1E597E",'#13#10'"s '#9'c #1E587E",'#13#10 + +'"t '#9'c #1E587D",'#13#10'"u '#9'c #1D577D",'#13#10'"v '#9'c #1D577C",'#13 + +#10'"w '#9'c #1D567C",'#13#10'"x '#9'c #1D567B",'#13#10'"y '#9'c #1D557A",' + +#13#10'"z '#9'c #1C557A",'#13#10'"A '#9'c #1C5579",'#13#10'"B '#9'c #1C5479"' + +','#13#10'"C '#9'c #1C5478",'#13#10'"D '#9'c #1C5378",'#13#10'"E '#9'c #1C53' + +'77",'#13#10'"F '#9'c #1B5277",'#13#10'"G '#9'c #1B5276",'#13#10'"H '#9'c #1' + +'B5175",'#13#10'"I '#9'c #1B5174",'#13#10'"J '#9'c #1B5074",'#13#10'"K '#9'c' + +' #1A5073",'#13#10'"L '#9'c #1A4F73",'#13#10'"M '#9'c #1A4F72",'#13#10'"N '#9 + +'c #1A4E71",'#13#10'"O '#9'c #1A4E70",'#13#10'"P '#9'c #1A4D70",'#13#10'"Q ' + +#9'c #194D70",'#13#10'"R '#9'c #194D6F",'#13#10'"S '#9'c #194C6F",'#13#10'"T' + +' '#9'c #194C6E",'#13#10'"U '#9'c #194B6E",'#13#10'"V '#9'c #194B6D",'#13#10 + +'"W '#9'c #184B6D",'#13#10'"X '#9'c #184A6C",'#13#10'"Y '#9'c #184A6B",'#13 + +#10'"Z '#9'c #18496B",'#13#10'"` '#9'c #18496A",'#13#10'" .'#9'c #18486A",' + +#13#10'"..'#9'c #184869",'#13#10'"+.'#9'c #174869",'#13#10'"@.'#9'c #174769"' + +','#13#10'"#.'#9'c #174768",'#13#10'"$.'#9'c #174667",'#13#10'"%.'#9'c #1746' + +'66",'#13#10'"&.'#9'c #164566",'#13#10'"*.'#9'c #164565",'#13#10'"=.'#9'c #1' + +'64465",'#13#10'"-.'#9'c #164464",'#13#10'";.'#9'c #164363",'#13#10'">.'#9'c' + +' #154363",'#13#10'",.'#9'c #154362",'#13#10'"''.'#9'c #154262",'#13#10'").' + +#9'c #154261",'#13#10'"!.'#9'c #154161",'#13#10'"~.'#9'c #154160",'#13#10'"{' + +'.'#9'c #154060",'#13#10'"].'#9'c #15405F",'#13#10'"^.'#9'c #14405F",'#13#10 + +'"/.'#9'c #143F5E",'#13#10'"(.'#9'c #143F5D",'#13#10'"_.'#9'c #143E5D",'#13 + +#10'":.'#9'c #143E5C",'#13#10'"<.'#9'c #133D5C",'#13#10'"[.'#9'c #133D5B",' + +#13#10'"}.'#9'c #133C5A",'#13#10'"|.'#9'c #133C59",'#13#10'"1.'#9'c #133B59"' + +','#13#10'"2.'#9'c #123B58",'#13#10'"3.'#9'c #123A58",'#13#10'"4.'#9'c #123A' + +'57",'#13#10'"5.'#9'c #123956",'#13#10'"6.'#9'c #123955",'#13#10'"7.'#9'c #1' + +'23855",'#13#10'"8.'#9'c #113855",'#13#10'"9.'#9'c #113854",'#13#10'"0.'#9'c' + +' #23668E",'#13#10'"a.'#9'c #1D557B",'#13#10'"b.'#9'c #1C5277",'#13#10'"c.'#9 + +'c #1B5073",'#13#10'"d.'#9'c #174767",'#13#10'"e.'#9'c #14405E",'#13#10'"f.' + +#9'c #133B58",'#13#10'"g.'#9'c #266A95",'#13#10'"h.'#9'c #1E5980",'#13#10'"i' + +'.'#9'c #1B5275",'#13#10'"j.'#9'c #1A4E72",'#13#10'"k.'#9'c #174566",'#13#10 + +'"l.'#9'c #133E5C",'#13#10'"m.'#9'c #123957",'#13#10'"n.'#9'c #256A95",'#13 + +#10'"o.'#9'c #28709C",'#13#10'"p.'#9'c #28719C",'#13#10'"q.'#9'c #28719D",' + +#13#10'"r.'#9'c #184B6C",'#13#10'"s.'#9'c #29719D",'#13#10'"t.'#9'c #29729D"' + +','#13#10'"u.'#9'c #29729E",'#13#10'"v.'#9'c #29739E",'#13#10'"w.'#9'c #2973' + +'9F",'#13#10'"x.'#9'c #2973A0",'#13#10'"y.'#9'c #2974A0",'#13#10'"z.'#9'c #2' + +'A74A0",'#13#10'"A.'#9'c #2A74A1",'#13#10'"B.'#9'c #2A75A1",'#13#10'"C.'#9'c' + +' #2A75A2",'#13#10'"D.'#9'c #2A76A2",'#13#10'"E.'#9'c #1B4D6E",'#13#10'"F.'#9 + +'c #1A4D6F",'#13#10'"G.'#9'c #1C4E6F",'#13#10'"H.'#9'c #1C4E70",'#13#10'"I.' + +#9'c #1D4F70",'#13#10'"J.'#9'c #1D4E70",'#13#10'"K.'#9'c #1F5071",'#13#10'"L' + +'.'#9'c #1F5070",'#13#10'"M.'#9'c #1F4F70",'#13#10'"N.'#9'c #205070",'#13#10 + +'"O.'#9'c #205171",'#13#10'"P.'#9'c #205071",'#13#10'"Q.'#9'c #1F4F6F",'#13 + +#10'"R.'#9'c #1E4E6E",'#13#10'"S.'#9'c #1E4D6D",'#13#10'"T.'#9'c #1E4D6C",' + +#13#10'"U.'#9'c #1D4C6B",'#13#10'"V.'#9'c #1C4B6B",'#13#10'"W.'#9'c #1A4969"' + +','#13#10'"X.'#9'c #1A4968",'#13#10'"Y.'#9'c #184767",'#13#10'"Z.'#9'c #1846' + ,'67",'#13#10'"`.'#9'c #1B4E70",'#13#10'" +'#9'c #1D5071",'#13#10'".+'#9'c #1' + +'D5072",'#13#10'"++'#9'c #1E5172",'#13#10'"@+'#9'c #1F5173",'#13#10'"#+'#9'c' + +' #205273",'#13#10'"$+'#9'c #225374",'#13#10'"%+'#9'c #225474",'#13#10'"&+'#9 + +'c #215474",'#13#10'"*+'#9'c #215373",'#13#10'"=+'#9'c #215273",'#13#10'"-+' + +#9'c #215272",'#13#10'";+'#9'c #215172",'#13#10'">+'#9'c #215171",'#13#10'",' + +'+'#9'c #204F6F",'#13#10'"''+'#9'c #204F6E",'#13#10'")+'#9'c #204E6E",'#13#10 + +'"!+'#9'c #1F4E6E",'#13#10'"~+'#9'c #1F4E6D",'#13#10'"{+'#9'c #1F4D6D",'#13 + +#10'"]+'#9'c #1F4D6C",'#13#10'"^+'#9'c #1D4C6A",'#13#10'"/+'#9'c #1D4B6A",' + +#13#10'"(+'#9'c #1C4968",'#13#10'"_+'#9'c #1B4867",'#13#10'":+'#9'c #1A4766"' + +','#13#10'"<+'#9'c #184565",'#13#10'"[+'#9'c #174565",'#13#10'"}+'#9'c #1B4F' + +'71",'#13#10'"|+'#9'c #1D5173",'#13#10'"1+'#9'c #1E5274",'#13#10'"2+'#9'c #2' + +'05475",'#13#10'"3+'#9'c #225676",'#13#10'"4+'#9'c #225576",'#13#10'"5+'#9'c' + +' #225575",'#13#10'"6+'#9'c #225475",'#13#10'"7+'#9'c #1F4C6B",'#13#10'"8+'#9 + +'c #1E4C6B",'#13#10'"9+'#9'c #1E4C6A",'#13#10'"0+'#9'c #1E4B6A",'#13#10'"a+' + +#9'c #1C4A68",'#13#10'"b+'#9'c #194766",'#13#10'"c+'#9'c #184564",'#13#10'"d' + +'+'#9'c #174462",'#13#10'"e+'#9'c #1B4F73",'#13#10'"f+'#9'c #205476",'#13#10 + +'"g+'#9'c #215577",'#13#10'"h+'#9'c #235778",'#13#10'"i+'#9'c #235777",'#13 + +#10'"j+'#9'c #235677",'#13#10'"k+'#9'c #225677",'#13#10'"l+'#9'c #20506F",' + +#13#10'"m+'#9'c #1E4B69",'#13#10'"n+'#9'c #1D4968",'#13#10'"o+'#9'c #1B4767"' + +','#13#10'"p+'#9'c #1A4664",'#13#10'"q+'#9'c #184563",'#13#10'"r+'#9'c #1743' + +'62",'#13#10'"s+'#9'c #2A76A3",'#13#10'"t+'#9'c #2B76A3",'#13#10'"u+'#9'c #1' + +'D5275",'#13#10'"v+'#9'c #1F5476",'#13#10'"w+'#9'c #215678",'#13#10'"x+'#9'c' + +' #235779",'#13#10'"y+'#9'c #215473",'#13#10'"z+'#9'c #205271",'#13#10'"A+'#9 + +'c #1F5171",'#13#10'"B+'#9'c #1D4E6F",'#13#10'"C+'#9'c #1D4D6F",'#13#10'"D+' + +#9'c #1C4D6E",'#13#10'"E+'#9'c #1B4C6D",'#13#10'"F+'#9'c #1B4C6C",'#13#10'"G' + +'+'#9'c #1C4D6D",'#13#10'"H+'#9'c #1C4D6C",'#13#10'"I+'#9'c #1C4C6C",'#13#10 + +'"J+'#9'c #1E4A69",'#13#10'"K+'#9'c #1E4A68",'#13#10'"L+'#9'c #1C4765",'#13 + +#10'"M+'#9'c #194664",'#13#10'"N+'#9'c #1C5175",'#13#10'"O+'#9'c #1F5477",' + +#13#10'"P+'#9'c #205678",'#13#10'"Q+'#9'c #22577A",'#13#10'"R+'#9'c #23587A"' + +','#13#10'"S+'#9'c #235879",'#13#10'"T+'#9'c #205274",'#13#10'"U+'#9'c #1E50' + +'71",'#13#10'"V+'#9'c #1A4D6E",'#13#10'"W+'#9'c #194C6D",'#13#10'"X+'#9'c #1' + +'A4868",'#13#10'"Y+'#9'c #1A4867",'#13#10'"Z+'#9'c #1B4968",'#13#10'"`+'#9'c' + +' #1C4A69",'#13#10'" @'#9'c #1E4A67",'#13#10'".@'#9'c #1D4A67",'#13#10'"+@'#9 + +'c #1D4967",'#13#10'"@@'#9'c #1B4764",'#13#10'"#@'#9'c #194462",'#13#10'"$@' + +#9'c #174261",'#13#10'"%@'#9'c #1C5275",'#13#10'"&@'#9'c #23577A",'#13#10'"*' + +'@'#9'c #24597B",'#13#10'"=@'#9'c #23597A",'#13#10'"-@'#9'c #215476",'#13#10 + +'";@'#9'c #1E5273",'#13#10'">@'#9'c #1C4F71",'#13#10'",@'#9'c #174464",'#13 + +#10'"''@'#9'c #194666",'#13#10'")@'#9'c #194765",'#13#10'"!@'#9'c #1B4967",' + +#13#10'"~@'#9'c #1D4A69",'#13#10'"{@'#9'c #1D4A66",'#13#10'"]@'#9'c #1D4966"' + +','#13#10'"^@'#9'c #1A4562",'#13#10'"/@'#9'c #174260",'#13#10'"(@'#9'c #153F' + +'5E",'#13#10'"_@'#9'c #1C5276",'#13#10'":@'#9'c #1E5477",'#13#10'"<@'#9'c #2' + +'15679",'#13#10'"[@'#9'c #23597B",'#13#10'"}@'#9'c #245A7B",'#13#10'"|@'#9'c' + +' #24597A",'#13#10'"1@'#9'c #1E5275",'#13#10'"2@'#9'c #1D5172",'#13#10'"3@'#9 + +'c #174463",'#13#10'"4@'#9'c #184664",'#13#10'"5@'#9'c #1A4765",'#13#10'"6@' + +#9'c #1D4965",'#13#10'"7@'#9'c #1D4865",'#13#10'"8@'#9'c #1C4664",'#13#10'"9' + +'@'#9'c #1A4462",'#13#10'"0@'#9'c #17415F",'#13#10'"a@'#9'c #153F5D",'#13#10 + +'"b@'#9'c #1E5578",'#13#10'"c@'#9'c #205779",'#13#10'"d@'#9'c #22587B",'#13 + +#10'"e@'#9'c #245A7C",'#13#10'"f@'#9'c #215677",'#13#10'"g@'#9'c #1C5072",' + +#13#10'"h@'#9'c #174361",'#13#10'"i@'#9'c #184463",'#13#10'"j@'#9'c #1A4665"' + +','#13#10'"k@'#9'c #1C4766",'#13#10'"l@'#9'c #1E4967",'#13#10'"m@'#9'c #1D48' + +'64",'#13#10'"n@'#9'c #1C4663",'#13#10'"o@'#9'c #184261",'#13#10'"p@'#9'c #1' + +'6405D",'#13#10'"q@'#9'c #1F5679",'#13#10'"r@'#9'c #245B7D",'#13#10'"s@'#9'c' + +' #215779",'#13#10'"t@'#9'c #205477",'#13#10'"u@'#9'c #1E5376",'#13#10'"v@'#9 + +'c #1C5173",'#13#10'"w@'#9'c #164261",'#13#10'"x@'#9'c #184462",'#13#10'"y@' + +#9'c #1C4865",'#13#10'"z@'#9'c #1C4764",'#13#10'"A@'#9'c #1C4763",'#13#10'"B' + +'@'#9'c #1A4461",'#13#10'"C@'#9'c #20577B",'#13#10'"D@'#9'c #23597D",'#13#10 + +'"E@'#9'c #23587B",'#13#10'"F@'#9'c #164160",'#13#10'"G@'#9'c #1B4664",'#13 + +#10'"H@'#9'c #1B4461",'#13#10'"I@'#9'c #18415E",'#13#10'"J@'#9'c #153E5B",' + +#13#10'"K@'#9'c #2B77A3",'#13#10'"L@'#9'c #1E557A",'#13#10'"M@'#9'c #21587B"' + +','#13#10'"N@'#9'c #255C7E",'#13#10'"O@'#9'c #255B7E",'#13#10'"P@'#9'c #2459' + +'7C",'#13#10'"Q@'#9'c #21577A",'#13#10'"R@'#9'c #1F5577",'#13#10'"S@'#9'c #1' + +'D5375",'#13#10'"T@'#9'c #16415F",'#13#10'"U@'#9'c #194361",'#13#10'"V@'#9'c' + +' #1B4563",'#13#10'"W@'#9'c #1C4662",'#13#10'"X@'#9'c #1B4561",'#13#10'"Y@'#9 + +'c #18415F",'#13#10'"Z@'#9'c #1E567A",'#13#10'"`@'#9'c #22587C",'#13#10'" #' + ,#9'c #245B7E",'#13#10'".#'#9'c #255C7F",'#13#10'"+#'#9'c #22587A",'#13#10'"@' + +'#'#9'c #205579",'#13#10'"##'#9'c #1D5377",'#13#10'"$#'#9'c #16415E",'#13#10 + +'"%#'#9'c #194360",'#13#10'"&#'#9'c #1B4663",'#13#10'"*#'#9'c #1C4661",'#13 + +#10'"=#'#9'c #1B4460",'#13#10'"-#'#9'c #18405D",'#13#10'";#'#9'c #143C5A",' + +#13#10'">#'#9'c #2B77A4",'#13#10'",#'#9'c #21587C",'#13#10'"''#'#9'c #245C7E' + +'",'#13#10'")#'#9'c #255D7F",'#13#10'"!#'#9'c #235A7C",'#13#10'"~#'#9'c #205' + +'77A",'#13#10'"{#'#9'c #194461",'#13#10'"]#'#9'c #1C4561",'#13#10'"^#'#9'c #' + +'1A4360",'#13#10'"/#'#9'c #163F5C",'#13#10'"(#'#9'c #20587C",'#13#10'"_#'#9 + +'c #235B7E",'#13#10'":#'#9'c #255D80",'#13#10'"<#'#9'c #24597D",'#13#10'"[#' + +#9'c #20567A",'#13#10'"}#'#9'c #1D5478",'#13#10'"|#'#9'c #1B4562",'#13#10'"1' + +'#'#9'c #19425E",'#13#10'"2#'#9'c #163E5B",'#13#10'"3#'#9'c #1F587C",'#13#10 + +'"4#'#9'c #225B7E",'#13#10'"5#'#9'c #255C80",'#13#10'"6#'#9'c #235A7D",'#13 + +#10'"7#'#9'c #153E5C",'#13#10'"8#'#9'c #1B4360",'#13#10'"9#'#9'c #18405C",' + +#13#10'"0#'#9'c #143C59",'#13#10'"a#'#9'c #1E567B",'#13#10'"b#'#9'c #225A7E"' + +','#13#10'"c#'#9'c #245D80",'#13#10'"d#'#9'c #255D81",'#13#10'"e#'#9'c #1841' + +'5D",'#13#10'"f#'#9'c #1A425E",'#13#10'"g#'#9'c #235B7F",'#13#10'"h#'#9'c #2' + +'65D81",'#13#10'"i#'#9'c #20587B",'#13#10'"j#'#9'c #1D5479",'#13#10'"k#'#9'c' + +' #153D5B",'#13#10'"l#'#9'c #19415E",'#13#10'"m#'#9'c #1C4461",'#13#10'"n#'#9 + +'c #1B435F",'#13#10'"o#'#9'c #183F5B",'#13#10'"p#'#9'c #143B58",'#13#10'"q#' + +#9'c #1E577B",'#13#10'"r#'#9'c #215A7E",'#13#10'"s#'#9'c #245C7F",'#13#10'"t' + +'#'#9'c #163E5A",'#13#10'"u#'#9'c #19415D",'#13#10'"v#'#9'c #163D59",'#13#10 + +'"w#'#9'c #265E81",'#13#10'"x#'#9'c #265E82",'#13#10'"y#'#9'c #225A7D",'#13 + +#10'"z#'#9'c #19425F",'#13#10'"A#'#9'c #20597E",'#13#10'"B#'#9'c #235C80",' + +#13#10'"C#'#9'c #20597D",'#13#10'"D#'#9'c #1D5679",'#13#10'"E#'#9'c #205A7E"' + +','#13#10'"F#'#9'c #245D82",'#13#10'"G#'#9'c #265E83",'#13#10'"H#'#9'c #1F57' + +'7C",'#13#10'"I#'#9'c #215B7F",'#13#10'"J#'#9'c #255E82",'#13#10'"K#'#9'c #2' + +'65F83",'#13#10'"L#'#9'c #255E81",'#13#10'"M#'#9'c #1B435E",'#13#10'"N#'#9'c' + +' #215B80",'#13#10'"O#'#9'c #255F82",'#13#10'"P#'#9'c #133A57",'#13#10'"Q#'#9 + +'c #173E5A",'#13#10'"R#'#9'c #2B78A4",'#13#10'"S#'#9'c #2B78A5",'#13#10'"T#' + +#9'c #1F597D",'#13#10'"U#'#9'c #225B80",'#13#10'"V#'#9'c #255F83",'#13#10'"W' + +'#'#9'c #265F84",'#13#10'"X#'#9'c #225C80",'#13#10'"Y#'#9'c #266083",'#13#10 + +'"Z#'#9'c #235B80",'#13#10'"`#'#9'c #1E577C",'#13#10'" $'#9'c #225B81",'#13 + +#10'".$'#9'c #266084",'#13#10'"+$'#9'c #276084",'#13#10'"@$'#9'c #265F82",' + +#13#10'"#$'#9'c #1E577D",'#13#10'"$$'#9'c #276085",'#13#10'"%$'#9'c #225B7F"' + +','#13#10'"&$'#9'c #1E587C",'#13#10'"*$'#9'c #215C81",'#13#10'"=$'#9'c #2560' + +'83",'#13#10'"-$'#9'c #205B80",'#13#10'";$'#9'c #255F84",'#13#10'">$'#9'c #2' + +'76186",'#13#10'",$'#9'c #276185",'#13#10'"''$'#9'c #235E83",'#13#10'")$'#9 + +'c #235D81",'#13#10'"!$'#9'c #225D82",'#13#10'"~$'#9'c #266185",'#13#10'"{$' + +#9'c #235D82",'#13#10'"]$'#9'c #1F597F",'#13#10'"^$'#9'c #205C82",'#13#10'"/' + +'$'#9'c #256185",'#13#10'"($'#9'c #245E82",'#13#10'"_$'#9'c #205B81",'#13#10 + +'":$'#9'c #246084",'#13#10'"<$'#9'c #276286",'#13#10'"[$'#9'c #256084",'#13 + +#10'"}$'#9'c #205B7F",'#13#10'"|$'#9'c #266085",'#13#10'"1$'#9'c #215D83",' + +#13#10'"2$'#9'c #266186",'#13#10'"3$'#9'c #286286",'#13#10'"4$'#9'c #246085"' + +','#13#10'"5$'#9'c #286387",'#13#10'"6$'#9'c #286287",'#13#10'"7$'#9'c #245F' + +'84",'#13#10'"8$'#9'c #276287",'#13#10'"9$'#9'c #215D82",'#13#10'"0$'#9'c #2' + +'05B82",'#13#10'"a$'#9'c #256086",'#13#10'"b$'#9'c #225E83",'#13#10'"c$'#9'c' + +' #225E84",'#13#10'"d$'#9'c #276288",'#13#10'"e$'#9'c #286388",'#13#10'"f$'#9 + +'c #256286",'#13#10'"g$'#9'c #235E84",'#13#10'"h$'#9'c #1A4C6E",'#13#10'"i$' + +#9'c #1A4C6D",'#13#10'"j$'#9'c #1A4B6D",'#13#10'"k$'#9'c #215E85",'#13#10'"l' + +'$'#9'c #276388",'#13#10'"m$'#9'c #286488",'#13#10'"n$'#9'c #256186",'#13#10 + +'"o$'#9'c #1B4E6F",'#13#10'"p$'#9'c #1E4F72",'#13#10'"q$'#9'c #215374",'#13 + +#10'"r$'#9'c #1E4E6F",'#13#10'"s$'#9'c #1B4B6C",'#13#10'"t$'#9'c #194969",' + +#13#10'"u$'#9'c #1E4C6C",'#13#10'"v$'#9'c #1B4A6A",'#13#10'"w$'#9'c #194867"' + +','#13#10'"x$'#9'c #246087",'#13#10'"y$'#9'c #276387",'#13#10'"z$'#9'c #1B4F' + +'72",'#13#10'"A$'#9'c #1E5173",'#13#10'"B$'#9'c #1F5374",'#13#10'"C$'#9'c #2' + +'05375",'#13#10'"D$'#9'c #225476",'#13#10'"E$'#9'c #215170",'#13#10'"F$'#9'c' + +' #1D4E6E",'#13#10'"G$'#9'c #1A496A",'#13#10'"H$'#9'c #184666",'#13#10'"I$'#9 + +'c #266388",'#13#10'"J$'#9'c #286489",'#13#10'"K$'#9'c #1F4C6A",'#13#10'"L$' + +#9'c #245F86",'#13#10'"M$'#9'c #276489",'#13#10'"N$'#9'c #215D84",'#13#10'"O' + +'$'#9'c #266387",'#13#10'"P$'#9'c #296489",'#13#10'"Q$'#9'c #266287",'#13#10 + +'"R$'#9'c #1F5274",'#13#10'"S$'#9'c #1A4767",'#13#10'"T$'#9'c #225F85",'#13 + +#10'"U$'#9'c #28648A",'#13#10'"V$'#9'c #296589",'#13#10'"W$'#9'c #236086",' + +#13#10'"X$'#9'c #28658A",'#13#10'"Y$'#9'c #29658A",'#13#10'"Z$'#9'c #276389"' + +','#13#10'"`$'#9'c #215E84",'#13#10'" %'#9'c #1C4F72",'#13#10'".%'#9'c #2562' + ,'88",'#13#10'"+%'#9'c #256287",'#13#10'"@%'#9'c #205576",'#13#10'"#%'#9'c #1' + +'F5172",'#13#10'"$%'#9'c #1A4A6B",'#13#10'"%%'#9'c #194767",'#13#10'"&%'#9'c' + +' #1C4866",'#13#10'"*%'#9'c #184362",'#13#10'"=%'#9'c #215576",'#13#10'"-%'#9 + +'c #1B4D6F",'#13#10'";%'#9'c #194B6C",'#13#10'">%'#9'c #1B4C6E",'#13#10'",%' + +#9'c #1E4F70",'#13#10'"''%'#9'c #1D4D6B",'#13#10'")%'#9'c #164463",'#13#10'"' + +'!%'#9'c #15405E",'#13#10'"~%'#9'c #226087",'#13#10'"{%'#9'c #276488",'#13#10 + +'"]%'#9'c #225D84",'#13#10'"^%'#9'c #1B5072",'#13#10'"/%'#9'c #1E4F71",'#13 + +#10'"(%'#9'c #1A4B6C",'#13#10'"_%'#9'c #194968",'#13#10'":%'#9'c #184766",' + +#13#10'"<%'#9'c #1F4E6C",'#13#10'"[%'#9'c #246288",'#13#10'"}%'#9'c #29668B"' + +','#13#10'"|%'#9'c #1D5274",'#13#10'"1%'#9'c #1E5174",'#13#10'"2%'#9'c #194A' + +'6A",'#13#10'"3%'#9'c #215271",'#13#10'"4%'#9'c #1B4B6A",'#13#10'"5%'#9'c #1' + +'84665",'#13#10'"6%'#9'c #1C4867",'#13#10'"7%'#9'c #18425F",'#13#10'"8%'#9'c' + +' #266489",'#13#10'"9%'#9'c #246187",'#13#10'"0%'#9'c #1F5478",'#13#10'"a%'#9 + +'c #1D4D6C",'#13#10'"b%'#9'c #1C4B6A",'#13#10'"c%'#9'c #27658A",'#13#10'"d%' + +#9'c #225F86",'#13#10'"e%'#9'c #1E4E6D",'#13#10'"f%'#9'c #1D4A68",'#13#10'"g' + +'%'#9'c #1D4765",'#13#10'"h%'#9'c #194362",'#13#10'"i%'#9'c #28658B",'#13#10 + +'"j%'#9'c #226088",'#13#10'"k%'#9'c #28668C",'#13#10'"l%'#9'c #1D5578",'#13 + +#10'"m%'#9'c #1E5579",'#13#10'"n%'#9'c #215578",'#13#10'"o%'#9'c #1C5074",' + +#13#10'"p%'#9'c #1D4866",'#13#10'"q%'#9'c #246289",'#13#10'"r%'#9'c #29668C"' + +','#13#10'"s%'#9'c #246188",'#13#10'"t%'#9'c #1D5579",'#13#10'"u%'#9'c #2259' + +'7C",'#13#10'"v%'#9'c #1D5477",'#13#10'"w%'#9'c #1E5276",'#13#10'"x%'#9'c #1' + +'F5375",'#13#10'"y%'#9'c #25648A",'#13#10'"z%'#9'c #29678C",'#13#10'"A%'#9'c' + +' #28668B",'#13#10'"B%'#9'c #236087",'#13#10'"C%'#9'c #226086",'#13#10'"D%'#9 + +'c #235A7E",'#13#10'"E%'#9'c #23587C",'#13#10'"F%'#9'c #1D4D6E",'#13#10'"G%' + +#9'c #194564",'#13#10'"H%'#9'c #15415F",'#13#10'"I%'#9'c #2A678C",'#13#10'"J' + +'%'#9'c #1C5376",'#13#10'"K%'#9'c #1C5073",'#13#10'"L%'#9'c #20597C",'#13#10 + +'"M%'#9'c #1F567A",'#13#10'"N%'#9'c #25638A",'#13#10'"O%'#9'c #1D567A",'#13 + +#10'"P%'#9'c #1F5579",'#13#10'"Q%'#9'c #194A6B",'#13#10'"R%'#9'c #194464",' + +#13#10'"S%'#9'c #236188",'#13#10'"T%'#9'c #29668D",'#13#10'"U%'#9'c #2A678D"' + +','#13#10'"V%'#9'c #246189",'#13#10'"W%'#9'c #23597C",'#13#10'"X%'#9'c #1E54' + +'76",'#13#10'"Y%'#9'c #225678",'#13#10'"Z%'#9'c #184768",'#13#10'"`%'#9'c #1' + +'B4866",'#13#10'" &'#9'c #236288",'#13#10'".&'#9'c #245B7C",'#13#10'"+&'#9'c' + +' #1C5274",'#13#10'"@&'#9'c #236289",'#13#10'"#&'#9'c #29688D",'#13#10'"$&'#9 + +'c #22597D",'#13#10'"%&'#9'c #1E5377",'#13#10'"&&'#9'c #225879",'#13#10'"*&' + +#9'c #205374",'#13#10'"=&'#9'c #194A6C",'#13#10'"-&'#9'c #1E4F6F",'#13#10'";' + +'&'#9'c #1A4A6A",'#13#10'">&'#9'c #1A4666",'#13#10'",&'#9'c #143D59",'#13#10 + +'"''&'#9'c #19405D",'#13#10'")&'#9'c #24628A",'#13#10'"!&'#9'c #2A688D",'#13 + +#10'"~&'#9'c #1D4F6F",'#13#10'"{&'#9'c #19405C",'#13#10'"]&'#9'c #24638A",' + +#13#10'"^&'#9'c #236187",'#13#10'"/&'#9'c #225779",'#13#10'"(&'#9'c #215475"' + +','#13#10'"_&'#9'c #205373",'#13#10'":&'#9'c #1B4560",'#13#10'"<&'#9'c #143B' + +'57",'#13#10'"[&'#9'c #2A688E",'#13#10'"}&'#9'c #21577B",'#13#10'"|&'#9'c #1' + +'B5173",'#13#10'"1&'#9'c #184464",'#13#10'"2&'#9'c #29688E",'#13#10'"3&'#9'c' + +' #245C81",'#13#10'"4&'#9'c #245C80",'#13#10'"5&'#9'c #21597D",'#13#10'"6&'#9 + +'c #245A7E",'#13#10'"7&'#9'c #1D5174",'#13#10'"8&'#9'c #1E5073",'#13#10'"9&' + +#9'c #164262",'#13#10'"0&'#9'c #1C4967",'#13#10'"a&'#9'c #23628A",'#13#10'"b' + +'&'#9'c #245E81",'#13#10'"c&'#9'c #1C4969",'#13#10'"d&'#9'c #23638A",'#13#10 + +'"e&'#9'c #226188",'#13#10'"f&'#9'c #1B4F70",'#13#10'"g&'#9'c #1D4B69",'#13 + +#10'"h&'#9'c #174363",'#13#10'"i&'#9'c #29698E",'#13#10'"j&'#9'c #1F597E",' + +#13#10'"k&'#9'c #1F577B",'#13#10'"l&'#9'c #1D5376",'#13#10'"m&'#9'c #1C4460"' + +','#13#10'"n&'#9'c #2A698E",'#13#10'"o&'#9'c #29678E",'#13#10'"p&'#9'c #245B' + +'7F",'#13#10'"q&'#9'c #29688F",'#13#10'"r&'#9'c #265D82",'#13#10'"s&'#9'c #2' + +'0587D",'#13#10'"t&'#9'c #1C5174",'#13#10'"u&'#9'c #194665",'#13#10'"v&'#9'c' + +' #28688E",'#13#10'"w&'#9'c #2A698F",'#13#10'"x&'#9'c #236189",'#13#10'"y&'#9 + +'c #1E5072",'#13#10'"z&'#9'c #17405D",'#13#10'"A&'#9'c #28678E",'#13#10'"B&' + +#9'c #27678D",'#13#10'"C&'#9'c #245D81",'#13#10'"D&'#9'c #1C5375",'#13#10'"E' + +'&'#9'c #173F5D",'#13#10'"F&'#9'c #28668D",'#13#10'"G&'#9'c #27658C",'#13#10 + +'"H&'#9'c #2B698F",'#13#10'"I&'#9'c #25638B",'#13#10'"J&'#9'c #235A7B",'#13 + +#10'"K&'#9'c #1E5375",'#13#10'"L&'#9'c #19496A",'#13#10'"M&'#9'c #194869",' + +#13#10'"N&'#9'c #26658C",'#13#10'"O&'#9'c #26658B",'#13#10'"P&'#9'c #245D7F"' + +','#13#10'"Q&'#9'c #205172",'#13#10'"R&'#9'c #1B425E",'#13#10'"S&'#9'c #2564' + +'8C",'#13#10'"T&'#9'c #27668C",'#13#10'"U&'#9'c #255D82",'#13#10'"V&'#9'c #1' + +'F5273",'#13#10'"W&'#9'c #17415D",'#13#10'"X&'#9'c #24638B",'#13#10'"Y&'#9'c' + +' #2A6990",'#13#10'"Z&'#9'c #1F587D",'#13#10'"`&'#9'c #19496B",'#13#10'" *'#9 + +'c #23638B",'#13#10'".*'#9'c #29698F",'#13#10'"+*'#9'c #1D4C6C",'#13#10'"@*' + ,#9'c #2B6A90",'#13#10'"#*'#9'c #1C4C6B",'#13#10'"$*'#9'c #1B4969",'#13#10'"%' + +'*'#9'c #27678E",'#13#10'"&*'#9'c #1D4B6B",'#13#10'"**'#9'c #153D5A",'#13#10 + +'"=*'#9'c #1A415E",'#13#10'"-*'#9'c #26658D",'#13#10'";*'#9'c #24648B",'#13 + +#10'">*'#9'c #21597C",'#13#10'",*'#9'c #1D4F71",'#13#10'"''*'#9'c #1A4A6C",' + +#13#10'")*'#9'c #1C4A6A",'#13#10'"!*'#9'c #24648C",'#13#10'"~*'#9'c #2A6A90"' + +','#13#10'"{*'#9'c #1E5679",'#13#10'"]*'#9'c #225778",'#13#10'"^*'#9'c #1849' + +'69",'#13#10'"/*'#9'c #28678D",'#13#10'"(*'#9'c #194868",'#13#10'"_*'#9'c #1' + +'33B57",'#13#10'":*'#9'c #1F577D",'#13#10'"<*'#9'c #205577",'#13#10'"[*'#9'c' + +' #163C58",'#13#10'"}*'#9'c #1B415D",'#13#10'"|*'#9'c #27668D",'#13#10'"1*'#9 + +'c #2A688F",'#13#10'"2*'#9'c #133956",'#13#10'"3*'#9'c #25658C",'#13#10'"4*' + +#9'c #23638C",'#13#10'"5*'#9'c #27668E",'#13#10'"6*'#9'c #25658D",'#13#10'"7' + +'*'#9'c #2B6A91",'#13#10'"8*'#9'c #2A6991",'#13#10'"9*'#9'c #26668D",'#13#10 + +'"0*'#9'c #296990",'#13#10'"a*'#9'c #26678E",'#13#10'"b*'#9'c #256085",'#13 + +#10'"c*'#9'c #1F577A",'#13#10'"d*'#9'c #1E5577",'#13#10'"e*'#9'c #24658C",' + +#13#10'"f*'#9'c #2A6B91",'#13#10'"g*'#9'c #215C82",'#13#10'"h*'#9'c #245A7D"' + +','#13#10'"i*'#9'c #2B6B91",'#13#10'"j*'#9'c #1F5678",'#13#10'"k*'#9'c #296A' + +'90",'#13#10'"l*'#9'c #1B4B6B",'#13#10'"m*'#9'c #27688E",'#13#10'"n*'#9'c #2' + +'66184",'#13#10'"o*'#9'c #205A80",'#13#10'"p*'#9'c #1C4D6F",'#13#10'"q*'#9'c' + +' #1F506F",'#13#10'"r*'#9'c #24658D",'#13#10'"s*'#9'c #205A7F",'#13#10'"t*'#9 + +'c #174564",'#13#10'"u*'#9'c #1B4A69",'#13#10'"v*'#9'c #215C83",'#13#10'"w*' + +#9'c #1E5070",'#13#10'"x*'#9'c #1A4B6B",'#13#10'"y*'#9'c #1B4765",'#13#10'"z' + +'*'#9'c #164260",'#13#10'"A*'#9'c #25668D",'#13#10'"B*'#9'c #23648B",'#13#10 + +'"C*'#9'c #2A6A91",'#13#10'"D*'#9'c #225577",'#13#10'"E*'#9'c #1B4D70",'#13 + +#10'"F*'#9'c #184361",'#13#10'"G*'#9'c #27688F",'#13#10'"H*'#9'c #245F85",' + +#13#10'"I*'#9'c #1C4F70",'#13#10'"J*'#9'c #215E83",'#13#10'"K*'#9'c #245E83"' + +','#13#10'"L*'#9'c #16405E",'#13#10'"M*'#9'c #296890",'#13#10'"N*'#9'c #1640' + +'5F",'#13#10'"O*'#9'c #1C4864",'#13#10'"P*'#9'c #17415E",'#13#10'"Q*'#9'c #1' + +'D4764",'#13#10'"R*'#9'c #1B4662",'#13#10'"S*'#9'c #17425F",'#13#10'"T*'#9'c' + +' #28688F",'#13#10'"U*'#9'c #1C4C6D",'#13#10'"V*'#9'c #163F5D",'#13#10'"W*'#9 + +'c #20597F",'#13#10'"X*'#9'c #276184",'#13#10'"Y*'#9'c #1D5276",'#13#10'"Z*' + +#9'c #225679",'#13#10'"`*'#9'c #153F5C",'#13#10'" ='#9'c #215C80",'#13#10'".' + +'='#9'c #205272",'#13#10'"+='#9'c #113754",'#13#10'"@='#9'c #225D81",'#13#10 + +'"#='#9'c #1E5478",'#13#10'"$='#9'c #1C4966",'#13#10'"%='#9'c #143D5A",'#13 + +#10'"&='#9'c #245F83",'#13#10'"*='#9'c #24587B",'#13#10'"=='#9'c #194563",' + +#13#10'"-='#9'c #17405C",'#13#10'";='#9'c #205679",'#13#10'">='#9'c #1E4B68"' + +','#13#10'",='#9'c #26668E",'#13#10'"''='#9'c #164362",'#13#10'")='#9'c #1B4' + +'766",'#13#10'"!='#9'c #1A425F",'#13#10'"~='#9'c #205478",'#13#10'"{='#9'c #' + +'194562",'#13#10'"]='#9'c #1A435E",'#13#10'"^='#9'c #225C82",'#13#10'"/='#9 + +'c #173F5C",'#13#10'"(='#9'c #1F5578",'#13#10'"_='#9'c #184260",'#13#10'":=' + +#9'c #266288",'#13#10'"<='#9'c #1B4E71",'#13#10'"[='#9'c #163D5A",'#13#10'"}' + +'='#9'c #1A415D",'#13#10'"|='#9'c #183F5C",'#13#10'"1='#9'c #1A405C",'#13#10 + +'"2='#9'c #21587A",'#13#10'"3='#9'c #1C4B6C",'#13#10'"4='#9'c #113753",'#13 + +#10'"5='#9'c #1E4D6B",'#13#10'"6='#9'c #163E5C",'#13#10'"7='#9'c #19415C",' + +#13#10'"8='#9'c #173E59",'#13#10'"9='#9'c #1C4562",'#13#10'"0='#9'c #1A425D"' + +','#13#10'"a='#9'c #183F5A",'#13#10'"b='#9'c #256187",'#13#10'"c='#9'c #143D' + +'5B",'#13#10'"d='#9'c #163F5B",'#13#10'"e='#9'c #153C58",'#13#10'"f='#9'c #1' + +'A415C",'#13#10'"g='#9'c #193F5B",'#13#10'"h='#9'c #123854",'#13#10'"i='#9'c' + +' #1E4968",'#13#10'"j='#9'c #133A56",'#13#10'"k='#9'c #19405B",'#13#10'"l='#9 + +'c #184461",'#13#10'"m='#9'c #255A7E",'#13#10'"n='#9'c #24648D",'#13#10'"o=' + +#9'c #1A4563",'#13#10'"p='#9'c #153D59",'#13#10'"q='#9'c #193F5A",'#13#10'"r' + +'='#9'c #215B81",'#13#10'"s='#9'c #255B7F",'#13#10'"t='#9'c #194565",'#13#10 + +'"u='#9'c #1A435F",'#13#10'"v='#9'c #173F5B",'#13#10'"w='#9'c #1C4E6E",'#13 + +#10'"x='#9'c #1D4D6D",'#13#10'"y='#9'c #163C59",'#13#10'"z='#9'c #205474",' + +#13#10'"A='#9'c #1A4A69",'#13#10'"B='#9'c #246086",'#13#10'"C='#9'c #1A4C6C"' + +','#13#10'"D='#9'c #113653",'#13#10'"E='#9'c #163E59",'#13#10'"F='#9'c #2153' + +'75",'#13#10'"G='#9'c #235B7D",'#13#10'"H='#9'c #153B58",'#13#10'"I='#9'c #1' + +'94768",'#13#10'"J='#9'c #143B56",'#13#10'"K='#9'c #113652",'#13#10'"L='#9'c' + +' #133A55",'#13#10'"M='#9'c #173D5A",'#13#10'"N='#9'c #1A405B",'#13#10'"O='#9 + +'c #133955",'#13#10'"P='#9'c #183E5A",'#13#10'"Q='#9'c #123754",'#13#10'"R=' + +#9'c #20567B",'#13#10'"S='#9'c #1B445F",'#13#10'"T='#9'c #123753",'#13#10'"U' + +'='#9'c #153E5A",'#13#10'"V='#9'c #183E59",'#13#10'"W='#9'c #225F84",'#13#10 + +'"X='#9'c #163C57",'#13#10'"Y='#9'c #226289",'#13#10'"Z='#9'c #1A415B",'#13 + +#10'"`='#9'c #1C4C6E",'#13#10'" -'#9'c #173E5B",'#13#10'".-'#9'c #143A56",' + +#13#10'"+-'#9'c #25648B",'#13#10'"@-'#9'c #163B57",'#13#10'"#-'#9'c #1F557A"' + ,','#13#10'"$-'#9'c #183D58",'#13#10'"%-'#9'c #29678D",'#13#10'"&-'#9'c #1740' + +'5E",'#13#10'"*-'#9'c #163D58",'#13#10'"=-'#9'c #1F5A7F",'#13#10'"--'#9'c #1' + +'53B57",'#13#10'";-'#9'c #133854",'#13#10'">-'#9'c #163B56",'#13#10'",-'#9'c' + +' #26668C",'#13#10'"''-'#9'c #1F567B",'#13#10'")-'#9'c #184360",'#13#10'"!-' + +#9'c #235E85",'#13#10'"~-'#9'c #1F4D6B",'#13#10'"{-'#9'c #153C57",'#13#10'"]' + +'-'#9'c #103652",'#13#10'"^-'#9'c #266187",'#13#10'"/-'#9'c #27668B",'#13#10 + +'"(-'#9'c #103551",'#13#10'"_-'#9'c #235E82",'#13#10'":-'#9'c #205C81",'#13 + +#10'"<-'#9'c #174360",'#13#10'"[-'#9'c #27658B",'#13#10'"}-'#9'c #22597B",' + +#13#10'"|-'#9'c #173D58",'#13#10'"1-'#9'c #25628A",'#13#10'"2-'#9'c #143955"' + +','#13#10'"3-'#9'c #246389",'#13#10'"4-'#9'c #1E4F6E",'#13#10'"5-'#9'c #2157' + +'7C",'#13#10'"6-'#9'c #205170",'#13#10'"7-'#9'c #28658C",'#13#10'"8-'#9'c #2' + +'35F84",'#13#10'"9-'#9'c #26648A",'#13#10'"0-'#9'c #215A80",'#13#10'"a-'#9'c' + +' #1B4665",'#13#10'"b-'#9'c #133954",'#13#10'"c-'#9'c #103651",'#13#10'"d-'#9 + +'c #28678C",'#13#10'"e-'#9'c #174160",'#13#10'"f-'#9'c #1D5476",'#13#10'"g-' + +#9'c #256289",'#13#10'"h-'#9'c #173C58",'#13#10'"i-'#9'c #19405A",'#13#10'"j' + +'-'#9'c #1A3F5A",'#13#10'"k-'#9'c #153A55",'#13#10'"l-'#9'c #103550",'#13#10 + +'"m-'#9'c #256389",'#13#10'"n-'#9'c #143A57",'#13#10'"o-'#9'c #27648A",'#13 + +#10'"p-'#9'c #1B425D",'#13#10'"q-'#9'c #143A55",'#13#10'"r-'#9'c #215F85",' + +#13#10'"s-'#9'c #18405E",'#13#10'"t-'#9'c #153B56",'#13#10'"u-'#9'c #113651"' + +','#13#10'"v-'#9'c #143A54",'#13#10'"w-'#9'c #103450",'#13#10'"x-'#9'c #163D' + +'57",'#13#10'"y-'#9'c #193F59",'#13#10'"z-'#9'c #183E58",'#13#10'"A-'#9'c #2' + +'05578",'#13#10'"B-'#9'c #1C4A67",'#13#10'"C-'#9'c #133853",'#13#10'"D-'#9'c' + +' #123652",'#13#10'"E-'#9'c #266389",'#13#10'"F-'#9'c #1A4564",'#13#10'"G-'#9 + +'c #193E59",'#13#10'"H-'#9'c #163A55",'#13#10'"I-'#9'c #29658B",'#13#10'"J-' + +#9'c #153C59",'#13#10'"K-'#9'c #193D58",'#13#10'"L-'#9'c #235F86",'#13#10'"M' + +'-'#9'c #225E85",'#13#10'"N-'#9'c #1A4460",'#13#10'"O-'#9'c #215A7F",'#13#10 + +'"P-'#9'c #1D5175",'#13#10'"Q-'#9'c #173C56",'#13#10'"R-'#9'c #215778",'#13 + +#10'"S-'#9'c #143954",'#13#10'"T-'#9'c #193E58",'#13#10'"U-'#9'c #225C81",' + +#13#10'"V-'#9'c #143B59",'#13#10'"W-'#9'c #123752",'#13#10'"X-'#9'c #286589"' + +','#13#10'"Y-'#9'c #255E83",'#13#10'"Z-'#9'c #225A80",'#13#10'"`-'#9'c #1438' + +'54",'#13#10'" ;'#9'c #21597E",'#13#10'".;'#9'c #173C57",'#13#10'"+;'#9'c #1' + +'84868",'#13#10'"@;'#9'c #153A54",'#13#10'"#;'#9'c #143852",'#13#10'"$;'#9'c' + +' #236085",'#13#10'"%;'#9'c #16425F",'#13#10'"&;'#9'c #10344F",'#13#10'"*;'#9 + +'c #123651",'#13#10'"=;'#9'c #1F4F6E",'#13#10'"-;'#9'c #19435F",'#13#10'";;' + +#9'c #235C81",'#13#10'">;'#9'c #1A4C6F",'#13#10'",;'#9'c #163D5B",'#13#10'"' + +''';'#9'c #193D57",'#13#10'");'#9'c #1B4A6B",'#13#10'"!;'#9'c #183C56",'#13 + +#10'"~;'#9'c #22597E",'#13#10'"{;'#9'c #173A55",'#13#10'"];'#9'c #266286",' + +#13#10'"^;'#9'c #21587D",'#13#10'"/;'#9'c #1B4D6D",'#13#10'"(;'#9'c #10334F"' + +','#13#10'"_;'#9'c #163955",'#13#10'":;'#9'c #1F5377",'#13#10'"<;'#9'c #153D' + +'58",'#13#10'"[;'#9'c #163954",'#13#10'"};'#9'c #20577C",'#13#10'"|;'#9'c #2' + +'45E84",'#13#10'"1;'#9'c #183D59",'#13#10'"2;'#9'c #153A56",'#13#10'"3;'#9'c' + +' #173B55",'#13#10'"4;'#9'c #1F5376",'#13#10'"5;'#9'c #123550",'#13#10'"6;'#9 + +'c #163953",'#13#10'"7;'#9'c #163A56",'#13#10'"8;'#9'c #133753",'#13#10'"9;' + +#9'c #113551",'#13#10'"0;'#9'c #12354F",'#13#10'"a;'#9'c #1D4B68",'#13#10'"b' + +';'#9'c #143C58",'#13#10'"c;'#9'c #153954",'#13#10'"d;'#9'c #133751",'#13#10 + +'"e;'#9'c #113550",'#13#10'"f;'#9'c #173D59",'#13#10'"g;'#9'c #183B56",'#13 + +#10'"h;'#9'c #193E5A",'#13#10'"i;'#9'c #193C57",'#13#10'"j;'#9'c #1B4B6D",' + +#13#10'"k;'#9'c #193C56",'#13#10'"l;'#9'c #235D83",'#13#10'"m;'#9'c #235678"' + +','#13#10'"n;'#9'c #204F6D",'#13#10'"o;'#9'c #143953",'#13#10'"p;'#9'c #235C' + +'7F",'#13#10'"q;'#9'c #174665",'#13#10'"r;'#9'c #123551",'#13#10'"s;'#9'c #1' + +'83E5B",'#13#10'"t;'#9'c #10334E",'#13#10'"u;'#9'c #133752",'#13#10'"v;'#9'c' + +' #0F334E",'#13#10'"w;'#9'c #123751",'#13#10'"x;'#9'c #0F324E",'#13#10'"y;'#9 + +'c #153853",'#13#10'"z;'#9'c #1F5272",'#13#10'"A;'#9'c #0F324D",'#13#10'"B;' + +#9'c #153953",'#13#10'"C;'#9'c #193C55",'#13#10'"D;'#9'c #215575",'#13#10'"E' + +';'#9'c #1D5073",'#13#10'"F;'#9'c #183C55",'#13#10'"G;'#9'c #11344F",'#13#10 + +'"H;'#9'c #143752",'#13#10'"I;'#9'c #193B55",'#13#10'"J;'#9'c #173A54",'#13 + +#10'"K;'#9'c #10344E",'#13#10'"L;'#9'c #123853",'#13#10'"M;'#9'c #163A54",' + +#13#10'"N;'#9'c #133852",'#13#10'"O;'#9'c #143751",'#13#10'"P;'#9'c #1C4B69"' + +','#13#10'"Q;'#9'c #164462",'#13#10'"R;'#9'c #183D57",'#13#10'"S;'#9'c #1134' + +'50",'#13#10'"T;'#9'c #11344E",'#13#10'"U;'#9'c #183C57",'#13#10'"V;'#9'c #1' + +'83B55",'#13#10'"W;'#9'c #133651",'#13#10'"X;'#9'c #22577B",'#13#10'"Y;'#9'c' + +' #183B54",'#13#10'"Z;'#9'c #225A7F",'#13#10'"`;'#9'c #163952",'#13#10'" >'#9 + +'c #163C56",'#13#10'".>'#9'c #193B54",'#13#10'"+>'#9'c #13354F",'#13#10'"@>' + +#9'c #1C5071",'#13#10'"#>'#9'c #143853",'#13#10'"$>'#9'c #10324C",'#13#10'"%' + ,'>'#9'c #153751",'#13#10'"&>'#9'c #0F314B",'#13#10'"*>'#9'c #173F5A",'#13#10 + +'"=>'#9'c #133652",'#13#10'"->'#9'c #11334D",'#13#10'";>'#9'c #215372",'#13 + +#10'">>'#9'c #18405B",'#13#10'",>'#9'c #11354F",'#13#10'"''>'#9'c #173952",' + +#13#10'")>'#9'c #0F304B",'#13#10'"!>'#9'c #13354E",'#13#10'"~>'#9'c #183A53"' + +','#13#10'"{>'#9'c #13364F",'#13#10'"]>'#9'c #0E304B",'#13#10'"^>'#9'c #1A44' + +'5F",'#13#10'"/>'#9'c #143650",'#13#10'"(>'#9'c #1E4E6C",'#13#10'"_>'#9'c #1' + +'73A53",'#13#10'":>'#9'c #0E304A",'#13#10'"<>'#9'c #163A53",'#13#10'" ' + +' . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * = ' + +'= = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , '' '' '' ' + +''' '' '' '' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) '' '' '' '' '' '' '' , , , , , , , ,' + +' , , > ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * & & & % % % % % $ $ ' + +'$ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^' + +' ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 ' + +'2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e' + +' e e e e f g g g g g h i j j j j k k l l l l m m m m n o o p p p p q q q q ' + +'q r r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C' + +' C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L M M M M ' + +'M N N N N N O P P Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` `' + +' ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.' + +',.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._.:.:.:.:.<.[.[.[.' + +'[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.",'#13 + +#10'" . + + + + + + + + @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & &' + +' & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , ''' + +' '' '' '' '' '' ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) '' '' '' '' '' '' , ' + +', , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & & %' + +' % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ' + +'~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | |' + +' 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b ' + +'b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n n o o p p p' + +' q q q q q q r s s s s t t t u u u v v v v w w x x x x x x a.y y y y z z A ' + +'B B B B B C C C D D D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L' + +' M M M M M N N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y ' + +'Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;' + +'.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.' + +':.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8' + +'.9.",'#13#10'" . + + + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % %' + +' % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' + +', '' '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) '' ' + +''' '' '' '' '' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = ' + +'= * * & & & % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + + . ' + +' ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ ' + +'} } | | | | 1 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0' + +' 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n ' + +'n o o p p p h.q q q q q r s s s s s t t u u u v v v v v w x x x x x x x y y' + +' y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J ' + +'J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X' + +' X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.' + +'=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(' + +'._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.' + +'5.7.7.8.8.8.9.",'#13#10'" . + + + + + + + g.@ @ @ @ @ @ @ # # # $ $ $ ' + +'$ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , ,' + +' , , , , '' '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !' + +' ! ) ) ) ) '' '' '' '' '' '' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - - ' + +'= = = = = = = * * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ g.+ + + + +' + +' + + . ~ ~ { { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0.' + +'[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9' + +' 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l ' + +'l m m m m n o o o p p p q q q q q r r s s s s t t t u u u v v v v w w x x x' + +' x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H ' + +'H I I J J J c.K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V' + ,' V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.' + +'&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e./.' + +'/././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m' + +'.5.5.5.5.5.6.7.8.8.8.9.",'#13#10'" . + + + + + + + n.@ @ @ @ @ @ @ # # #' + +' $ $ $ $ $ $ % % % % & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; > , ' + +', , , , , , , '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' , , , , , , , , > ; ; ; ; ; ; ; ; ;' + +' ; ; - - = = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.' + +'+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : :' + +' : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 ' + +'7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k' + +' k l l l m m m m n n o o p p p q q q q q q r s s s s s t t u u u v v v v w ' + +'w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H' + +' H H H H I I J J J J K K K L L M M M M M N N N N N O P P Q Q Q R R S S S T ' + +'T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$' + +'.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^' + +'.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.' + +'4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.",'#13#10'" . + + + + + + + n.@ @ @ @ @ @ @ ' + +'# # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ;' + +' , , , , , , , , '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o' + +'.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' , , , , , , , , ; ; ; ; ' + +'; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @' + +' @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ' + +'_ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5' + +' 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e e g g g g g h h j ' + +'j j j k k l l l l m m m n n o o p p p p q q q q q r r s s s s t t u u u v v' + +' v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G ' + +'G G G i.H H H H H I I J J J c.K K L L M M M M M j.N N N N N O P Q Q Q R R S' + +' S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.' + +'$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~' + +'.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.' + +'2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.",'#13#10'". + + + + + + + + @ @ @ @ ' + +'@ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ;' + +' ; ; , , , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.' + +'o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o' + +'.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , , ' + +', ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # #' + +' @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ' + +'( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5' + +' 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g ' + +'g h i j j j k k k l l l m m m m n o o o p p p q q q q q q r s s s s t t t u' + +' u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E ' + +'F G G G G G i.H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q' + +' R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.' + +'#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.~' + +'.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.' + +'1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.",'#13#10'". + + + + + + + @ @ ' + +'@ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ;' + +' ; ; ; ; , , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.' + +'o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o' + +'.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , ' + +', , , , , ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $' + +' $ # # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / ' + +'/ / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3' + +' 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e f g ' + +'g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s s' + +' t t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C D D E E ' + +'E E E F G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P' + +' Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.' + +'@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.''.''.''.).).' + +').!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|' + +'.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.",'#13#10'"+ + + + + + + g' + ,'.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ' + +'; ; ; ; ; > , , , , , , , '' '' '' '' '' ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.' + +'o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p' + +'.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ) ) '' '' '' ' + +''' '' , , , , , , , > ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % %' + +' $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ' + +'^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2' + +' 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e ' + +'e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r r' + +' s s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C ' + +'D D D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L M M M M M N N N' + +' N N O P P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . .' + +'..+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''' + +'.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}' + +'.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.",'#13#10'"+ + + +' + +' + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ' + +'; ; ; ; ; ; ; ; , , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! o' + +'.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.' + +'p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ) ) )' + +' '' '' '' '' , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & ' + +'% % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ {' + +' { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 ' + +'2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d' + +' e e e e e g g g g g g h i j j j k k k l l l m m m m n n o o p p p q q q q ' + +'q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C' + +' C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L M M M M M ' + +'j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` `' + +' . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.' + +'''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.' + +'[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.",'#13#10'"+ ' + +'+ + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ;' + +' ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! o.' + +'o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.p.p' + +'.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ' + +'! ! ) ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = *' + +' & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + + . ' + +'~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | |' + +' 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a b b b ' + +'c d d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q' + +' q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B ' + +'B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M' + +' M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ' + +'Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;' + +'.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.' + +'[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.",'#13 + +#10'"+ + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = -' + +' - ; ; ; ; ; ; ; ; ; > , , , , , , , '' '' '' '' ) ) ! ! ! ! ! ! ! ! ! ! ! ' + +'o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q' + +'.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.! ! ! ! ' + +'! ! ! ! ! ! ! ) ) '' '' '' '' , , , , , , , > ; ; ; ; ; ; ; ; ; - - = = = =' + +' = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ' + +' ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } |' + +' | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a ' + +'a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p' + +' p p q q q q q r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z ' + +'A B B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L' + +' L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y ' + +'Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;' + +'.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.' + +':.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8."' + +','#13#10'"+ + + + g.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = =' + +' = - - ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ' + +'! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q' + +'.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.' + ,'o.! ! ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; - -' + +' = = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ g.+ + + + + + + ' + +'. ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [' + +' [ } | | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 ' + +'0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n n' + +' o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y ' + +'y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I J J J J c' + +'.K K L L M M M M M N N N N N O P P Q Q R R R S S S T T T U V V V V W r.X X ' + +'X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-' + +'.-.-.-.;.;.;.>.,.,.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._' + +'.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.' + +'8.8.",'#13#10'"+ + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = ' + +'= = = - - ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' '' ) ) ! ! ! ! ! ! ! ! !' + +' ! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.' + +'q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.o.o' + +'.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ' + +'; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + +' + +' + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ ' + +'[ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9' + +' 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m ' + +'m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y' + +' y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J ' + +'J J c.K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W' + +' X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.' + +'=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(.' + +'_._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5' + +'.7.7.8.8.",'#13#10'"+ + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * = = =' + +' = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' ) ) ) ! ! ! ! ! ! ! !' + +' ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.' + +'s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p' + +'.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ) '' '' '' , , , , , , , ; ; ; ; ;' + +' ; ; ; ; - - = = = = = = = * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + ' + +'+ + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0' + +'.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 ' + +'9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m' + +' m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x ' + +'x a.y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I' + +' J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V ' + +'V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*' + +'.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(' + +'.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.' + +'5.5.6.7.8.8.",'#13#10'"+ + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * * = ' + +'= = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! ' + +'! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.t.t' + +'.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.' + +'p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) '' '' '' , , , , , , , ; ; ' + +'; ; ; ; ; ; ; - - = = = = = = * * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @' + +' + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : ' + +'< < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8' + +' 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l ' + +'l m m m m n o o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x' + +' x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H ' + +'I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V' + +' V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.' + +'*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././.' + +'/.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5' + +'.5.5.5.6.7.8.8.",'#13#10'"+ n.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * =' + +' = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' '' ) ) ! ! ! ! ! ! ! ' + +'! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.t.t.t.t.t.t' + +'.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.q.q.q.q.q.q.q.q.q.' + +'q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) '' '' '' '' , , , , ,' + +' , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ ' + +'@ @ @ n.+ + + + + + + ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : :' + +' : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 ' + ,'7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h h j j j j k k' + +' l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w ' + +'x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G H H H' + +' H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T ' + +'U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&' + +'.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.' + +'e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4' + +'.m.5.5.5.5.5.6.7.7.8.",'#13#10'"+ @ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & &' + +' * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' '' ) ) ! ! ! ! ! ' + +'! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t' + +'.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.q.q.q.' + +'q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) '' '' '' '' ,' + +' , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # ' + +'@ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( (' + +' ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 ' + +'6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j' + +' j k k k l l l m m m m n o o p p p h.q q q q q r s s s s s t t u u u v v v ' + +'v w w x x x x x x y y y y z z z A B B B B C C C D D D E E E E E F G G G G G' + +' i.H H H H H I I J J J c.K K L L M M M M M N N N N N N O P Q Q Q R R S S S ' + +'T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$' + +'.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.].^.' + +'^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4' + +'.4.4.4.m.5.5.5.5.5.6.7.7.8.",'#13#10'"g.@ @ @ @ @ @ # # $ $ $ $ $ % % % % &' + +' & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ) ! ! ! !' + +' ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.' + +'t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.t.t.t.s' + +'.s.s.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ) '' ' + +''' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $' + +' $ # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ' + +'( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5' + +' 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e f g g g g g h ' + +'i j j j k k k l l l m m m m n o o o p p p q q q q q r r s s s s t t u u u v' + +' v v v v w x x x x x x x y y y z z z A B B B B B C C C D D E E E E E F G G ' + +'G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S' + +' S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.' + +'$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.' + +'].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3' + +'.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.",'#13#10'"@ @ @ @ @ @ # # # $ $ $ $ $ % % %' + +' % & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! !' + +' ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t' + +'.t.t.t.s.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ' + +') '' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ ' + +'$ $ $ # # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / /' + +' / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 ' + +'5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g' + +' h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u ' + +'u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G' + +' G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R ' + +'R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#' + +'.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~' + +'.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.' + +'2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.",'#13#10'"@ @ @ @ @ @ # # $ $ $ $ $ % % ' + +'% % & & * * = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ' + +'! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.' + +'t.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! !' + +' ! ) ) '' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = * * & & % % % %' + +' $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ' + +'^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4' + +' 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g ' + +'g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t' + +' u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E ' + +'b.F G G G G G H H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q' + ,' R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.' + +'#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.' + +'~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1' + +'.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.7.7.8.",'#13#10'"@ @ @ @ @ # # $ $ $ $ $ % %' + +' % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! !' + +' ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ' + +'! ! ! ! ) ) '' '' '' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & & % ' + +'% % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^' + +' ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | | 1 1 1 2 2 3 3 3 ' + +'3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e g g' + +' g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r s s s s s ' + +'t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C D D D E E E' + +' E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P ' + +'Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#' + +'.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.' + +'!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1' + +'.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.",'#13#10'"@ @ @ @ @ # # $ $ $ $ $' + +' % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! !' + +' ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ' + +'! ! ! ! ! ! ! ) ) '' '' '' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & ' + +'& % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { {' + +' ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 ' + +'3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e' + +' e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q r r s s ' + +'s s t t t u u v v v v v w x x x x x x x y y y z z z A B B B B B C C C D D E' + +' E E E E F G G G G G i.H H H H H I I J J J c.K K K L M M M M M j.N N N N N ' + +'O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+' + +'.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).' + +').!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|' + +'.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.",'#13#10'"@ @ @ @ # # $ $ $ $' + +' $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) !' + +' ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.' + +'o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = ' + +'= * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + + ~ ~ ~' + +' { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : < < < [ [ [ [ [ [ } | | | | 1 1 1 2 ' + +'2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e' + +' e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q q r ' + +'s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D' + +' D D E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N ' + +'N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+' + +'.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.' + +'''.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.' + +'}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.",'#13#10'"@ @ @ # # $ $ ' + +'$ $ $ % % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ' + +') ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p' + +'.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , , , , , ; ; ; ; ; ; ; ; - - = =' + +' = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1' + +' 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d ' + +'d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q' + +' q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C ' + +'C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N' + +' N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` .' + +' ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.' + +'''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}' + +'.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.",'#13#10'"@ @ @ # # $' + ,' $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , '' '' '' )' + +' ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v' + +'.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.' + +'p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , , , , > ; ; ; ; ; ; ; - - ' + +'= = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | ' + +'| 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c' + +' c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o o p p p q q ' + +'q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B' + +' C C C D D D E E E E b.F G G G G G i.H H H H H I J J J J K K K L L M M M M ' + +'M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` `' + +' . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.' + +'''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.' + +'[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.",'#13#10'"@ @ # # ' + +'$ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , '' '' '' ' + +') ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.q' + +'.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , , , , ; ; ; ; ; ; ; ;' + +' - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | |' + +' | | 1 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b ' + +'b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q' + +' q q q q r r s s s s t t t u u v v v v v w x x x x x x a.y y y z z z A B B ' + +'B B C C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M' + +' M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ' + +'` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>' + +'.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[' + +'.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.",'#13#10'"@ @ #' + +' # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' ' + +') ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q' + +'.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' , , , , , , ; ; ; ; ; ; ' + +'; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + .' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } ' + +'| | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b' + +' b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p ' + +'q q q q q q r s s s s t t t u u u v v v v w x x x x x x x y y y y z z A B B' + +' B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L M M ' + +'M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W r.X X X X Y Y Z Z Z' + +' ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.' + +'>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.' + +'[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.",'#13#10'"@ # ' + +'# $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , , '' '' ' + +''' ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.' + +'q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) '' '' '' , , , , , , ; ; ; ' + +'; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + +' + +' + . ~ ~ ~ { { ] ] ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ ' + +'} } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a' + +' a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p ' + +'p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A' + +' A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L ' + +'L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z' + +' Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.' + +';.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<' + +'.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.",'#13#10 + +'"# # # $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , '' ' + +''' '' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t' + ,'.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , , , , , >' + +' ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ # # # @ @ @ @ @ @ + + ' + +'+ + + + + ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [' + +' [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 ' + +'0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o' + +' o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y ' + +'z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K' + +' K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X ' + +'X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-' + +'.;.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.' + +':.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.",'#13 + +#10'"# # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , '' ' + +''' '' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t' + +'.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , , , ,' + +' , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.' + +'+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[' + +' [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 ' + +'9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n' + +' n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y ' + +'y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I J J J J K' + +' K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X ' + +'X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-' + +'.-.-.;.;.;.>.,.,.''.''.''.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:' + +'.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.",' + +#13#10'"# $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , ''' + +' '' '' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.' + +'u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.' + +'u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , ' + +', , , , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @' + +' @ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < ' + +'< [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9' + +' 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k k l l l m m ' + +'m n n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x x y' + +' y y z z z A B B B B C C C C D D E E E E E F G G G G G i.H H H H H I I J J ' + +'J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X' + +' X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.' + +'-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._.' + +'_.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7."' + +','#13#10'"# $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ,' + +' '' '' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.' + +'u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.' + +'u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' ,' + +' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ ' + +'@ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : <' + +' < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 ' + +'9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m' + +' m m n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x ' + +'y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J' + +' J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X ' + +'X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-' + +'.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._' + +'._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.' + +'",'#13#10'"$ $ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' + +''' '' '' ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.' + +'u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.x.x.x.y.y.y' + +'.y.y.y.y.x.x.x.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.' + +'u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) '' ''' + +' '' , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ $ # # @ @' + +' @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : ' + +': < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8' + ,' 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l ' + +'m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x' + +' x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I ' + +'J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W' + +' X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.' + +'=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(.' + +'_._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6' + +'.7.",'#13#10'"$ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , ,' + +' '' '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.y.y.y.y.y.y.y.y.' + +'y.y.y.y.y.y.y.y.y.y.y.y.y.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u' + +'.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' + +''' '' '' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # ' + +'@ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ :' + +' : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 ' + +'7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h j j j j k k l l' + +' l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x ' + +'x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I' + +' I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V ' + +'V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*' + +'.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(' + +'.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.' + +'5.6.7.",'#13#10'"$ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; > , , , ' + +', , '' '' ) ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.y.y.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ' + +') ) '' '' , , , , , > ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # #' + +' @ @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ ' + +': : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7' + +' 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l ' + +'l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v v w x x x' + +' x x x y y y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H ' + +'I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V' + +' V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.' + +'*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././' + +'.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.' + +'5.5.5.7.",'#13#10'"$ $ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; , , , ' + +', , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.' + +'u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.z.z.z.z.z.z.z.z.z' + +'.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! !' + +' ! ! ) '' '' '' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % % $ $ $ $' + +' $ # # @ @ @ @ @ @ + + + + + + + ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ' + +'( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6' + +' 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k ' + +'k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u v v v v v w x' + +' x x x x x x y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H ' + +'H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U' + +' V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.' + +'&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e./' + +'./././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.' + +'5.5.5.5.5.6.",'#13#10'"$ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; , , ' + +', , , '' '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.' + +'u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z' + +'.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! !' + +' ! ! ! ! ) ) '' '' '' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % % $' + +' $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ' + +'( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5' + +' 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j ' + +'j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v' + +' w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.' + +'H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T' + ,' T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.' + +'k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.].^.^.^.^' + +'.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.' + +'4.m.5.5.5.5.5.6.",'#13#10'"$ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; ' + +', , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u' + +'.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.x.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.' + +'o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % %' + +' $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ' + +'( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5' + +' 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h h j ' + +'j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v' + +' v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G ' + +'i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T' + +' T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.' + +'%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.].^.^.^.' + +'^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4' + +'.4.m.5.5.5.5.5.6.",'#13#10'"$ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; ,' + +' , , , , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u' + +'.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.' + +'o.o.! ! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % ' + +'% % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^' + +' / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 ' + +'5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h' + +' i j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v ' + +'v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G' + +' G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N P P Q Q Q R R S S ' + +'S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$' + +'.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.].].^' + +'.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.' + +'4.4.4.m.5.5.5.5.5.6.",'#13#10'"$ $ % % % & & & * = = = = = - ; ; ; ; ; ; ; ' + +'; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.q.s.t.t.t.t.u.u.u' + +'.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.q.p.p.p.p.' + +'o.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; ; ; - = = = = = * & & &' + +' % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ' + +'^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5' + +' 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g ' + +'h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v' + +' v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G ' + +'G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S' + +' S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.' + +'$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].' + +'^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4' + +'.4.4.4.m.5.5.5.5.5.6.",'#13#10'"$ $ % % % & & * = = = = = - - ; ; ; ; ; ; ;' + +' , , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u' + +'.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y' + +'.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.' + +'p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ; ; ; ; - - = = = = = * ' + +'& & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^' + +' ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 ' + +'4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g' + +' g h i j j j k k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u ' + +'u v v v v v w x x x x x x a.y y y z z z A B B B B C C C D D D E E E E b.F G' + +' G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R ' + +'S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$' + +'.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{' + +'.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.' + +'3.4.4.4.4.4.5.5.5.5.5.6.",'#13#10'"$ % % % % & & * = = = = = - ; ; ; ; ; ; ' + ,'; > , , , , '' '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.' + +'u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z' + +'.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p' + +'.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' '' , , , , > ; ; ; ; ; ; ; - = = = = =' + +' * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ' + +'] ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3' + +' 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d d e e e e e g g ' + +'g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t' + +' u u u v v v v w x x x x x x x y y y z z z A B B B B C C C D D D E E E E E ' + +'F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R' + +' R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.' + +'d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.' + +'~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2' + +'.3.3.4.4.4.4.4.5.5.5.5.5.6.",'#13#10'"$ % % % & & * = = = = = - - ; ; ; ; ;' + +' ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.' + +'u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A' + +'.A.A.A.A.A.A.A.A.A.A.A.B.B.B.B.B.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.' + +'z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q' + +'.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; ; - - = = = ' + +'= = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { {' + +' ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 ' + +'3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g' + +' g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t ' + +'t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E' + +' F G G G G G i.H H H H H I J J J J c.K K L L M M M M M N N N N N O P Q Q Q ' + +'R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#' + +'.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.' + +'~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2' + +'.2.3.3.4.4.4.4.4.5.5.5.5.5.6.",'#13#10'"% % % % & & * = = = = = - - ; ; ; ;' + +' ; ; > , , , , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.u.u' + +'.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.' + +'A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.A.A.z.z.z.z.z' + +'.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.q.' + +'q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) '' '' '' , , , , > ; ; ; ; ; ; - - = ' + +'= = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ~ ~' + +' { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 ' + +'2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e' + +' g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s ' + +'s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E' + +' E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q ' + +'Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#' + +'.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.' + +'~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1' + +'.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.",'#13#10'"% % % & & & = = = = = - - ; ; ;' + +' ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.' + +'u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A' + +'.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.z.' + +'z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.t' + +'.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; ; - ' + +'- = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ~' + +' ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 ' + +'2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e' + +' e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r r s s ' + +'s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E' + +' E E E F G G G G G G H H H H H I I J J J c.K K L L M M M M M N N N N N O P ' + +'P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#' + +'.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.' + +'!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1' + +'.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.5.",'#13#10'"% % % & & * = = = = = - - ; ;' + +' ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t' + +'.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.A.A.A.' + +'A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A' + +'.A.A.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.' + ,'t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; ' + +'; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 ' + +'1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e' + +' e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r ' + +'s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D' + +' D E E E E b.G G G G G G H H H H H I I J J J c.K K K L M M M M M N N N N N ' + +'O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+' + +'.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.''.''.''.).)' + +'.).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.' + +'1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% % % & & * = = = = = - ; ' + +'; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t' + +'.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.A.A.A.A.' + +'A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B' + +'.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.' + +'u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ;' + +' ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1' + +' 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 8 8 9 9 9 9 0 0 a a a b b c c d d ' + +'e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r' + +' s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D ' + +'D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N' + +' N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.' + +'+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).' + +').).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|' + +'.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% % & & * = = = = = - - ;' + +' ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t' + +'.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.' + +'B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B' + +'.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.' + +'u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , , , ; ; ; ' + +'; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | ' + +'| 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d' + +' d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h.q q q q ' + +'q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z A B B B B C C C' + +' D D D E E E E b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N ' + +'N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+' + +'.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''' + +'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.' + +'}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% % & & * = = = = = - ' + +'; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t' + +'.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.' + +'B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.B' + +'.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.' + +'u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ;' + +' ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | |' + +' | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c ' + +'d d e e e e e g g g g g g h i j j j k k l l l m m m m n o o o p p p q q q q' + +' q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C ' + +'C D D D E E E E E F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N' + +' N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .' + +'+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.' + +'''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.' + +'}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% % & & * = = = = = ' + +'- ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t' + +'.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.' + +'B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B' + +'.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.' + +'u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) '' '' , , , , , ;' + +' ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + ' + +'. ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } |' + +' | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b ' + ,'c c d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q' + +' q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B ' + +'C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M M N' + +' N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` .' + +' .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''' + +'.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}' + +'.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% & & * = = = = = -' + +' - ; ; ; ; ; ; > , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t' + +'.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.' + +'B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B' + +'.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.' + +'u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' '' , , , ' + +', > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ n.+ + + +' + +' + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } ' + +'} | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b' + +' b c c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q ' + +'q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B' + +' B C C C D D E E E E E F G G G G G i.H H H H H I J J J J c.K K L L M M M M ' + +'M N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` `' + +' . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''' + +'.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.' + +'}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.",'#13#10'"% & & * = = = = ' + +'= - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t' + +'.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.' + +'B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C' + +'.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.' + +'v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) '' '' , , ,' + +' , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ g.+ + + ' + +'+ + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ }' + +' } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a ' + +'b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q' + +' q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B ' + +'B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M' + +' M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ' + +'` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.' + +'''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.' + +'}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"% & & * = = = ' + +'= - - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.' + +'s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B' + +'.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.' + +'C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w' + +'.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' ''' + +' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + ' + +'+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [' + +' [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 ' + +'a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p' + +' p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A ' + +'B B B B C C C D D D E E E E F G G G G G G H H H H H I I J J J c.K K L L M M' + +' M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ' + +'` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>' + +'.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[' + +'.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & * = = =' + +' = = - - ; ; ; ; ; ; > , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.' + +'s.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B' + +'.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.' + +'C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w' + +'.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' ' + +''' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ ' + +'+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [' + +' [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 ' + +'0 a a b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p' + +' p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A ' + +'A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K L L M' + +' M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z ' + ,'Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;' + +'.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.' + +'[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & * = ' + +'= = = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q' + +'.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.A.B.' + +'B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D' + +'.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.' + +'w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' + +''' '' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @' + +' @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.' + +'[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0' + +' 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o ' + +'o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x a.y y y z z' + +' A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K ' + +'L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V E.F.G.G.G.H.I.I.J' + +'.J.J.K.L.L.M.M.N.O.O.O.P.N.N.N.Q.R.S.S.S.S.T.U.V.V.W.X.X.Y.Z.Z.-.-.-.-.;.;.' + +';.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<' + +'.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & *' + +' = = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.' + +'B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D' + +'.D.D.D.D.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.y.x.' + +'w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! !' + +' ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @' + +' @ @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : ' + +'< 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9' + +' 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m ' + +'n o o p p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y' + +' z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K ' + +'K K L L M M M M j.N N N N N P P Q Q R R R S `.`. +.+++@+#+$+%+%+&+*+*+*+*+*' + +'+*+=+=+=+=+-+-+;+;+>+O.O.O.P.N.N.N.N.N.,+,+,+,+''+''+)+!+!+~+~+~+{+{+]+]+^+' + +'/+(+_+:+<+[+,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:' + +'.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10 + +'"& & = = = = = - - ; ; ; ; ; ; > , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.' + +'p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A' + +'.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.' + +'D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z' + +'.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ' + +'! ! ! ) ) '' '' , , , , > ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # #' + +' @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : ' + +': : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9' + +' 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m ' + +'m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y' + +' y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J ' + +'c.K K K L L M M M M j.N N N N N }+.+|+1+2+2+3+4+4+4+5+5+5+6+%+%+%+%+&+*+*+*' + +'+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.O.N.N.N.N.N.,+,+,+,+''+''+''+!+!+~+~+~+{+{+]' + +'+]+]+]+]+7+7+7+8+9+0+a+_+b+c+d+).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.' + +':.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10 + +'"& * = = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.' + +'q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A' + +'.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.' + +'D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z' + +'.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ' + +'! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # #' + +' @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : ' + +': : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8' + +' 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k l l l m m ' + +'m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y' + +' y y y z z A B B B B C C C D D D E E E E E F G G G G G H H H H H I I J J J ' + +'J K K K L L M M M M e+|+1+f+g+h+i+j+k+k+k+3+3+4+4+4+5+5+5+6+%+%+%+%+&+*+*+*' + +'+*+*+*+=+=+=+=+-+-+-+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+''+!+!+~+~+~+~+{+]+' + +']+]+]+]+7+7+7+8+9+9+0+0+0+m+m+m+n+o+p+q+r+~.].^.^.^.^.e././././.(.(._._.:.:' + +'.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.",'#13#10 + +'"& * = = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p' + ,'.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.' + +'B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.s+s+s+s+t+t+t+t+t+t+t+s' + +'+s+s+s+D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.' + +'z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! !' + +' ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $' + +' # # @ @ @ @ @ n.+ + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : ' + +': : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7' + +' 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l ' + +'m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x' + +' y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J ' + +'J J K K K L J u+v+w+x+x+h+h+h+h+h+i+j+k+k+k+3+3+4+4+4+5+5+5+6+6+%+%+%+&+y+*' + +'+*+*+*+z+A+K.K.K.L.B+B+C+C+D+E+F+F+F+G+G+G+G+H+I+I+I+S.T.T.!+!+~+~+~+~+{+]+' + +']+]+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+L+M+r+~.^.e././././.(.(._._.:.:' + +'.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"& * = = = = - - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.' + +'q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.A.B' + +'.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z' + +'.z.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ' + +'! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ #' + +' # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : ' + +': : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8' + +' 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m ' + +'m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y' + +' y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J ' + +'N+u+O+P+Q+R+S+S+S+S+x+h+h+h+h+h+i+j+k+k+k+3+3+4+4+4+5+5+&+T+@+U+I.H.V+W+X X' + +' X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.Y.Y.Y.X+Y+Z+' + +'`+/+8+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@+@@@#@$@]./.(.(._._._.:.:' + +'.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"&' + +' = = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B' + +'.B.B.B.B.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z' + +'.z.y.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ' + +'! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # #' + +' @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : ' + +': < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9' + +' 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m ' + +'m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y' + +' y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H %@O+w+&@*@' + +'=@=@=@R+R+S+S+S+S+S+h+h+h+h+h+i+j+k+-@f+;@;@.+>@`.F.T T U U V V V W W X X X' + +' X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.' + +'-.-.;.,@<+''@)@!@(+~@0+m+m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@L+^@/@(@_._.:.:.' + +':.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"* ' + +'= = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.' + +'B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t' + +'+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.' + +'z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! !' + +' ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # ' + +'@ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : :' + +' : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 ' + +'9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k l l l m m m' + +' m n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y ' + +'y y z z A A B B B B C C C D D E E E E E F G G G G G i.H _@:@<@[@}@}@*@*@*@|' + +'@=@=@R+R+S+S+S+S+S+h+h+g+f+1@2@>@Q Q Q R R S S S T T T T U V V V W W X X X ' + +'X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-' + +'.-.;.;.;.;.>.,.''.''.''.3@4@5@o+n+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@6@7@8@9@0@a' + +'@:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"*' + +' = = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.' + +'B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t' + +'+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.' + +'z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! !' + ,' ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $' + +' # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : ' + +': : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8' + +' 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m ' + +'m m m n o o p p p q q q q q q r s s s s t t t u u v v v v w w x x x x x x y' + +' y y y z z A A B B B B C C C D D D E E E E F G G G G b@c@d@e@e@e@e@}@}@*@*@' + +'*@|@=@=@R+R+S+x+f@v+u+g@N N N N O P Q Q Q R R S S S T T T T U V V V W W X X' + +' X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.' + +'-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.h@i@j@k@l@.@.@.@{@]@]@]@]@6@6@7@7@7@' + +'m@m@n@o@p@[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"* = = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B' + +'.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z' + +'.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.' + +'! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $' + +' # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : ' + +': : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8' + +' 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m ' + +'m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y' + +' y y y z z A A B B B B C C C D D D E E E E F E q@d@r@r@r@e@e@e@e@e@}@}@*@*@' + +'*@|@=@s@t@u@v@M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X' + +' X X X Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.' + +'-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].w@x@p+y@]@]@]@]@6@6@7@7@7@' + +'m@m@m@z@A@n@B@0@:.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"* = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B' + +'.B.B.C.C.C.C.C.C.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z' + +'.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.' + +'! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $' + +' # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : ' + +': : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8' + +' 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m ' + +'m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y' + +' y y y z z A A B B B B C C C D D D E E b@C@D@r@r@r@r@r@r@e@e@e@e@e@}@}@E@<@' + +'O+u+K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X' + +' X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.' + +'-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e./.F@x@G@7@6@7@7@7@' + +'m@m@m@z@A@A@A@A@A@H@I@J@}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"= = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B' + +'.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@' + +'t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z' + +'.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.' + +'! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $' + +' # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : ' + +': : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8' + +' 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m ' + +'m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a' + +'.y y y z z A A B B B B C C C D D L@M@r@N@N@O@r@r@r@r@r@r@e@e@e@P@Q@R@S@J J ' + +'J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X' + +' X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.' + +'-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.T@U@V@7@' + +'m@m@m@z@z@A@A@A@A@W@W@W@X@Y@J@|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"= = = = = - - ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.' + +'B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@K@K@K' + +'@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.' + +'z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o' + +'.! ! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $' + +' $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( ' + +'_ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7' + +' 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k l l l ' + ,'m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x' + +' a.y y y z z z A B B B B C C Z@`@ #.#N@N@N@N@O@r@r@r@r@r@r@+#@###H H I I J ' + +'J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X' + +' X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.' + +'-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._.' + +'_.$#%#&#z@z@A@A@A@A@W@W@W@W@W@*#=#-#;#1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"= = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.' + +'B.B.B.C.C.C.C.C.D.D.D.D.D.D.t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@>#>#>#K@K' + +'@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+D.D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.' + +'A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o' + +'.o.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ ' + +'$ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( (' + +' _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k l l l' + +' m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x ' + +'x x y y y z z z A B B B L@,#''#)#.#.#.#N@N@N@N@O@r@r@!#~#b@E H H H H H I I ' + +'J J J c.K K K L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W' + +' X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.' + +'=.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._.' + +'_._.:.:.:._.0@{#n@A@A@W@W@W@W@W@*#*#]#]#^#/#|.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"= = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.' + +'B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@>#>#>#>#>#>#>#>#>' + +'#>#>#K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.' + +'A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o' + +'.o.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ ' + +'$ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( (' + +' _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l' + +' m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x ' + +'x x y y y z z z A y (#_#:#)#)#)#.#.#.#N@N@N@N@<#[#}#G G G G H H H H H I I J' + +' J J c.K K K L L M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W ' + +'X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=' + +'.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._' + +'._.:.:.:.<.<.[.[.p@%#|#W@W@W@W@*#*#]#]#]#]#X@1#2#1.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"= = = = - - ; ; ; ; ; ; > , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.' + +'B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.' + +'A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o' + +'.o.o.! ! ! ! ! ! ) '' '' , , , , > ; ; ; ; ; ; - - = = = = * & & % % % $ $ ' + +'$ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( (' + +' _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l' + +' l m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x ' + +'x x y y y y z 3#4#5#:#:#:#)#)#)#.#.#.#N@6#~#}#b.F G G G G G H H H H H I I J' + +' J J c.K K K L L M M M M j.N N N N O P P Q Q R R R S S T T T T U V V V V W ' + +'X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=' + +'.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._' + +'._.:.:.:.<.<.[.[.[.[.[.7#I@H@W@*#*#]#]#]#]#X@X@H@8#9#0#4.4.m.5.5.5.5.",'#13 + +#10'"= = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.' + +'B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.' + +'A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o' + +'.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ ' + +'$ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( (' + +' _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l' + +' l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x ' + +'x x y y a#b#c#d#:#:#:#:#:#)#)#)#.#D@C@}#E E E b.F G G G G G H H H H H I I J' + +' J J c.K K K L L M M M M j.N N N N N P P Q Q R R R S S T T T T U V V V V W ' + +'X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=' + ,'.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._.' + +'_._.:.:.:.<.<.[.[.[.[.[.}.}.}.J@e#X@]#]#]#]#X@X@H@H@=#=#f#2#f.5.5.5.5.",'#13 + +#10'"= = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.' + +'B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.' + +'A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o' + +'.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & % % % % $ ' + +'$ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( (' + +' _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l' + +' l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x ' + +'x x (#g#h#h#d#d#:#:#:#:#:#)#_#i#j#D D E E E E E F G G G G G H H H H H I I J' + +' J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S T T T T U V V V V W ' + +'X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=' + +'.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._.' + +'_._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.k#l#m#]#X@X@H@H@=#=#=#=#n#o#p#5.5.",'#13 + +#10'"= = = = - - ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B' + +'.C.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B' + +'.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.' + +'p.o.o.o.! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; - - = = = = = & & & % % % ' + +'$ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ / / / ( ( (' + +' ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 ' + +'7 7 7 8 9 9 9 9 0 0 0 a a a b c c d d e e e e e g g g g g h i j j j k k k l' + +' l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x ' + +'q#r#d#h#h#h#h#d#d#:#:#:#s#(#y C C C D D E E E E E F G G G G G H H H H H I I' + +' J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V ' + +'W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=' + +'.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.' + +'_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.t#I@=#H@H@=#=#=#=#=#n#n#u#v#",' + +#13#10'"= = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! o.o.o.p.p.p.p.q' + +'.q.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.' + +'B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.' + +'B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p' + +'.p.p.o.o.o.! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % %' + +' % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ' + +'( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6' + +' 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k ' + +'k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x 3' + +'#g#w#x#x#h#h#h#h#d#d#:#y#a#B B B C C C D D E E E E E F G G G G G H H H H H ' + +'I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V' + +' V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.' + +'*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(' + +'.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.2#z#=#=#=#=#=#n#n#n#n#' + +'",'#13#10'"= = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.' + +'p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B' + +'.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B' + +'.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.' + +'p.p.p.p.o.o.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & %' + +' % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ' + +'( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5' + +' 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j ' + +'k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w A#c' + +'#x#x#x#x#x#h#h#h#h#B#C#D#B B B B B C C C D D E E E E E F G G G G G i.H H H ' + +'H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V' + +' V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.' + +'*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././' + +'.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.0#9#n#=#=#n#n#n#' + +'n#",'#13#10'"= = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.' + +'p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.A.A.B.B.B' + ,'.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B' + +'.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.' + +'q.p.p.p.p.o.o.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & &' + +' % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / ' + +'/ ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5' + +' 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j ' + +'j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v v v E#F#G' + +'#x#x#x#x#x#x#h#h#g#H#z z A B B B B B C C C D D E E E E E F G G G G G i.H H ' + +'H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V' + +' V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.' + +'*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^./././' + +'./.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.f.2#f#n#n#' + +'n#n#",'#13#10'"= = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.' + +'p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B' + +'.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C' + +'.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.' + +'q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * &' + +' & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ ' + +'/ / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5' + +' 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j ' + +'j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v I#J#K#G' + +'#G#x#x#x#x#x#L#b#a#y y z z A A B B B B C C C D D E E E E E F G G G G G i.H ' + +'H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U' + +' V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.' + +'&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././' + +'././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.p#' + +'o#M#n#",'#13#10'"= = = = - ; ; ; ; ; ; > , , , , '' '' ) ! ! ! ! ! ! o.o.o.' + +'p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B' + +'.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C' + +'.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.' + +'q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , > ; ; ; ; ; ; - = = = = = *' + +' & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ' + +'^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5' + +' 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c d d e e e e e g g g g g h h j ' + +'j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u t N#O#K#K#K' + +'#G#G#x#x#x#c#C#x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.' + +'H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T' + +' U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.' + +'&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^./' + +'./././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.' + +'5.5.P#Q#",'#13#10'"= = = = - ; ; ; ; ; ; > , , , , '' '' ) ! ! ! ! ! ! o.o.' + +'o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A' + +'.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#R#S#R#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C' + +'.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.' + +'s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , > ; ; ; ; ; ; - = = = = =' + +' * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ' + +'^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5' + +' 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h h ' + +'j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t T#U#V#W#K#K#K' + +'#K#G#G#x#B#3#x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G ' + +'i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T' + +' T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.%.%.' + +'%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^' + +'././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.' + +'4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.' + +'o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A' + +'.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C' + +'.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.' + +'t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - = = = =' + ,' = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ' + +'^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4' + +' 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h ' + +'h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s X#Y#W#W#W#K#K' + +'#K#K#G#Z#`#x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G ' + +'G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T' + +' T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.' + +'%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^' + +'.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.' + +'4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! ' + +'o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A' + +'.A.A.B.B.B.B.B.B.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#' + +'>#>#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+s+D.D.D.D.D' + +'.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.' + +'t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = =' + +' = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ' + +'^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4' + +' 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g ' + +'g h j j j j k k l l l m m m m n o o p p p q q q q q q r s s $.$+$+$W#W#W#K' + +'#K#@$X##$w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G ' + +'G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S' + +' T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.' + +'$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.{.].^.^' + +'.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.' + +'4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ' + +'! o.o.o.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z' + +'.A.A.A.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#' + +'>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D.D' + +'.D.C.C.C.C.C.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.' + +'t.t.t.s.q.q.q.q.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - =' + +' = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ' + +'] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3' + +' 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g ' + +'g g h j j j j k k l l l m m m m n o o p p p q q q q q q r N#W#$$$$+$+$W#W#W' + +'#O#%$&$v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G ' + +'G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S' + +' S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.' + +'$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.{.].^' + +'.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.' + +'4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ' + +'! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z' + +'.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#' + +'>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D' + +'.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.' + +'u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - -' + +' = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { ' + +'{ ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3' + +' 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g ' + +'g g g h j j j j k k l l l m m m m n o o p p p q q q q q *$=$$$$$$$$$+$+$W#V' + +'#N#v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G ' + +'G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S' + +' S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.' + +'$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.{.]' + +'.^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.' + +'4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ' + +'! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z' + +'.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#' + +'>#>#>#R#S#S#S#S#S#S#S#S#S#S#S#R#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+D.D' + +'.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.' + +'u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; -' + +' - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ ' + +'{ { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3' + +' 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g ' + +'g g g g h j j j j k k l l l m m m m n o o p p p q q q -$;$>$,$$$$$$$$$+$Y#X' + ,'#u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F ' + +'G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R' + +' S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.' + +'$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.{' + +'.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.' + +'3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ' + +'! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z' + +'.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#' + +'>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+D' + +'.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.' + +'u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ;' + +' - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ' + +'~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3' + +' 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g ' + +'g g g g g h j j j j k k l l l m m m m n o o p p p q o ''$>$>$>$,$$$$$$$+$)$' + +'T#t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E' + +' F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q ' + +'R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#' + +'.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.~.' + +'~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2' + +'.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' ) !' + +' ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.' + +'z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>' + +'#>#>#>#>#>#R#S#S#S#S#S#S#S#S#S#S#S#R#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+' + +'t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u' + +'.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ' + +'; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 ' + +'2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e' + +' g g g g g g h j j j j k k l l l m m m m n o o p p p !$~$>$>$>$>$,$$$+${$]$' + +'t t t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E' + +' E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q ' + +'Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#' + +'.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.~.' + +'~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2' + +'.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' '' )' + +' ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.' + +'z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>' + +'#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+' + +'t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u' + +'.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ' + +'; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 ' + +'2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e' + +' e g g g g g g h j j j j k k l l l m m m m n o o p ^$/$>$>$>$>$>$>$,$($]$s ' + +'s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E' + +' E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q ' + +'Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#' + +'.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.~.' + +'~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f' + +'.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' ''' + +' ) ! ! ! ! ! ! o.o.o.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.' + +'y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>' + +'#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+' + +'t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v' + +'.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ' + +'; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 ' + +'1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e' + +' e e g g g g g g h j j j j k k l l l m m m m n o _$:$<$<$>$>$>$>$>$[$}$s s ' + +'s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E' + +' E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P ' + +'Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#' + +'.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.' + ,'~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1' + +'.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = - - ; ; ; ; ; ; , , , , , '' ' + +''' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.' + +'x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+K@K@K@>#>' + +'#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+' + +'t+t+t+s+D.D.D.D.D.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w' + +'.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , , ; ' + +'; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 ' + +'1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e' + +' e e e g g g g g g h j j j j k k l l l m m m m n ''$<$<$<$<$>$>$>$|$*$q r s' + +' s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D ' + +'E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O' + +' P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.' + +'#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).)' + +'.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.' + +'1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ; , , , , , ' + +''' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w' + +'.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@' + +'>#>#>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t' + +'+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.' + +'w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , , ,' + +' ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | |' + +' 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d ' + +'e e e e e g g g g g h h j j j j k k l l l m m m 1$2$3$3$<$<$<$>$,$''$q q q ' + +'r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D' + +' D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N ' + +'N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+' + +'.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).' + +').!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|' + +'.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ; > , , ,' + +' , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.' + +'w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K' + +'@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#R#S#R#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+' + +'t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w' + +'.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , , ' + +', > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + +' + +' . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | ' + +'| | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c' + +' d e e e e e g g g g g h h j j j j k k l l l m m 4$5$6$3$3$<$<$<$7$o q q q ' + +'q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C' + +' D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N ' + +'N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+' + +'.+.+.#.#.#.#.#.d.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.' + +').).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}' + +'.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ; > , ,' + +' , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.' + +'w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t' + +'+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@' + +'t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w' + +'.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' , , ' + +', , > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + +' + +' + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | ' + +'| | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c' + +' d d e e e e e g g g g g h h j j j j k k l l l 1$8$5$5$6$3$3$<$2$9$q q q q ' + +'q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C' + +' C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N ' + +'N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+' + +'.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.' + +'''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.' + +'}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ; ; ' + +', , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w' + +'.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+' + ,'t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K' + +'@t+t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.' + +'w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) '' '' ,' + +' , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + ' + +'+ + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } }' + +' | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b ' + +'b c d d e e e e e g g g g g h h j j j j k k l 0$a$5$5$5$5$6$3$3$b$p p q q q' + +' q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B ' + +'C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N' + +' N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` .' + +'..+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.' + +'''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}' + +'.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ; ;' + +' ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.' + +'w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t' + +'+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@' + +'K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.x.w' + +'.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) '' ' + +''' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + ' + +'+ + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [' + +' } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a ' + +'a b b c d d e e e e e g g g g g h i j j j j k k c$d$e$5$5$5$5$6$|$_$p p p q' + +' q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B ' + +'B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M M M' + +' j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ' + +' . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.' + +'''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.' + +'}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ; ; ' + +'; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u' + +'.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+' + +'t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K' + +'@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.' + +'w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' + +''' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ g' + +'.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ ' + +'[ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a' + +' a a b b c d d e e e e e g g g g g h i j j j j ^$f$e$e$e$5$5$5$8$g$o o p p ' + +'p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B' + +' B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M ' + +'M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X h$h$h$i$j$j$j$Z `' + +' ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.' + +'''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[' + +'.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - ; ; ;' + +' ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.u.u.u.u.u' + +'.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.' + +'D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.' + +'y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! )' + +' '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @' + +' @ g.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ ' + +'[ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0' + +' 0 a a a b b c d d e e e e e g g g g g h i j j j k$l$m$e$e$e$5$5$n$0$n o o ' + +'p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A' + +' B B B B B C C C D D E E E E E F G G G G G H H H H H I I J J J J K K K L L ' + +'M M M M j.N N N N N O P Q Q Q R R S S S o$`.>@p$U+#+#+q$&+*+*+*+*+*+*+=+=+=' + +'+=+-+L.r$s$t$+.+.+.@.#.#.#.#.#.$.$.W.,+T.T.u$v$v$w$Y.%.=.=.-.-.-.-.;.;.;.;.' + +'>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[' + +'.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = = = - -' + +' ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u' + +'.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.C.D.' + +'D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.' + +'z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! !' + +' ! ) '' '' '' , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @' + ,' @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < ' + +'< [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9' + +' 0 0 0 a a a b c c d d e e e e e g g g g g h i j j x$m$m$m$e$e$e$y$c$m n n ' + +'o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z' + +' z A B B B B B C C C D D E E E E E F G G G G G H H H H H I I J J J c.K K K ' + +'L L M M M M j.N N N N N O P Q }+z$A$B$C$D$4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=' + +'+=+=+=+-+-+;+;+E$F$` +.@.#.#.#.#.#.$.$.$.G$''+''+)+!+!+~+~+~+{+U./+Z+Y+H$,@' + +';.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l' + +'.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= = =' + +' = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.' + +'t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C' + +'.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#' + +'>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z' + +'.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ' + +'! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & % % % % $ $ $ $ # #' + +' @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : ' + +': < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9' + +' 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i g I$J$m$m$m$e$e$n$^$m m ' + +'n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y' + +' y z z A B B B B C C C D D D E E E E E F G G G G G H H H H H I I J J J c.K ' + +'K K L L M M M M j.N N N N N O -@k+k+k+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*' + +'+*+=+=+=+=+-+-+;+;+>+O.O.G+..#.#.#.#.#.$.$.$.$.U.''+)+!+!+~+~+~+{+{+]+]+]+]' + +'+K$,@;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.' + +':.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"= ' + +'= = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t' + +'.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.' + +'C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.' + +'z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.!' + +' ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ ' + +'# # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : :' + +' : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 ' + +'9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h L$M$J$J$m$m$m$l$c$l m m' + +' m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y ' + +'y y y z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J c' + +'.K K K L L M M M M j.N N N N N P -@k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+' + +'*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.M.t$#.#.#.#.$.$.$.$.%.T.!+!+!+~+~+~+{+{+]+]+]' + +'+]+7+~@;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.' + +':.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.",'#13#10'"' + +'= = = = - - ; ; ; ; ; ; > , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s' + +'.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.' + +'C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>' + +'#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.' + +'z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o' + +'.! ! ! ! ! ! ) '' '' , , , , > ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ ' + +'$ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ :' + +' : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 ' + +'8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g N$O$P$J$J$J$m$m$Q$i l l m' + +' m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x ' + +'y y y y z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J' + +' c.K K K c.u+u@M M M j.N N N N O P R$k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+' + +'*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.P.l+t$#.#.#.$.$.$.$.%.Y.S.!+!+~+~+~+{+{+]+]' + +'+]+]+7+7+S$;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:' + +'.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"= = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.' + +'q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B' + +'.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@>#>#>#>#>#>#>#>#>#>#' + +'>#K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z' + +'.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.' + +'o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $' + +' $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ ' + +': : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7' + +' 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g T$U$V$P$J$J$J$m$4$k l l m ' + +'m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x' + ,' y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J ' + +'J c.S@R@s@R+R+S+c.M M N N N N N O P A$k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*' + +'+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.l+..#.#.$.$.$.$.%.%.Y.~+!+~+~+~+{+{+]+' + +']+]+]+7+7+K$,@>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.' + +':.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"= = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.' + +'q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B' + +'.B.C.C.C.C.C.D.D.D.D.D.D.t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@>#>#>#K@K@K@' + +'K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+D.D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z' + +'.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.' + +'o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $' + +' # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ ' + +': : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7' + +' 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g W$X$Y$V$P$J$J$Z$`$k l l l m ' + +'m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x x' + +' y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I N+O+' + +'<@|@=@=@=@R+S+S+u+M M N N N N N O P %k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*' + +'+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.R.#.d.$.$.$.$.%.%.&.t$!+~+~+~+{+{+]+' + +']+]+]+7+7+7+~@>.,.''.''.''.).).).!.!.~.~.~.~.i@L+p+x@~.e././././.(.(._._._.' + +':.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"= = = = = - - ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.' + +'B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@K@K@K' + +'@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.' + +'z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o' + +'.! ! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $' + +' $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( ' + +'_ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7' + +' 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g .%Y$Y$Y$V$P$J$+%i k k l l l ' + +'m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x' + +' a.y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I w+*@' + +'*@*@|@=@=@R+R+S+S+@%M M N N N N N O P P k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*' + +'+*+#%*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.$%d.$.$.$.$.%.%.&.&.V.~+~+~+{+]+' + +']+]+]+]+7+7+7+7+%%,.''.''.''.).).).!.!.~.~.r+&%K+ @.@.@.@@@*%]././.(.(._._.' + +'_.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"= = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q' + +'.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.' + +'B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K' + +'@K@t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.' + +'z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o' + +'.o.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ ' + +'$ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( (' + +' : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 ' + +'7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e k$Z$Y$Y$Y$Y$V$J$T$j j k k l l l' + +' m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x ' + +'x a.y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I :@*' + +'@*@*@|@=@=@R+R+S+S+w+M M N N N N N O P Q =%k+3+3+3+4+4+5+5+5+5+6+%+U+-%i$W ' + +'X X X X X ;%>%,%=+-+-+-+;+;+>+O.O.O.P.N.N.N.Q.d.$.$.$.$.%.%.&.&.$.''%~+~+{+' + +']+]+]+]+]+7+7+7+7+9+)%''.''.''.).).).!.!.w@j@K+K+K+ @.@.@.@{@]@]@L+*%!%(._.' + +'_._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"* = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q' + +'.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.B.B.B.' + +'B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t' + +'+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.' + +'z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o' + +'.o.! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % % $ $ ' + +'$ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _' + +' : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 ' + +'7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e ~%U$Y$Y$Y$Y$Y${%]%j j j k k l l l' + +' m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x ' + +'x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I %@*' + +'@*@*@|@=@=@R+R+S+S+S+^%M N N N N N O P Q f+k+3+3+3+4+4+5+5+5+5+6+%+/%V V W ' + +'X X X X X Y Z Z (%L.-+-+;+;+>+O.O.O.P.N.N.N.N._%$.$.$.$.%.%.&.&.&.:%<%~+{+]' + +'+]+]+]+]+7+7+7+7+8+a+''.''.''.).).).!.c+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@z@o@(' + ,'@_.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"* = = = = = - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q' + +'.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.' + +'B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t' + +'+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.' + +'z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o' + +'.o.! ! ! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ ' + +'$ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _' + +' : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 ' + +'7 8 9 9 9 9 9 0 0 a a a b b c c d e e e [%}%Y$Y$Y$Y$Y$I$1$j j j j k k l l l' + +' m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x ' + +'x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I Q' + +'+*@*@|@=@=@R+R+S+S+S+|%M N N N N N O P Q 1%k+3+3+4+4+4+5+5+5+5+6+%+/%V W W ' + +'X X X X X Y Z Z Z 2%3%-+;+;+O.O.O.O.P.N.N.N.N.4%$.$.$.$.%.%.&.&.&.*._%~+{+]' + +'+]+]+]+]+7+7+7+7+8+9+5%''.''.).).3@6%J+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@6@7@7@' + +'V@7%a@:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"* = = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.' + +'p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B' + +'.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z' + +'.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.' + +'o.! ! ! ! ! ! ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ ' + +'$ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( (' + +' _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 ' + +'7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e 8%}%}%Y$Y$Y$Y$9%h h j j j j k k l l' + +' l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v w w x x x x ' + +'x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G i.H H H H H I I' + +' 0%*@*@|@=@=@R+R+S+S+S+v+M N N N N N O P Q g@k+3+3+4+4+4+5+5+5+5+6+%+#+V W ' + +'W X X X X Y Y Z Z Z ` E+-+;+;+O.O.O.O.P.N.N.N.N.a%$.$.$.$.%.%.&.&.&.*.*.b%{' + +'+]+]+]+]+]+7+7+7+7+8+9+0+;.''.).5@m+J+J+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@6@7@7' + +'@7@m@B@:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13 + +#10'"* = = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p' + +'.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.' + +'B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t' + +'+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.' + +'z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o' + +'.! ! ! ! ! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ ' + +'$ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _' + +' : : : : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 ' + +'7 8 9 9 9 9 0 0 0 a a a b b c d d b c%}%}%}%Y$Y$U$d%g h i j j j j k k l l l' + +' m m m m n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x ' + +'x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I I u' + +'+*@*@|@=@=@R+R+S+S+S+w+M N N N N N O P Q }+k+3+3+4+4+4+5+5+5+5+6+%+#+V W W ' + +'X X X X Y Y Z Z Z ` ` -+;+;+O.O.O.O.N.N.N.N.N.e%$.$.$.$.%.%.&.&.&.*.*.%.u$]' + +'+]+]+]+]+7+7+7+7+8+9+0+_+,@f%m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@6@7@g%h%' + +'a@:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10 + +'"& = = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.' + +'q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B' + +'.B.B.B.B.B.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + +'t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z' + +'.z.z.y.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ' + +'! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ #' + +' # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : ' + +': : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8' + +' 9 9 9 9 0 0 0 a a b b c c d ~%i%}%}%}%}%Y$Z$k$g g h i j j j k k k l l l m ' + +'m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y' + +' y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J R+' + +'*@=@=@=@R+R+S+S+S+S+M N N N N N O P Q Q k+3+3+4+4+4+5+5+5+6+6+%+q$V W W X X' + +' X X Y Y Z Z Z ` ` L.;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.Z.]+]+' + +']+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@7@U@(@_.:.:' + +'.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"&' + +' * = = = = - - ; ; ; ; ; ; ; , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.' + +'q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.A.B.B' + +'.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+' + ,'t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z' + +'.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ' + +'! ! ! ) ) '' '' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # #' + +' @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : ' + +': < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9' + +' 9 9 9 0 0 0 a a b b c c j%k%}%}%}%}%}%.%g g g g h i j j j k k k l l l m m ' + +'m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y' + +' y y z z A B B B B B C C l%m%D E E E E E F G G G G G i.H H H H H I J J n%*@' + +'=@=@=@R+R+S+S+S+S+o%N N N N N O P Q Q f+3+3+4+4+4+5+5+5+6+6+%+%+V W r.X X X' + +' X Y Y Z Z Z ` ` F$;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.Z+]+]+' + +']+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@{@]@]@]@p%{#!%_._._.:.:.:' + +'.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"& *' + +' = = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q' + +'.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.' + +'B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.s+s+s+s+t+t+t+t+t+t+t+s+s+s' + +'+s+D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.' + +'y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! !' + +' ! ! ) '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # #' + +' @ @ @ @ @ n.+ + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : ' + +': < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9' + +' 9 9 9 0 0 a a a b b c q%r%}%}%}%}%}%s%g g g g g h i j j j k k k l l l m m ' + +'m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y' + +' y y z z A B B B B B t%u%)#.# #~#v%E E E F G G G G G i.H H H H I I J J u@*@' + +'=@=@=@R+R+S+S+S+S+w%N N N N N O P Q Q x%3+3+4+4+4+5+5+5+6+6+%+%+V W r.X X X' + +' X Y Y Z Z Z ` ` F$;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.-.^+]+' + +']+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@{@]@p%#@].(.(._._.:.:.:.:' + +'.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.",'#13#10'"& *' + +' = = = = = - - ; ; ; ; ; ; , , , , , '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.' + +'q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B' + +'.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.' + +'D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y' + +'.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ' + +'! ! ) '' '' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @' + +' @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : ' + +'< < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9' + +' 9 9 0 0 a a a b b y%z%r%}%}%}%A%B%f g g g g g h i j j j k k l l l m m m m ' + +'n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y' + +' y z z A B B B a#y#)#)#.#.#.#N@ #d@b@E F G G G G G H H H H H I I J J J *@=@' + +'=@=@R+R+S+S+S+S+g+N N N N N O P Q Q 1%3+3+4+4+4+5+5+5+6+%+%+%+-%W X X X X X' + +' Y Z Z Z Z ` ` F$;+>+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.-.5%]+]+' + +']+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@]@#@]././.(.(._._.:.:.:.l.<' + +'.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.",'#13#10'"& & =' + +' = = = = - - ; ; ; ; ; ; > , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.' + +'q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.B.B' + +'.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.' + +'D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.x.w' + +'.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ' + +') ) '' '' , , , , > ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @' + +' @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < ' + +'< [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9' + +' 9 0 0 a a a b c%z%z%r%}%}%i%C%e g g g g g g h i j j j k k l l l m m m m n ' + +'n o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y z' + +' z z A B a#D%:#)#)#)#.#.#.#N@N@N@N@E%L@G G G G G H H H H H I I J J J w+=@=@' + +'=@R+R+S+S+S+S+x+N N N N N O P Q Q z$3+3+4+4+4+5+5+5+6+%+%+%+-%W X X X X X Y' + +' Z Z Z Z ` ` F%;+>+O.O.O.O.N.N.N.N.N.e%$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.Z+]+]+' + +'7+7+7+8+9+0+0+0+0+m+m+m+J+K+K+K+K+K+ @.@.@.@G%H%/./././.(.(._._.:.:.:.l.<.[' + +'.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & * =' + +' = = = = - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.B.' + +'B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D' + +'.D.D.D.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.' + +'w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! )' + +' '' '' '' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @' + +' @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < ' + ,'0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9' + +' 9 0 0 a a j%i%I%z%z%r%}%c%b e e g g g g g g h i j j j k k l l l m m m m n ' + +'o o p p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z' + +' z z q#D%:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@!#m%J%G G H H H H H I I J J J u@=@=@' + +'=@R+R+S+S+S+S+x+K%N N N N P P Q Q O 3+3+4+4+5+5+5+5+6+%+%+%+H.W X X X X X Y' + +' Z Z Z Z ` ` r$;+>+O.O.O.P.N.N.N.N.N.S.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.8+7+' + +'7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+K+ @.@5@~.^././././.(.(.(._._.:.:.:.l.<.[' + +'.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & * =' + +' = = = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.' + +'q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.A.B' + +'.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.' + +'D.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w' + +'.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' + +''' '' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @' + +' @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.' + +'[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0' + +' 0 0 a j%k%I%I%z%r%r%8%e e e e g g g g g h h j j j j k k l l l m m m m n o ' + +'o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x a.y y y z z' + +' L%:#:#:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@r@r@6#M%J%H H H H H I I J J J v@=@=@=@' + +'R+S+S+S+S+S+h+1+N N N O P P Q Q R 4+3+4+4+5+5+5+5+6+%+%+%+/%W X X X X X Y Z' + +' Z Z Z ` ` M.;+>+O.O.O.P.N.N.N.N.N.V.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.H$7+7+' + +'7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+K+p%w@^.^.^././././.(.(._._._.:.:.:.l.<.[.[' + +'.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.",'#13#10'"& & * = =' + +' = = = - - ; ; ; ; ; ; > , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.' + +'q.s.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.A.B' + +'.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.C.C.C.C.C.' + +'C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w' + +'.w.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ''' + +' '' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @' + +' + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ ' + +'[ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0' + +' 0 j%r%I%I%z%z%r%N%e e e e e g g g g g h i j j j j k k l l l m m m m n o o ' + +'p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z O' + +'%:#:#:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@r@r@r@r@6#P%%@H H H I I J J J c.s@=@R+R+' + +'S+S+S+S+S+h+f+N N N O P P Q Q R 2+3+4+4+5+5+5+5+6+%+%+%+/%W X X X X X Y Z Z' + +' Z ` ` Q%;+;+>+O.O.O.P.N.N.N.N.N.G$$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-._+7+7+' + +'7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+R%].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[' + +'.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.1.4.4.4.4.4.5.5.5.5.5.",'#13#10'"% & & * = =' + +' = = - - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q' + +'.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.z.A.A.A.A.' + +'B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C' + +'.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.' + +'w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' ''' + +' '' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @' + +' + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ ' + +'[ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0' + +' S%T%U%I%I%z%z%V%d e e e e e g g g g g h i j j j k k k l l l m m m n n o o ' + +'p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A' + +' y#:#:#:#:#)#)#)#.#.#.#N@N@N@N@ #r@r@r@r@r@r@e@W%P%%@H I I J J J c.X%=@R+R+' + +'S+S+S+S+S+h+Y%N N N O P Q Q Q R ;@3+4+4+5+5+5+5+6+%+%+%+#+W X X X X X Y Z Z' + +' Z ` ` F$;+;+>+O.O.O.P.N.N.N.N.l+Z%$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.<+7+7+' + +'7+8+9+0+0+0+m+m+m+J+J+K+K+`%w@{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[' + +'.[.[.}.}.}.}.|.|.1.1.1.f.2.2.;#H@u#0#4.4.4.5.5.5.5.5.",'#13#10'"% & & * = =' + +' = = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.' + +'q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.A' + +'.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.' + +'C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w' + +'.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) '' '' ' + +', , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ g.+' + +' + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ ' + +'[ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 &T' + +'%U%U%I%I%z% &d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p ' + +'p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A O' + +'%:#:#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@W%P%+&I J J J c.+&=@R+R+S+' + ,'S+S+S+S+h+h+g@N N O P Q Q Q R 2@4+4+4+5+5+5+5+6+%+%+%+#+W X X X X X Y Z Z Z' + +' ` E+-+;+;+>+O.O.O.P.N.N.N.N.e%$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.b+7+7+7+' + +'8+9+0+0+0+m+m+m+J+J++@r+~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[' + +'.}.}.}.}.}.|.|.1.1.1.f.2.2.I@H@=#=#1#0#4.5.5.5.5.5.",'#13#10'"% & & * = = =' + +' = = - - ; ; ; ; ; ; > , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q' + +'.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.' + +'A.A.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C' + +'.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.' + +'w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) '' '' '' ' + +', , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ n.+' + +' + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ ' + +'[ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 @&#&U' + +'%U%U%I%z% &c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p ' + +'p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A' + +' $&:#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@e@e@[@%&J J J c.K &&R+R+S+' + +'S+S+S+S+h+h+1+N N O P Q Q Q R }+4+4+4+5+5+5+5+6+%+%+%+*&W X X X X Y Y Z Z =' + +'&-&-+-+;+;+O.O.O.O.P.N.N.N.N.;&$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-._+7+7+7+' + +'8+9+0+0+0+m+m+m+J+>&w@~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[' + +'.}.}.}.}.}.|.|.1.1.1.2.2.,&H@H@=#=#=#=#''&p#5.5.5.5.",'#13#10'"% % & & * = ' + +'= = = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.' + +'A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B' + +'.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.' + +'w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) '' '' , ,' + +' , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + ' + +'+ + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [' + +' [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 )&#&!&U%' + +'U%U%z%S%c c d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p' + +' p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B ' + +'y :#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@e@e@}@*@R+u@J K K R@R+R+S+S' + +'+S+S+S+h+h+f+N N O P Q Q Q R R 4+4+4+5+5+5+6+6+%+%+%+&+W X X V h$i$~&B+K.=+' + +'-+-+-+;+;+O.O.O.O.N.N.N.N.R.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.~@7+7+7+8' + +'+9+0+0+0+m+m+m+n+!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.' + +'}.}.}.}.}.|.|.1.1.1.2.2.I@H@H@=#=#=#=#8#n#{&p#5.5.",'#13#10'"% % & & * = = ' + +'= = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q' + +'.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.' + +'A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B' + +'.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.' + +'u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , ,' + +' , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + ' + +'+ + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < [ [ [ [ [ [' + +' } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 ]&!&!&!&U%U%' + +'T%^&b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n o o o p p p' + +' q q q q q r s s s s t t t u u u v v v v w w q#g#%$x x x y y y y z z A B B ' + +'$&:#)#)#)#.#.#.#N@N@N@N@d@6#r@r@r@r@r@e@e@e@e@e@}@}@*@*@*@/&+&K S@R+R+S+S+S' + +'+S+x+h+h+k+N N O P Q Q Q R R (&4+4+5+5+5+6+6+%+%+%+&+_&*+*+*+*+*+*+=+=+=+-+' + +'-+-+;+;+O.O.O.O.N.N.N.N.t$d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.K$7+7+7+8+9' + +'+0+0+0+m+m+m+>&!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.' + +'}.}.}.}.|.|.1.1.1.2.|.:&H@H@=#=#=#=#8#n#n#n#{&<&",'#13#10'"% % & & * = = = ' + +'= = - - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.' + +'q.s.t.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A' + +'.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.' + +'B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.v.u' + +'.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , ,' + +' , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + ' + +'+ + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ }' + +' } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 ]&[&!&!&!&U%T%' + +'B%b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h' + +'.q q q q q r s s s s t t t u u u v v v v w r#L#x#x#3#x a.y y y z z z A B B ' + +'y :#)#)#)#.#.#.#N@N@N@N@}&F m%u%r@r@r@e@e@e@e@e@}@}@*@*@*@|@=@R@|&Q+R+S+S+S' + +'+S+x+h+h+h+z$N O P Q Q Q R R C$4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+-+' + +'-+-+;+;+O.O.O.O.N.N.N.$%#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.[+7+7+7+7+9+9' + +'+0+0+0+m+m+m+1&!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.' + +'}.}.}.}.|.|.1.1.1.2.e#X@H@H@=#=#=#=#8#n#n#n#n#n#",'#13#10'"% % % & & * = = ' + ,'= = = - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q' + +'.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.' + +'z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B' + +'.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.' + +'u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , ,' + +' ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + ' + +'+ . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } }' + +' | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 8 8 9 )&2&[&!&!&U%r%j%b ' + +'b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q' + +' q q q q r s s s s t t t u u v v v v T#3&x#x#x#x#4&x y y y y z z A A B B B ' + +'5&)#)#)#.#.#.#N@N@N@N@6&J%G G }#d@r@e@e@e@e@e@}@}@*@*@*@|@=@=@+#/&R+S+S+S+S' + +'+x+h+h+h+7&N O P Q Q Q R R 8&4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+' + +'-+;+>+O.O.O.O.N.N.$%#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.H$7+7+7+7+9+9+0' + +'+0+0+m+m+m+9&!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.' + +'}.}.}.|.1.1.1.1.|.:&X@H@H@=#=#=#=#8#n#n#n#n#n#",'#13#10'"% % % & & * = = = ' + +'= = - - ; ; ; ; ; ; ; , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.' + +'q.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z' + +'.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.' + +'B.B.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u' + +'.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , , , ;' + +' ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + ' + +'+ . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } }' + +' | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 )&2&[&[&!&!&k%j%a b ' + +'b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q' + +' q q q q r s s s s t t u u u v v v I#G#G#x#x#x#x#x#A#y y y y z z A A B B B ' + +'t%''#)#)#.#.#.#N@N@N@N@O@q@G G G G }#+#e@e@e@e@}@}@*@*@*@|@=@=@=@R+S+S+S+S+' + +'S+h+h+h+h+f+O P P Q Q R R R >@4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-' + +'+;+;+>+O.O.O.O.l+$%#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.Z+7+7+7+8+9+9+' + +'0+0+0+m+m+0&!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}' + +'.}.}.}.|.1.1.1.1.e#X@X@H@H@=#=#=#=#8#n#n#n#n#n#",'#13#10'"% % % & & & = = =' + +' = = - - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.' + +'q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z' + +'.z.z.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.' + +'A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u' + +'.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ' + +'; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + +' + +' . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | ' + +'| | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 a&2&[&[&[&!&z%j%a a b b' + +' c c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q ' + +'q q q r r s s s s t t u u u v T#b&K#G#G#x#x#x#x#x#L#Z@y y y z z A A B B B B' + +' i#)#)#.#.#.#N@N@N@N@ #!#G G G G G H ##+#e@e@}@}@*@*@*@|@=@=@R+R+S+S+S+S+S+' + +'h+h+h+h+k+O P P Q Q R R R `.4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;' + +'+;+>+O.O.O.r$..#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.c&7+7+7+8+9+0+0+' + +'0+0+m+m+j@!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}' + +'.}.}.|.1.1.1.;#:&X@H@H@H@=#=#=#=#n#n#n#n#n#n#",'#13#10'"% % % % & & * = = =' + +' = = - - ; ; ; ; ; ; > , , , , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q' + +'.q.q.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.' + +'z.z.z.z.z.z.A.A.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A' + +'.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.' + +'u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) '' '' '' , , , , > ; ' + +'; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + +' + +' ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | ' + +'| | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 d&2&[&[&[&!&z%e&a a a b b' + +' c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q ' + +'q q q r s s s s s t t u u u I#K#K#K#G#G#x#x#x#x#x#x#b#y y y z z A A B B B B' + +' l%''#)#.#.#N@N@N@N@N@ #r@m%G G G i.H H H ##+#}@}@*@*@*@|@=@=@R+R+S+S+S+S+S' + +'+h+h+h+h+h+f&P Q Q Q R R R S 4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+' + +';+;+>+O.-&Q%@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.g&7+7+7+8+9+0+0' + +'+0+0+m+m+h&!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.' + +'}.}.}.|.1.1.1.-#X@X@H@H@=#=#=#=#=#n#n#n#n#n#n#",'#13#10'"$ % % % & & * = = ' + +'= = = - - ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q' + +'.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.B.B.B.B.B.A.A.A.A.A.A.A.A.A.A.A.A' + +'.A.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.' + ,'u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ;' + +' ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | |' + +' | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 d&i&[&[&[&[&z%e&0 a a b b c ' + +'c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q' + +' q q r s s s s t t t u j&($K#K#K#K#G#G#x#x#x#x#x#x#h#k&y y z z A B B B B B ' + +'C i#)#.#.#N@N@N@N@N@ #r@d@G G G i.H H H H H l&Q+*@*@*@|@=@=@R+R+S+S+S+S+S+h' + +'+h+h+h+h+2@P Q Q Q R R S S C$5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+M.' + +'r$s$` +.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.7+7+7+7+8+9+0+0+0' + +'+0+m+m+!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.' + +'}.}.|.1.1.;#m&X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#",'#13#10'"$ % % % % & & * = = ' + +'= = = - ; ; ; ; ; ; ; > , , , , '' '' '' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.' + +'q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z' + +'.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u' + +'.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) '' '' '' , , , , > ; ; ;' + +' ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | |' + +' | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 d&i&n&[&[&[&o&e&0 a a a b b c ' + +'c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q' + +' q q r s s s s t t t ]$V#W#K#K#K#K#G#x#x#x#x#x#x#x#h#p&y z z z A B B B B C ' + +'C l% #.#.#N@N@N@N@N@ #r@r@v%G G i.H H H H H I J %&R+*@=@=@=@R+R+S+S+S+S+S+h' + +'+h+h+h+h+B$P Q Q Q R R S S R$5+5+5+5+6+%+%+%+&+&+*+*+*+#%A+A+B+B+B+F+(%` .' + +' .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.H$7+7+7+7+8+9+0+0+0' + +'+m+m+0&!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.' + +'}.|.|.1.1.-#]#X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#",'#13#10'"$ $ % % % & & * = = ' + +'= = = - - ; ; ; ; ; ; ; , , , , , '' '' '' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.' + +'q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.z.z.z' + +'.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.' + +'z.z.z.z.z.z.z.z.z.z.z.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t' + +'.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ;' + +' ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | |' + +' | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 5 q&n&[&[&[&o&6 0 0 a a a b b c ' + +'c d e e e e e e g g g g g h i j j j k k k l l l m m m m n o o p p p q q q q' + +' q q r s s s s t t t t ($K#K#K#K#K#G#x#x#x#x#x#x#r&h#h#s&z z z A B B B B C ' + +'C C C@.#.#N@N@N@N@O@ #r@r@Q@G G H H H H H I I J J t&0%+#=@=@R+R+S+S+S+S+x+h' + +'+h+h+h+h+=%P Q Q Q R R S S 8&5+5+5+6+T+#+/%/%F.V+;%X X X Y Y Z Z Z ` ` ` .' + +' .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.w$7+7+7+7+8+9+0+0+0' + +'+m+m+u&!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.' + +'}.|.|.1.;#m#]#X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#",'#13#10'"$ $ % % % & & & * = ' + +'= = = = - ; ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q' + +'.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.' + +'z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z' + +'.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.' + +'t.t.s.q.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ;' + +' ; ; - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | |' + +' 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 5 v&w&n&[&[&o&x&0 0 0 a a a b b c c ' + +'d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q' + +' r r s s s s t t u u j&K#K#K#K#G#G#x#x#x#x#x#x#h#h#h#:#O%z A A B B B B C C ' + +'C }# #.#N@N@N@N@O@r@r@r@r@E G H H H H H I I J J J c.I P+=@R+R+S+S+S+S+x+h+h' + +'+h+h+h+i+`.Q Q Q R R S S P y&`.F.U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .' + +'+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.w$0+7+7+7+8+9+0+0+0+m' + +'+m+3@!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.' + +'|.|.1.z&]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#",'#13#10'"$ $ % % % % & & * = = ' + +'= = = - - ; ; ; ; ; ; ; , , , , , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.' + +'q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y' + +'.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.' + +'z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t' + +'.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ; ; ;' + +' ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | |' + +' 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 x&A&w&w&n&[&o&x&9 0 0 0 a a a b b c d ' + ,'d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q' + +' r r s s s s t t u u u I#K#K#K#G#G#x#x#x#x#x#x#h#h#h#h#r#z A A B B B B C C ' + +'C D C@.#N@N@N@N@O@r@r@r@r@M%G H H H H H I I J J J c.K K %@/&R+S+S+S+S+x+h+h' + +'+h+h+h+j+ %Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .' + +'+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.,@<+_+a+9+0+0+0+m' + +'+m+).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.' + +'|.|.1.m#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#",'#13#10'"$ $ $ % % % & & & = = ' + +'= = = = - ; ; ; ; ; ; ; ; , , , , , '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p' + +'.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.' + +'y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z' + +'.z.z.z.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.' + +'s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' , , , , , ; ; ; ; ; ; ; ;' + +' - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ' + +' ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | 1 1' + +' 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 B&w&w&n&n&2&a&9 9 0 0 0 a a b b c c d d ' + +'e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r' + +' s s s s s t t u u u v C&K#K#G#G#x#x#x#x#x#x#h#h#h#h#d#q#A A B B B B C C C ' + +'D }# #N@N@N@N@ #r@r@r@r@6#D&H H H H H I I J J J c.K K L c.u@S+S+S+S+h+h+h+h' + +'+h+=%R$>@Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.' + +'+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.,@5%_+f%!' + +'@).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.' + +'|.E&]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#",'#13#10'"$ $ $ % % % % & & * = = ' + +'= = = - - ; ; ; ; ; ; ; , , , , , '' '' '' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.' + +'p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x' + +'.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.' + +'y.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q' + +'.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' '' , , , , , ; ; ; ; ; ; ; -' + +' - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ' + +' ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1' + +' 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 F&w&w&w&n&[&]&9 9 9 0 0 a a a b b c c d e ' + +'e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r' + +' s s s s t t t u u u v &$O#K#G#G#x#x#x#x#x#x#h#h#h#h#d#g#A B B B B B C C C ' + +'D D M%N@N@N@N@ #r@r@r@r@r@b@H H H H H I I J J J c.K K L L M v@w+S+S+h+k+f+|' + +'+}+O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.' + +'+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''' + +'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.' + +'}.|.1.H@]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#",'#13#10'"$ $ $ $ % % % & & & ' + +'= = = = = = - ; ; ; ; ; ; ; ; , , , , , '' '' '' ) ! ! ! ! ! ! ! o.o.o.p.p.' + +'p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.x.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.' + +'y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s' + +'.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ; ; ; ;' + +' ; - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ' + +' ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | |' + +' 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 G&H&w&w&w&n&I&9 9 9 0 0 0 a a a b b c c ' + +'d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q' + +' q r s s s s t t t u u v v v E#K#G#x#x#x#x#x#x#x#h#h#h#d#d#d#3#B B B B C C ' + +'C D D D E r@N@N@N@ #r@r@r@r@r@J&H H H H H I J J J J K K K L L M M M K&K&g@N' + +' N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X i$j$j$j$(%(%` . .' + +'..+.+.+.@.#.;&I+L&L&M&M&$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.' + +'''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}' + +'.}.}.}.|.k#m#]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#",'#13#10'"$ $ $ $ % % % %' + +' & & * = = = = = - - ; ; ; ; ; ; ; > , , , , , '' '' ) ) ! ! ! ! ! ! ! o.o.' + +'o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.x.x.y.y.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.' + +'y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s' + +'.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ) '' '' , , , , , > ; ; ; ; ; ' + +'; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + . ' + +' ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | ' + +'| 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 N&H&H&w&w&w&O&9 9 9 9 0 0 0 a a a b b c' + +' d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q ' + +'q q r r s s s s t t u u u v v v v B#G#x#x#x#x#x#x#h#h#h#h#d#d#d#P&t%B B B C' + +' C C D D D E M%N@N@O@ #r@r@r@r@r@e@##H H H I I J J J J K K K L L M M M M j.' + +'N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X E.*+=+Q&K.3%-+L. ' + ,'. ...+.+.+.@.#.R.l+N.l+,+,+,+M&%.%.&.&.&.*.%.*.=.=.-.-.-.-.;.;.;.;.>.,.''.' + +'''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.' + +'}.}.}.}.}.|.1.1.-#m#X@X@H@H@=#=#=#=#=#n#n#n#n#n#n#R&",'#13#10'"$ $ $ $ $ % ' + +'% % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , '' '' '' ) ) ! ! ! ! ! ! ' + +'o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.' + +'x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t' + +'.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) '' '' '' , , , , , ; ; ; ;' + +' ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ' + +'. ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } |' + +' | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 S&H&H&w&w&w&T&8 9 9 9 9 0 0 0 a a b ' + +'b c c d d e e e e e g g g g g g h j j j j k k l l l m m m m n n o o p p p q' + +' q q q q r r s s s s t t u u u v v v v `#U&x#x#x#x#x#x#h#h#h#h#d#d#:#:#5&B ' + +'B B C C C D D D E E r@N@O@r@r@r@r@r@r@e@+#H H H I I J J J c.K K K L L M M M' + +' M j.N N N N N O P Q Q Q R R S S S T o$V&T+T+I.V V W W X X X X i$*+j$Z Z ` ' + +'F$-+$% .+.+.+.+.@.#.R.R.#.d.$.$.M&Z%%.%.&.&.&.*.w$~+H$-.-.[+H$-.;.;.;.;.>.,' + +'.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[' + +'.[.}.}.}.}.}.|.1.1.1.0#W&:&H@H@=#=#=#=#=#n#n#n#n#n#M#R&",'#13#10'"$ $ $ $ $' + +' % % % % & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , '' '' '' ) ! ! ! ! !' + +' ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.x.x.x.y.y.y.y.y.y.y.x.x.x.x.x.x' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.' + +'t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) '' '' '' , , , , , ; ; ; ' + +'; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + +' + +' . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } ' + +'} | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 X&Y&H&H&w&w&B&8 9 9 9 9 9 0 0 a a a' + +' b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p ' + +'p q q q q q r s s s s s t t u u u v v v v w Z&x#x#x#x#x#x#h#h#h#h#d#d#:#:#:' + +'#Z@B B C C C D D D E E M%N@ #r@r@r@r@r@r@e@e@_@H H I I J J J c.K K K L L M ' + +'M M M j.N N N N N O P Q Q Q R R S S S y&5+*&p$U+%+#%V W X X X X X i$*+j$Z Z' + +' ` G+-+`& .+.+.+.+.#.#.R.R.#.d.$.$.$.$.%.%.&.&.&.*.:%~+Z.-.-.`+]+Z+;.;.;.,@' + +'>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.' + +'[.[.[.}.}.}.}.|.|.1.1.1.f.2.|.-#H@=#=#=#=#=#n#n#n#n#n#M#R&",'#13#10'"# $ $ ' + +'$ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , , '' '' ) ) ! ! !' + +' ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.' + +'t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' , , , , , , ; ; ;' + +' ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + ' + +'+ . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ }' + +' } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 *.*H&H&H&w&A&6 8 9 9 9 9 0 0 0 a a ' + +'a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p p p' + +' q q q q q q r s s s s t t t u u u v v v v w w r#x#x#x#x#x#h#h#h#h#d#d#:#:#' + +':#D%B B C C C D D E E E E r@ #r@r@r@r@r@.&e@e@<@H H I I J J J c.K K L L M M' + +' M M M N N N N N O P P Q Q R R R S S `.5+ +T U V H./%V W X X X X X ;%=+,%K.' + +'K.O.-+F$ . .+.+.+.+.#.#.R.N.R.e%S.+*G$$.%.%.&.&.*.*.*.~+%%-.[+]+8+/+;.;.,@7' + +'+Y+,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[' + +'.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.;#u#=#=#=#=#n#n#n#n#n#M#R&",'#13#10'"# $ $' + +' $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , '' '' '' ) ) !' + +' ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.' + +'u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.' + +'t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , , , , , ; ; ' + +'; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + +' + +' + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ ' + +'[ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 v&@*H&H&H&2&5 7 8 9 9 9 9 0 0 0 a' + +' a a b b c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p ' + +'p p q q q q q q r s s s s t t t u u v v v v v w x x 4&x#x#x#x#h#h#h#d#d#d#:' + +'#:#:#:#H#C C C C D D E E E E M% #r@r@r@r@r@.&e@e@W%%@H I I J J J c.K K L L ' + +'M M M M M N N N N N O P Q Q Q R R R S S y&5+T T U V V V V W X X X X X Y =+K' + +'.B+,%-+F$ . ...+.+.+.+.#.#.R.R.$%#*V.V.V.$.%.%.&.&.*.*.*.<%$*-.`+8+Z+/+;.;.' + +'~@K$)%,.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.' + +'[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.0#1#=#8#n#n#n#n#n#M#R&",'#13#10'"# ' + +'# $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , '' '' '' ' + ,') ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u' + +'.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t' + +'.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , , , , , ; ;' + +' ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + ' + +'+ + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [' + +' [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 %*@*@*H&H&.*d&7 8 8 9 9 9 9 0 0 ' + +'a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o' + +' p p p q q q q q r r s s s s t t u u u v v v v w w x x `#L#x#x#h#h#h#h#d#d#' + +'d#:#:#:#:#s#l%C C D D D E E E E E 6#r@r@r@r@r@.&e@e@e@:@H I J J J J K K K L' + +' L M M M M M N N N N N O P Q Q Q R R S S S y&5+T T U V V V V W X X X X X Y ' + +'=+B+Z ` G+-+F% ...+.+.+.@.#.#.R.I+#.$.$.$.$.%.%.k.&.&.*.*.*.&*$*[+]+w$Y+]+;' + +'.<+7+:%,.,.''.''.''.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<' + +'.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.**=*n#n#n#n#n#M#R&",'#13#10 + +'"# # # $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , '' ' + +''' '' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t' + +'.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) '' '' '' , , , , , >' + +' ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ # # # @ @ @ @ @ @ + + ' + +'+ + + + + ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [' + +' [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 3 4 5 5 -*@*@*H&H&H&;*7 7 8 9 9 9 9 9 ' + +'0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o' + +' o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x A#x#x#h#h#h#h#' + +'d#d#:#:#:#:#:#:#>*C C D D D E E E E b.m%r@r@r@r@r@e@e@<@_@H I I J J J J K K' + +' K L L M M |%^%j.N N N N N O P Q Q Q R R S S S +5+F.T U V V #%,*W X X X X ' + +'X Y Q&B+Z ` ` -&;+''*..+.+.+.@.#.#.Q.I+#.$.$.$.$.%.%.&.&.&.*.*.*.)*)*`+/+-.' + +'H$7+;.~@g&>.,.''.''.''.''.).).).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:' + +'.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.v#f#n#n#n#M#R&",' + +#13#10'"@ # # $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , ' + +', , '' '' '' ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.' + +'t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) '' '' '' , , , , ' + +', , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n' + +'.+ + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < ' + +'[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 !*~*@*@*H&H&G&7 7 7 8 9 9 9' + +' 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m ' + +'m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x B#x#h#h' + +'#h#h#d#d#:#:#:#:#:#:#)#{*C D D D E E E E b.G d@r@r@r@6#P%%@H H H I I J J J ' + +'c.K K K L t&v+]*S+|%j.N N N N N O P Q Q Q R R S S S V+5+V&U U V -%%+o$W X X' + +' X X Y Y K.,%Z ` ` 2%P.>+^*+.+.+.@.#.#.N.N.l+,+,+!+S.%.%.&.&.&.*.*.*.$*u$]+' + +'H$-.H$7+<+7+<+>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._' + +'.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.Q#M#n#M#R&' + +'",'#13#10'"@ @ # # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , ' + +', , , , , '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t' + +'.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t' + +'.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' , , , , ' + +', , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ @' + +' + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < ' + +'0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 *w&@*@*@*H&/*7 7 7 8 8 9 9' + +' 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m ' + +'m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w x x x x x q#d#h' + +'#h#h#h#d#d#:#:#:#:#:#:#)#6#C D D E E E E E F G }#r@6#M%J%H H H H H I I J J ' + +'J c.K |&X%/&S+/&K&c.M j.N N N N N O P Q Q Q R R S S S T y&5+@+G./%$+#+W r.X' + +' X X X Y Y (%(%Z ` ` ` . .+.+.+.+.#.#.#.#.#.t$M&M&W.G$%.%.&.&.&.*.*.*.(*]+' + +'/+-.-.[+7+~@~@;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._.' + +'_.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.5._*{&R&R' + +'&",'#13#10'"@ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ;' + +' , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s' + +'.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.' + +'w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t' + +'.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , ,' + ,' , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ ' + +'@ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : <' + +' < [ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 3 3 4 5 q&@*@*@*H&2&x&7 7 7 8 9 ' + +'9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m' + +' m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x ' + +':*h#h#h#d#d#d#:#:#:#:#:#)#)#)#k&D D E E E E E F G G m%J%G i.H H H H H I I J' + +' J J c.K +#Q+<*f@]*M M M N N N N N O P P Q Q R R R S S T T T >@T+#+#+H.V W ' + +'X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.k' + +'./+H$-.-.-.7+7+,@;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._' + +'._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.' + +'[*}*",'#13#10'"@ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ' + +'; ; > , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.' + +'q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.w.w.w.w.w' + +'.w.w.w.w.w.w.w.w.w.w.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.' + +'t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , , ' + +', , , > ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @' + +' @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( : : : : ' + +'< < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 |*@*@*@*@*1*a&6 7 7 7 8' + +' 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l ' + +'m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x' + +' x y 5&h#h#d#d#:#:#:#:#:#:#)#)#)# #}#D E E E E b.F G G G G G i.H H H H H I ' + +'J J J J K K +&L L u+S+|%M M N N N N N O P Q Q Q R R R S S T T T T U V V V V' + +' W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.' + +'*.=.-.-.-.-.-.c&_+;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.' + +'(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5' + +'.6.7.2*",'#13#10'"@ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = - - ; ; ; ;' + +' ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q' + +'.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.' + +'v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t' + +'.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , ,' + +' , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ ' + +'@ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : :' + +' : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 3*@*@*@*@*~*X&6 7 7 7 ' + +'8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l' + +' l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x ' + +'x x x y y 4&h#d#d#:#:#:#:#:#:#)#)#)#.#,#D E E E E b.G G G G G G H H H H H I' + +' I J J J J K K K L L M /&@%M M N N N N N O P Q Q Q R R S S S T T T T U V V ' + +'V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*' + +'.*.*.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././' + +'.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.' + +'5.5.6.7.7.",'#13#10'"@ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ' + +'; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.' + +'p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.' + +'t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' , ' + +', , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @' + +' @ @ @ g.+ + + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : ' + +': : < < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 4*Y&@*@*@*@*N&6 6 7 7' + +' 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k l ' + +'l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x' + +' x x x x y y k&:#d#d#:#:#:#:#:#:#)#)#)#.#.#m%E E E E F G G G G G G H H H H ' + +'_@w++&J J J c.K K K L L M @%/&M j.N N N N N O P Q Q Q R R S S S T T T T U V' + +' V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.' + +'*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.].^.^.^.^.e././' + +'././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.' + +'5.5.5.5.6.7.7.",'#13#10'"@ @ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = ' + +'- - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.' + +'p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.' + +'t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' '' ' + +', , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @' + +' @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ ' + +': : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 q&@*@*@*@*v&6 6 6' + +' 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k ' + ,'k l l l m m m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x' + +' x x x x x x y y y C#d#d#:#:#:#:#:#:#)#)#)#.#.#W%E E E E F G G G G G i.H H ' + +'H H ##}@R@J J J c.K K K L M M v@S+u+j.N N N N N O P Q Q Q R R S S S T T T U' + +' U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.' + +'&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e' + +'././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.' + +'m.5.5.5.5.5.6.7.8.",'#13#10'"@ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = ' + +'= = = - - ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! o.o.o.o.' + +'o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u' + +'.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.' + +'t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) '' '' ' + +''' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # ' + +'# @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( (' + +' ( _ : : : : < < [ [ [ [ [ [ } | | | | | 1 1 1 2 2 3 3 3 3 3 5*@*@*@*@*w& *' + +'6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e g g g g g g h i j j j' + +' k k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v ' + +'w w x x x x x x y y y y z %$d#:#:#:#:#:#)#)#)#.#.#.#~#E E E E F G G G G G i' + +'.H H H H H &&=@J J J c.K K L L M M M ]*<*N N N N N O P P Q Q R R R S S T T ' + +'T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%' + +'.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.' + +'^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4' + +'.4.4.m.5.5.5.5.5.6.7.8.",'#13#10'"@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & * *' + +' = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! ! ! ! !' + +' o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t' + +'.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) '' ' + +''' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = * * & & % % % % $ $ $ $ $' + +' # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ' + +'( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 6*7*@*@*@*~' + +'*!*6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i ' + +'j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u u v v' + +' v v w w x x x x x x y y y y z O%P&:#:#:#:#:#)#)#)#.#D@j#E E E E b.F G G G ' + +'G G H H H H H H X%*@%&J J K K K L L M M M K&w+N N N N N O P Q Q Q R R R S S' + +' T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.' + +'$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].' + +'^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3' + +'.4.4.4.4.m.5.5.5.5.5.7.7.8.",'#13#10'"@ @ @ @ @ @ # # # $ $ $ $ $ % % % % &' + +' & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ! ! ! ! ! !' + +' ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.u.u.' + +'u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t' + +'.t.s.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ''' + +' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ ' + +'$ # # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / (' + +' ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 1 8*@*@*@*' + +'@*9*5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i' + +' j j j j k k l l l m m m m n n o !$p p p q q q q q q r s s s s t t t u u u ' + +'v v v v w w x x x x x x y y y y z z q#:#:#:#:#:#)#)#''#C@D D E E E E b.G G ' + +'G G G G H H H H H I I R+R+J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S' + +' S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.' + +'$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.' + +'{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3' + +'.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.",'#13#10'"g.@ @ @ @ @ @ # # $ $ $ $ $ % % %' + +' % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' ) ) ) ! !' + +' ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.' + +'t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.t.t' + +'.t.s.s.s.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ) ' + +''' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ ' + +'$ $ $ # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / /' + +' ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 0*7*@*' + +'@*@*v&5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e f g g g g g' + +' h i j j j k k k l l l m m m m n o [$3$g$p p q q q q q r r s s s s t t u u ' + +'u v v v v v w x x x x x x x y y y z z z A 5&:#:#:#:#)#y#l%D D E E E E E F G' + +' G v%m%v%G H H H H H I I %&*@%&c.K K K L L M M M M j.N N N N N O P Q Q Q R ' + +'R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#' + ,'.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~' + +'.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.' + +'2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.",'#13#10'"+ @ @ @ @ @ @ @ # # $ $ $ $ $ ' + +'% % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' '' '' '' )' + +' ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.s.s.s.s.' + +'t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s' + +'.s.s.s.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' + +''' '' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $' + +' $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ ' + +'/ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 a*7' + +'*@*@*@*w& *5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g ' + +'g g g h i j j j k k k l l l m m m m n b*3$<$<$b$h.q q q q q r s s s s s t t' + +' u u u v v v v w w x x x x x x y y y y z z z A t%g#:#:#''#c*C D D D E E E E' + +' E F }#d@r@r@r@d*H H H H H I I J R+/&c.K K L L M M M M M N N N N N N O P Q ' + +'Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#' + +'.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.' + +'!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1' + +'.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.",'#13#10'"+ n.@ @ @ @ @ @ # # # $' + +' $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , '' ' + +''' '' '' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.' + +'q.q.q.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s' + +'.s.s.s.s.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ' + +'! ) ) '' '' '' '' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % %' + +' % % $ $ $ $ $ # # # @ @ @ @ @ @ n.+ + + + + + + ~ ~ ~ { { ] ] ' + +'^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3' + +' 3 e*f*7*@*@*~*!*5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e ' + +'e g g g g g h h j j j j k k l l l m m m m n b*3$3$<$<$<$!$q q q q q r s s s' + +' s t t t u u u v v v v w w x x x x x x y y y y z z A A B Z@:#D%t%C C D D D ' + +'E E E E ##}&r@d@}#J%!#<@H H H H H I J J %&*@u+K K L L M M M M M N N N N N O' + +' P P Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.' + +'+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.)' + +'.).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.' + +'|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.",'#13#10'"+ + @ @ @ @ @ @ ' + +'@ # # $ $ $ $ $ $ % % % % & & * * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , ,' + +' , , , '' '' '' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q' + +'.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.' + +'s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! !' + +' ! ! ) ) '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * * & & %' + +' % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ' + +'] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2' + +' 3 3 3 0*7*7*@*@*|*5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e ' + +'e e g g g g g g h i j j j j k k l l l m m m m b*3$3$3$<$<$<$>$g*q q q r r s' + +' s s s t t t u u v v v v v w x x x x x x x y y y y z z A A B B Z@B C C C D ' + +'D D E E E E h*r@b@G G G u%+#H H H H I I J J J O+|&K K L L M M M M M N N N N' + +' N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.' + +'+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.' + +').).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}' + +'.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.",'#13#10'"+ + + @ @ @ @' + +' @ @ @ # # $ $ $ $ $ $ % % % % & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , ' + +', , , , , , '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.' + +'p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.q' + +'.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ' + +'! ! ) ) ) '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & ' + +'% % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { {' + +' ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 ' + +'2 2 3 3 %*i*7*@*@*.*5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d e e' + +' e e e g g g g g g h i j j j k k k l l l m m m b*6$3$3$<$<$<$<$>$,$*$q q r ' + +'s s s s s t t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C' + +' D D E E E E E M%r@q@G G Q@r@P%H H H H I I J J J c.K K K L L M M M M j.N N ' + +'N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ..' + +'.+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.' + +'''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.' + +'}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.",'#13#10'"+ + + n.@ ' + +'@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ;' + ,' ; , , , , , , , '' '' '' '' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.p.p.p.p.' + +'p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q' + +'.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ! ' + +'! ! ! ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * &' + +' & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ ' + +'{ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1' + +' 2 2 2 3 3 e*f*7*7*@*~*;*5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d ' + +'e e e e e e g g g g g h i j j j j k k l l l m m m b*6$6$3$3$<$<$<$>$>$>$,$*' + +'$q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C ' + +'C C D D D E E E E E F u%6#b@u%6#j*H H H H H I I J J J c.K K L L M M M M M j' + +'.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` ' + +' . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.' + +'''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.' + +'[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.",'#13#10'"+ + ' + +'+ + g.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ;' + +' ; ; ; ; ; , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.' + +'o.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q' + +'.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ' + +'! ! ! ! ) ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = =' + +' * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ g.+ + + + + + + . ' + +'~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | |' + +' 1 1 1 2 2 2 3 3 k*i*7*@*@*9*5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c ' + +'c d d e e e e e g g g g g g h i j j j j k k l l l m m 4$5$6$6$3$3$<$<$<$>$>' + +'$>$>$,$_$r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B ' + +'B B C C C D D D E E E E b.F v%r@r@M@E i.H H H H H I J J J J c.K K L L M M M' + +' M M N N N N N O P P Q Q R R R S S S T T T U V V V V W r.X X X X Y Y Z Z Z ' + +'` ` ` .`&Q%Q%Q%Q%`&l*l*l*l*4%v$v$v$G$(*(*Z%&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>' + +'.,.,.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[' + +'.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.",'#13#10 + +'"+ + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ' + +'; ; ; ; ; ; ; ; ; > , , , , , , , '' '' '' '' ) ) ! ! ! ! ! ! ! ! ! ! ! o.o' + +'.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.' + +'q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.! ! ! ! ! !' + +' ! ! ! ! ! ) ) '' '' '' '' , , , , , , , > ; ; ; ; ; ; ; ; ; - - = = = = = ' + +'= = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ' + +' ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | ' + +'| | 1 1 1 2 2 2 3 3 m*i*i*7*@*q&5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b' + +' b c c d e e e e e e g g g g g g h i j j j k k k l l l m 7$5$5$6$3$3$<$<$<$' + +'<$>$>$>$>$>$n*o*s s s s t t u u u v v v v v w x x x x x x x y y y y z z A B' + +' B B B B C C C D D D E E E E b.G G M%r@}#G H H H H H I I J J J J K K K L L ' + +'M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X i$i$D' + +'+p*J.q*L.3%;+;+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+''+!+!+!+~+U.U.)*X+%%H$-.-.;.' + +';.;.;.>.,.''.''.''.''.).).).!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:' + +'.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.",' + +#13#10'"+ + + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = ' + +'= = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , '' '' '' '' ) ) ) ! ! ! ! ! ! ! !' + +' ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.' + +'q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! !' + +' ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = ' + +'= = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + + . ' + +' ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } ' + +'| | | | | 1 1 1 2 2 2 3 r*i*i*7*@*~* *5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a' + +' a b b b c d d e e e e e e g g g g g h i j j j j k k l l l m g$5$5$6$6$3$3$' + +'<$<$<$>$>$>$>$>$>$,$.$s*s s t t t u u u v v v v w w x x x x x x y y y y z z' + +' z A B B B B C C C C D D E E E E E F G G J%!#!#G H H H H H I I J J J c.K K ' + +'K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V F.G.I.#%#+*' + +'+*+=+=+=+=+-+-+-+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+''+!+!+!+~+~+~+{+]+]+]+' + +']+8+~@(+_+:+t*,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:' + +'.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.' + +'8.8.",'#13#10'"+ + + + + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * ' + +'= = = = = = = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , '' '' '' '' ) ) ) ! !' + +' ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.' + +'p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! !' + +' ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - ' + ,'= = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + +' + +' . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ ' + +'[ [ } } | | | | 1 1 1 2 2 2 3 3 k*i*i*7*@*-*5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0' + +' 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l b$5$5$5$' + +'6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$.$s*s t t t u u u v v v v w w x x x x x x y y' + +' y y z z A A B B B B C C C D D D E E E E E F G G G }#r@j*H H H H H I I J J ' + +'J c.K K K L M M M M M j.N N N N N O P Q Q Q R R S S S T T T T -%I.#%*+&+&+*' + +'+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,+''+''+!+!+~+~+~+~+' + +'{+]+]+]+]+]+7+7+7+7+8+9+(+:+c+).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._' + +'._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.' + +'7.8.8.8.",'#13#10'"+ + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & ' + +'& & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; > , , , , , , , '' '' '' '' '' ' + +') ) ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p' + +'.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ) ) '' '' '' '' '' , , , , , , , > ; ; ; ; ; ; ; ; ; ; ' + +'- - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + +' + +' + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ ' + +'[ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 %*i*i*7*7*q&5 5 5 5 6 6 6 7 7 7 8 8 9 9' + +' 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l N$5$' + +'5$5$5$6$3$3$<$<$<$<$>$>$>$>$>$,$,$$$$$.$]$t t u u u v v v v v w x x x x x x' + +' x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G q@%@H H H H H I ' + +'J J J J c.K K L L M M M M M N N N N N O P P Q Q Q R R S S S `.++*&6+6+%+%+%' + +'+&+&+*+*+*+*+*+*+=+=+=+-+-+-+>+M.Q.D+D+D+D+G+l*l*l*l*v$v$v$v$u*b%#*V.V.U.U.' + +'T.~+{+]+]+]+]+]+7+7+7+7+9+9+0+0+0+m+f%:+1&9&~.~.~.~.{.].^.^.^.e././././.(.(' + +'._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.' + +'5.6.7.8.8.8.",'#13#10'". + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % ' + +'% % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , '' '' ' + +''' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p' + +'.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ;' + +' - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + ' + +'+ + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [' + +' [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 1 f*i*i*7*~* *5 5 5 5 6 6 6 7 7 7 8 9 ' + +'9 9 9 9 0 0 a a a b b c c d d e e e e e f g g g g g h i j j j j k k l l v*y' + +'$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$W#j&t u u u v v v v w w x x x x ' + +'x x a.y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I' + +' I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R O >@R$(&5+5+5+6+%+%+' + +'%+%+&+*+*+*+*+*+A+w*J.D+D+(%x*` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*' + +'.*.*.=.k.H$Y+X+`+/+9+7+7+8+9+9+0+0+0+m+m+m+J+J++@y*i@z*].].^.^.^.e././././.' + +'(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5' + +'.5.5.7.7.8.8.8.",'#13#10'". + + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $' + +' % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' + +''' '' '' '' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o' + +'.o.o.o.o.o.o.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ) ) ) '' '' '' '' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; -' + +' - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + ' + +'+ + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0' + +'.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 0*i*i*7*7*9*5 5 5 5 5 6 6 7 7 7 8 8 ' + +'9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l i d' + +'$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$+$K#j&u u u v v v v w w x x x ' + +'x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H' + +' I I J J J c.K K K L L M M M M j.N N N N N O P Q Q }+|+2+4+4+4+5+5+5+5+6+%+' + +'%+%+%+*&U+I.-%i$X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&' + +'.*.*.*.=.=.-.-.-.-.;.;.,@<+b+Y+(+0+0+0+m+m+m+J+K+K+K+K+K+L+r+~.^.^.e./././.' + +'/.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5' + +'.5.5.6.7.7.8.8.8.",'#13#10'" . + + + + + + + n.@ @ @ @ @ @ @ # # # $ $ $ $' + +' $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , ' + +', , '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.' + +'o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ;' + +' - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.+ + ' + +'+ + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < <' + +' < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 A*i*i*i*7*.*5 5 5 5 5 6 6 6 7 7 7 ' + +'8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e e g g g g g h h j j j j k k l l' + ,' Q$e$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$,$,$$$$$$$$$+$($u u u v v v v v w x x ' + +'x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H' + +' H H I I J J J c.K K L L M M M M M j.N N N N N O .+R$g+k+3+3+4+4+4+5+5+5+5+' + +'6+#+U+G.h$W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&' + +'.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.,@u&`%f%J+J+K+K+K+K+K+ @.@+@y*x@H%' + +'/./././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m' + +'.5.5.5.5.5.6.7.7.8.8.9.",'#13#10'" . + + + + + + + n.@ @ @ @ @ @ @ # # #' + +' $ $ $ $ $ $ % % % % & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; > , ' + +', , , , , , , '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' , , , , , , , , > ; ; ; ; ; ; ; ; ;' + +' ; ; - - = = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.' + +'+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : :' + +' : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 B*C*i*i*7*7*!*5 5 5 5 5 6 6 7 ' + +'7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k' + +' k l n$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$$$b&t u u u v v v v w ' + +'w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H' + +' H H H H I I J J J J K K K L L M M M M M N N N N K%B$D*j+k+k+k+3+3+4+4+4+5+' + +'_&.+E*U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$' + +'.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).9&1&y*+@K+K+K+ @.@.' + +'@.@.@]@G@F*].(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.' + +'4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.",'#13#10'" . + + + + + + + g.@ @ @ @ @ ' + +'@ @ # # # $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ;' + +' ; ; ; , , , , , , , , , '' '' '' '' '' '' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' '' , , , , , , , , , ; ; ; ; ; ; ' + +'; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @' + +' @ @ g.+ + + + + + + . ~ ~ { { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( ' + +'_ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 G*i*i*i*7*A&5 5 5 5 5' + +' 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i ' + +'j j j k k k H*e$e$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$,$,$$$$$$$$$b&t t u u u v' + +' v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G ' + +'G G G G H H H H H I I J J J c.K K K L L M M M M M N z$1@k+h+i+j+k+k+k+3+3+3' + +'+(&A$I*T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.' + +'$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.w@' + +'p+6% @.@.@.@{@]@]@]@]@&#/@_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2' + +'.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.9.",'#13#10'" . + + + + + + + + g' + +'.@ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ' + +'; ; ; ; ; ; ; ; ; ; , , , , , , , , , '' '' '' '' '' '' ) ) ) ) ! ! ! ! ! !' + +' ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) '' '' '' '' '' '' , , , , , , , , , ; ; ; ;' + +' ; ; ; ; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ $ # # @ ' + +'@ @ @ @ @ @ g.+ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( (' + +' ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 e*i*i*i*7*8*d&' + +'5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g' + +' g h i j j j j k k c$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$($t t ' + +'u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E' + +' E F G G G G G i.H H H H H I I J J J c.K K K L L M M M M o%f+h+h+h+h+i+j+k+' + +'k+k+2+|+`.S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#' + +'.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).' + +'!.!.~.~.~.~.].F@q+y*.@{@]@]@]@]@6@6@z@U@(@:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1' + +'.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.9.",'#13#10'" . + + +' + +' + + + + + @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & & * = = = = = = ' + +'= = - - ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , '' '' '' '' '' '' ) )' + +' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) '' '' '' '' '' '' , , , , , , , , , , ;' + +' ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & & % % % % $ $ $ $ $ $ ' + +'$ # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ /' + +' / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 0*i*' + +'i*i*7*3*5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e' + +' g g g g g g h i j j j k k J*l$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$' + +'$$K*t t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D' + +' D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L M M ^%K&w+h+h+h+h+' + +'h+h+i+j+f+|+N R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+' + ,'.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''' + +'.''.).).!.!.~.~.~.~.~.].^.^.^.~.x@G@]@]@]@6@6@7@7@7@9@L*:.<.[.[.[.[.[.}.}.}' + +'.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.",'#13#10'" ' + +' . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * ' + +'= = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , '' '' ' + +''' '' '' '' '' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) '' '' '' '' '' '' '' , , , , , , ' + +', , , , > ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * & & & % % % % % $' + +' $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ' + +'] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 1' + +' 2 2 2 9*i*i*i*7*M*5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d ' + +'d e e e e e f g g g g g h i j j j j k h d$e$e$e$5$5$5$5$6$3$3$<$<$<$<$>$>$>' + +'$>$>$,$,$$$$$K*s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B ' + +'B C C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L c.v+x' + +'+S+S+h+h+h+h+h+k+R$>@Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z ' + +'` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;' + +'.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././.].*%L+6@6@7@7@m@m@m@|#N*[.[' + +'.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.",' + +#13#10'" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % ' + +'% % % & & & * * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , ,' + +' , , , , , '' '' '' '' '' '' '' '' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ' + +'! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) '' '' '' '' '' '' '' '' , , ,' + +' , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * * & & & ' + +'% % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ' + +' ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | ' + +'| | | 1 1 1 2 2 2 3 ~*i*i*i*C*X&5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a' + +' a b b c c d e e e e e e g g g g g g h i j j j j k Q$e$e$e$5$5$5$5$6$6$3$3$' + +'<$<$<$>$>$>$>$>$>$,$$$$$K*s s t t u u u v v v v v w x x x x x x x y y y y z' + +' z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K ' + +'K K J O+x+S+S+S+S+h+h+h+f+|+O P Q Q Q R R R S S T T T T U V V V V W X X X X' + +' X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.' + +'-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.T@9@7@7@' + +'m@m@m@O*z@|#P*[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7' + +'.8.8.9.9.",'#13#10'" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $' + +' $ $ $ $ $ % % % % % & & & * * = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ' + +'; ; ; , , , , , , , , , , , , '' '' '' '' '' '' '' '' '' ) ) ) ) ) ) ) ) ) ' + +') ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) ) ) ) ) '' '' '' '' '' '' '' ''' + +' '' , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = =' + +' = * * & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ' + +' ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [' + +' [ [ [ } } | | | | 1 1 1 2 2 2 3 %*i*i*i*7*%*5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 ' + +'9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g h h i j j j k 4$m$e$e$e$5' + +'$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$,$$$V#s s t t t u u u v v v v w w x x x x ' + +'x x a.y y y y z z A B B B B B C C C D D D E E E E b.F G G G G G i.H H H H H' + +' I I J J J c.K I O+&@S+S+S+S+S+x+Y%x%z$N N O P Q Q Q R R S S S T T T T U V ' + +'V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&' + +'.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.e././' + +'././.(.(._.(@U@Q*m@m@O*z@A@A@R*P*[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.' + +'5.5.5.5.5.6.7.8.8.8.9.9.",'#13#10'"~ . + + + + + + + + @ @ @ ' + +'@ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & & * = = = = = = = = = - - - ; ;' + +' ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , , , , , '' '' '' '' '' '' '' ' + +''' '' '' '' '' ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) '' '' '' '' '' '' ' + +''' '' '' '' '' '' , , , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; -' + +' - - = = = = = = = = = * & & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ ' + +'@ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _' + +' : : : : < < < [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 1 f*i*i*7*8*d&5 5 5 5 ' + +'5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i' + +' j j j j `$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$,$,$W#]$s s t t t u u ' + +'u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G' + +' G G G G G H H H H H I I J J J J I R@+#R+R+S+S+S+S+Y%7&N N N N N O P Q Q Q ' + +'R R S S S T T T T U V V V W W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#..' + +'.L&t$M&M&M&M&(*(*$.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.' + +'~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.S*&#z@z@A@A@A@A@|#z&}.}.}.|.|.1.1.1' + +'.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.6.7.8.8.8.9.9.",'#13#10'"~ ~ .' + ,' . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % & & & * * ' + +'= = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , ,' + +' , , , , '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ' + +''' '' '' '' '' '' '' '' '' '' '' '' '' '' '' , , , , , , , , , , , , , , , ' + +'> ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * * & & & % % % % % $' + +' $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . . ~ ~ ~ { ' + +'{ { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ [ } } | | | | 1' + +' 1 1 2 2 2 3 T*i*i*i*7*9*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b ' + +'c c d d e e e e e e g g g g g g h i j j j h l$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<' + +'$>$>$>$>$>$>$,$W#]$s s s t t u u u v v v v v w x x x x x x x y y y y z z A ' + +'A B B B B C C C D D D E E E E E F G G G G G G H H H H H I I J J t&<*+#=@=@R' + +'+R+S+S+Y%u+M N N N N N O P P Q Q R R R S S S T T T U U V V V W r.X X X X Y ' + +'Y Z Z Z ` ` Q%''*U*I+D+F$-&M.N.N.N.N.N.l+,+,+,+,+''+''+!+!+~+~+~+U.&*/+`+Z+' + +'Y+b+<+;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._' + +'.:.:.:.(@%#n@A@A@A@W@W@H@V*}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.' + +'8.8.8.9.9.",'#13#10'"~ ~ ~ . . + + + + + + + + @ @ @ @ @ @ @ @ ' + +'# # # $ $ $ $ $ $ $ % % % % % & & & & * = = = = = = = = = - - - ; ; ; ; ; ;' + +' ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , , , , , , , , '' '' '' '' '' ' + +''' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' , , , , , , , ,' + +' , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = ' + +'= = * & & & & % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + +' + +' . . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.' + +'[ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 e*i*i*i*7*0*5 5 5 5 5 5 6 6 6 7 7 7 8' + +' 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j Q$m$e$' + +'e$e$e$5$5$5$5$6$3$3$<$<$<$<$>$>$>$>$>$,$n*W*s s s t t t u u u v v v v w w x' + +' x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.F G G G G G i.H ' + +'H H H H I I J O+E@|@=@=@=@R+R+Y%u+M M j.N N N N N O P Q Q Q R R R S S T T T' + +' T U V V V V W X X X X X Y Y j$D+,%L.-+-+;+;+O.O.O.O.P.N.N.N.N.N.,+,+,+,+''' + +'+''+)+!+!+~+~+~+~+{+]+]+]+]+]+7+7+0+~@Y+)@3@''.''.).).).!.!.~.~.~.~.{.].^.^' + +'.^.^././././.(.(.(._._.:.:.:.:.<.[.:.%#A@A@W@W@W@W@B@J@|.1.1.1.2.2.2.3.3.4.' + +'4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.9.",'#13#10'"{ ~ ~ ~ . + + + + ' + +'+ + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & * * = = = =' + +' = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , ' + +', , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,' + +' , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = * ' + +'* & & & % % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + + .' + +' ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ ' + +'[ [ [ [ } } | | | | | 1 1 1 2 2 2 3 0*i*i*i*7*3*5 5 5 5 5 6 6 6 7 7 7 8 8 9' + +' 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j j 4$m$m$e$e$' + +'e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$X*o*s s s s t t t u u v v v v v w w x x' + +' x x x x y y y y z z A A B B B B C C C C D D E E E E E F G G G G G G H H H ' + +'H H I I Y*Q+*@*@|@=@=@R+Z*u+M M M M N N N N N N O P Q Q Q R R S S S T T T T' + +' U V V V V W X V h$G.U+z+=+=+=+=+-+-+-+;+;+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+' + +'''+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+9+9+0+0+(+)@3@).!.!.~.~.~.~.{.].^.^.^.' + +'e././././.(.(._._._.:.:.:.l.<.[.[.[.`*{#W@W@W@W@W@*#l#;#1.1.2.2.2.3.3.4.4.4' + +'.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.",'#13#10'"{ { ~ ~ ~ . + + + + +' + +' + + + + @ @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % % % & & & & * = = = = ' + +'= = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ,' + +' , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ' + +', , ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = = * & & &' + +' & % % % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ @ @ + + + + + + + + + . ' + +' ~ ~ ~ { { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < [ [ [ [ [' + +' [ [ } } | | | | 1 1 1 2 2 2 2 A*i*i*i*7*M*5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 ' + +'9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k$m$m$e$e$e$e$5' + +'$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$ =s s s s t t t u u u v v v v w w x x x x ' + +'x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G G H H H H H' + +' %@w+*@*@*@*@=@=@/&Y*L M M M M M N N N N N O P P Q Q Q R R S S S T T T U U ' + +'V V V F. +.=*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+''+!' + +'+!+!+~+~+~+{+{+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+0&j@i@w@~.~.].].^.^.^.e./.' + +'/././.(.(._._._.:.:.:.<.<.[.[.[.[.[.7#B@W@W@W@*#]#m#z&1.2.2.3.3.3.4.4.4.4.m' + +'.5.5.5.5.5.6.7.8.8.8.9.9.+=",'#13#10'"{ { ~ ~ ~ . + + + + + +' + +' + + + @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & * * = = = = ' + +'= = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , ,' + +' , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ; ; ; ; ' + ,'; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & % % %' + +' % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ + + + + + + + + + . ' + +' ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ }' + +' } | | | | 1 1 1 1 2 2 2 3 k*i*i*i*7*;*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 ' + +'0 0 a a a b b c c d d e e e e e e g g g g g g h i j j g l$m$m$e$e$e$5$5$5$5' + +'$6$6$3$3$<$<$<$>$>$>$>$>$>$@=r s s s s t t t u u u v v v v w w x x x x x x ' + +'y y y y z z z A B B B B B C C C D D D E E E E b.F G G G G G i.H H H H #=[@}' + +'@*@*@*@|@/&l&K L L M M M M j.N N N N N O P Q Q Q R R R S S T T T T U h$H.#%' + +'$+&+y+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+L.-&F$D+l*l*;&L&L&M&M&(*$.$.%.%.$.Z%Y.Y' + +'.Y.X.X+Y+`+/+8+]+7+7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+$=G%F@^.^.^.^.e././././.' + +'(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}./#H@*#*#]#]#]#n#%=2.3.3.4.4.4.4.4.5.5.5.5' + +'.5.5.7.7.8.8.8.9.9.+=",'#13#10'"{ { { ~ ~ ~ . + + + + + + + +' + +' + g.@ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & & * * = = = = = ' + +'= = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , ,' + +' , , , , , , , , , , , , , , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ' + +'; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & & % % % % % % $' + +' $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ g.+ + + + + + + + + . ~ ~ ' + +'~ { { { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | |' + +' | | 1 1 1 2 2 2 3 9*i*i*i*7*A&5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a ' + +'a a b b c c d d e e e e e g g g g g g h i j j j +%m$m$m$e$e$e$5$5$5$5$6$3$3' + +'$3$<$<$<$>$>$>$>$>$&=r s s s s s t t u u u v v v v v w x x x x x x x y y y ' + +'y z z A A B B B B C C C C D D E E E E E F G G G G G G H H H _@+#e@}@*@*@*@*' + +'=v+K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T `.++T+%+%+%+%+&+*+' + +'*+*+*+*+*+*+=+=+Q&q*F$G+''*`&+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.' + +'=.-.-.-.-.-.,@<+:+_+Z+a+0+0+0+m+m+m+J+J+K+K+K+K+K+ @&%==z*^./././././.(.(._' + +'._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.-=:&]#]#]#]#X@e#1.3.4.4.4.4.4.5.5.5.5.5.6.' + +'7.7.8.8.8.9.9.+=",'#13#10'"] { { { ~ ~ ~ . + + + + + + + + + ' + +'g.@ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % & & & & * * = = = = = =' + +' = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ' + +'> > , , , , , , , , , , , > > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & & % % % % % % $ $ $ $ $ ' + +'$ $ $ # # # @ @ @ @ @ @ @ @ g.+ + + + + + + + + . ~ ~ ~ { { {' + +' ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | 1 ' + +'1 1 1 2 2 2 3 ~*i*i*7*C* *5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b' + +' c c d d e e e e e e g g g g g g h i j j T$J$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$' + +'<$<$>$>$>$>$[$r r s s s s t t t u u u v v v v w w x x x x x x x y y y y z z' + +' A A B B B B C C C D D D E E E E E F G G G G G G H H ;=P@e@}@}@*@*@<@N+K K ' + +'K L L M M M M M N N N N N O P P Q Q Q R R S S P ++(&5+5+6+%+%+%+%+&+*+*+*+*' + +'+#+w*p*j$Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.' + +'-.-.-.;.;.;.;.,.,.''.,@''@!@>=m+J+J+K+K+K+K+K+ @.@.@.@&%#@F@/./.(.(._._._.:' + +'.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.E&m#]#X@X@X@8#k#4.4.4.4.m.5.5.5.5.5.6.7.7.' + +'8.8.9.9.9.+=",'#13#10'"^ ] { { { ~ ~ ~ . . + + + + + + + + n.' + +'@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & & & * = = = = = = =' + +' = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ' + +'; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; - - - - = = = = = = = = = = = = * & & & & & % % % % % % $ $ $ $ $ $ $ # ' + +'# # @ @ @ @ @ @ @ @ @ n.+ + + + + + + + . . ~ ~ ~ { { { ] ^ ^' + +' ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 1 ' + +'2 2 2 3 ,=i*i*i*7*%*5 5 5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c' + +' d d e e e e e g g g g g g h h j j g M$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$' + +'>$>$>$~$o r s s s s s t t t u u v v v v v w w x x x x x x y y y y z z z A B' + +' B B B B C C C D D D E E E E b.F G G G G G i.##[@e@e@e@}@}@Q+Y*J c.K K K L ' + +'L M M M M j.N N N N N O P Q Q Q R R O .+C$5+5+5+5+6+%+%+%+%+&+&+#+U+-%V X Y' + +' Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.' + +';.;.;.;.>.,.''.''.''.''.).).''=G%)=+@K+K+K+ @ @.@.@.@{@]@]@L+*%!%(._._._.:.' + +':.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.;#!=X@X@X@H@H@9#4.4.4.m.5.5.5.5.5.6.7.7.8' + +'.8.9.9.9.+=",'#13#10'"^ ] ] { { { ~ ~ ~ . . + + + + + + + + n' + +'.@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % & & & & & * = = = = = ' + +'= = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - ' + +'- - = = = = = = = = = = = = = * & & & & & % % % % % % $ $ $ $ $ $ $ $ # # #' + +' @ @ @ @ @ @ @ @ @ n.+ + + + + + + + . . ~ ~ ~ { { { ] ] ^ ^ ' + +'^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 2 2' + +' 2 3 3 ~*i*i*7*C* *5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d ' + ,'d e e e e e e g g g g g g h i j j .%J$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$>$>' + +'$>$,$_$q r s s s s t t t u u u v v v v v w x x x x x x x y y y y z z A A B ' + +'B B B C C C C D D E E E E E F G G G G G G @#e@e@e@e@e@*@~=J J J c.K K L L M' + +' M M M M j.N N N N N O P Q Q Q }+V&4+4+4+5+5+5+5+6+%+%+%+#%I*V X X X X Y Y ' + +'Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;' + +'.;.;.;.>.,.''.''.''.''.).).!.!.~.~.w@==k@l@.@.@.@.@{@]@]@]@]@7@{=L*_._.:.:.' + +':.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f./#=#H@H@=#=#]=0#4.m.5.5.5.5.5.6.7.8.8.8' + +'.9.9.+=+=",'#13#10'"^ ^ ] ] { { { ~ ~ ~ . . + + + + + + + + +' + +' @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ % % % % % % & & & & & * * = = = ' + +'= = = = = = = = = - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - = ' + +'= = = = = = = = = = = * * & & & & & % % % % % % $ $ $ $ $ $ $ $ # # # # @ @' + +' @ @ @ @ @ @ @ + + + + + + + + + . . ~ ~ ~ { { { ] ] ^ ^ ^ ^ ' + +'/ / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2' + +' 3 a*i*i*7*7*%*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 a a a a b b c c d d ' + +'e e e e e g g g g g g h h j j W$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>' + +'$^=q r s s s s s t t t u u u v v v v w w x x x x x x a.y y y y z z A A B B ' + +'B B C C C D D D E E E E E F G G G G E +#e@e@e@e@e@+#S@J J J J K K K L L M M' + +' M M M N N N N N O P P Q K%f+3+3+4+4+4+5+5+5+5+6+#%H.i$V W X X X X X Y Z Z ' + +'Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.;.;' + +'.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.{.F@x@y*+@.@]@]@]@]@]@6@6@7@V@0@:.:.:.<' + +'.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.|.l#H@=#=#=#=#/=5.5.5.5.5.5.7.7.8.8.8.9.' + +'9.+=+=",'#13#10'"^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + ' + +'g.@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % % & & & & & * * = = =' + +' = = = = = = = = = = = - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ' + +'; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - = = = = = = =' + +' = = = = = = = * * & & & & & % % % % % % % $ $ $ $ $ $ $ $ # # # @ @ @ @ @ ' + +'@ @ @ @ g.+ + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / /' + +' ( ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 ' + +'~*i*i*7*~* *5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e' + +' e e e g g g g g g h i j g M$J$m$m$e$e$e$5$5$5$5$5$6$3$3$<$<$<$<$>$>$>$K*q ' + +'q r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z z A B B B B B' + +' C C C D D D E E E E b.G G G G }#!#.&e@e@e@P@(=I I J J J c.K K K L L M M M ' + +'M M N N N N N O P |+g+k+3+3+3+4+4+4+5+5+V&H.U V V V W W X X X X X Y Z Z Z Z' + +' ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.' + +';.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.H%r+p+p%]@]@6@6@7@7@7@m@n@_=(.<.[.' + +'[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.2#n#=#=#=#=#u#P#5.5.5.6.7.7.8.8.8.9.9.+' + +'=+=",'#13#10'"^ ^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + n' + +'.@ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ % % % % % % % & & & & & * * = = ' + +'= = = = = = = = = = = = = - - - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - - - = = = = = = = = = = = ' + +'= = = = * * & & & & & % % % % % % % $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @' + +' @ n.+ + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ' + +'( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 ,=i*i' + +'*7*7*|*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b b c c d e e e e e ' + +'e g g g g g g h h j j :=J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$b*q q r r' + +' s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C ' + +'C D D D E E E E E F G G G [#r@r@e@e@e@E@l&I I J J J J c.K K L L M M M M M j' + +'.N N N N N ;@4+k+k+k+3+3+4+4+4+*& +F.T U V V V V W X X X X X Y Y Z Z Z ` ` ' + +'` . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,' + +'.''.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././.F*L+6@6@7@7@7@m@m@m@z@_=:.[.' + +'[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.e#=#=#8#n#R&p#5.5.6.7.7.8.8.9.9.9.+=+' + +'=",'#13#10'"^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + +' + +' @ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ % % % % % % % & & & & & & * * ' + +'= = = = = = = = = = = = = = = = = - - - - - - - - - ; ; ; ; ; ; ; ; ; ; ; ;' + +' ; ; ; ; ; ; ; ; ; ; ; - - - - - - - - - = = = = = = = = = = = = = = = = = ' + +'* * & & & & & & % % % % % % % $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ +' + +' + + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ' + +'( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 ~*i*7*7' + +'*~* *5 5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e ' + +'g g g g g g h i j T$J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$>$~$-$q q r s s' + +' s s s t t u u u v v v v v w w x x x x x x y y y y z z z A B B B B B C C C ' + +'D D D E E E E E F G E d@r@r@.&e@e@Q@H H I I J J J J K K K L L M M M M M N N' + +' N N 1+3+i+j+k+k+k+3+3+4+R$<=T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ' + ,' . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.' + +'''.''.''.''.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.T@^@7@7@7@m@m@m@z@z@n@0@[' + +'.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.3.4.4.**n#8#n#n#M#[=5.6.7.8.8.8.9.9.+=+=+=' + +'",'#13#10'"/ ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . + + + + + + + + + ' + +'+ g.@ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ % % % % % % % % & & & & & *' + +' * * = = = = = = = = = = = = = = = = = = = - - - - - - - - - - - - - - - - ' + +'- - - - - - - - - - - - - - - = = = = = = = = = = = = = = = = = = = * * * &' + +' & & & & % % % % % % % % $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ g.+ + ' + +'+ + + + + + + + . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( (' + +' ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 9*i*i*7*@*' + +'|*5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b b c c d e e e e e e g g' + +' g g g g h i j h M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$^=q q r r s s ' + +'s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C C D' + +' D E E E E E b.G }#!#r@r@r@e@P@0%H H H I I J J J c.K K K L L M M M M M N N ' + +'1+k+h+i+j+k+k+k+3+=%|+P S S T T T U U V V V W W X X X X X Y Z Z Z Z ` ` . ' + +'. .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.>.,.''' + +'.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(.(@U@g%m@m@m@z@A@A@A@R*P*' + +'}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.f.}=n#n#n#n#|=7.7.8.8.8.9.9.+=+=+=",' + +#13#10'"/ / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . . + + + + + + + + + ' + +'n.@ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ % % % % % % % % & & & & & &' + +' * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = - - - - - - - ' + +'- - = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * & & & & & &' + +' % % % % % % % % $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ n.+ + + + + ' + +'+ + + + . . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ :' + +' : : : : < < 0.[ [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 3 k*i*7*7*Y& *5 ' + +'5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g' + +' g g h i j 9%J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$>$K*q q q r s s s s s ' + +'t t u u u u v v v v w w x x x x x x a.y y y y z z A A B B B B C C C D D D E' + +' E E E E F [#r@r@r@r@r@E@l&H H H I I J J J J c.K K L L M M M M M j.7&k+h+h+' + +'h+i+j+k+k+g+2@R R S S T T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . ...+' + +'.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.,.''.''.' + +'''.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.7%n@O*z@A@A@A@A@A@H@V*' + +'}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.9#n#n#n#M#1=2*8.8.8.9.9.+=+=+=",'#13 + +#10'"/ / / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . + + + + + + + + + +' + +' @ @ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ % % % % % % % % % & & & & ' + +'& & * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =' + +' = = = = = = = = = = = = = = = = = = = = = = = = * * * & & & & & & % % % % ' + +'% % % % % $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ @ + + + + + + + + +' + +' + . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : ' + +': : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 A*i*7*7*@*|*5 5 5 5' + +' 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g ' + +'h i j `$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$|$q q q r r s s s s t t t' + +' u u u v v v v v w x x x x x x x y y y y z z z A B B B B B C C C D D D E E ' + +'E E b.}&r@r@r@r@r@2=H H H H H I I J J J c.K K K L L M M M M t&g+h+h+h+h+i+i' + +'+j+g+|+Q R R S S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.' + +'+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''' + +'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.$#V@A@A@A@A@A@W@W@B@' + +'7#}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.[=M#n#M#R&}*p#8.9.9.9.+=+=+=",'#13#10 + +'"( / / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + ' + +'n.@ @ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % & & &' + +' & & & & * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ' + +'= = = = = = = = = = = = = = = = = = = * * * & & & & & & & % % % % % % % % %' + +' $ $ $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + . ' + +' ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : <' + +' < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 3 0*i*7*@*~* *5 5 5 5 6 ' + +'6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e f g g g g g g h i' + +' j I$J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$,$-$q q q r s s s s s t t t u ' + +'u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E' + +' ,#r@r@r@r@h*P%H H H H H H I I J J J c.K K K L L M M c.f+x+h+h+h+h+h+i+D*|+' + +'Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#' + +'.#.#...L&;&v$v$v$3=#*#*V.V.V.b%u*X.X.X+w$H$H$-.-.;.;.;.;.>.,.''.''.''.).).)' + +'.!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.<.L*|#A@A@A@W@W@W@W@z#' + +'%=1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.p#R&M#R&f#f#v#9.9.9.+=+=4=",'#13#10'"( ' + +'( / / / ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ . . + + + + + + + + + +' + ,' g.@ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % & ' + +'& & & & & & & * * * * = = = = = = = = = = = = = = = = = = = = = = = = = = =' + +' = = = = = = = = = = = = * * * * & & & & & & & & % % % % % % % % % % $ $ $ ' + +'$ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + . . ' + +' ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < < ' + +'[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 e*i*7*@*@*A&5 5 5 5 6 6 6 7' + +' 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j W$' + +'J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>${$q q q r r s s s s t t t u u u v' + +' v v v v w x x x x x x x y y y y z z z A B B B B B C C C D D D E E E }#!# #' + +'r@r@r@!#v%G H H H H H I I J J J J K K K L L M M u+/&S+x+h+h+h+h+k+;@P Q Q Q' + +' R R R S S T T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . ...2%(%G+F$R.Q.' + +'N.N.N.l+,+,+,+,+''+''+!+!+!+~+~+~+{+{+]+]+]+]+5=~@(+_+:+t*)%''.''.''.).).).' + +'!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.:.:.<.[.[.[.P*n@W@W@W@W@W@*#]' + +'#6=1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.P#7=R&f#f#f#8=9.+=+=+=4=",'#13#10'"( (' + +' ( / / / / ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ . + + + + + + + + + ' + +'+ n.@ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % %' + +' % & & & & & & & & & & * * * * * = = = = = = = = = = = = = = = = = = = = = ' + +'= = = = = = * * * * * & & & & & & & & & & % % % % % % % % % % $ $ $ $ $ $ $' + +' $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + . ' + +' ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < [ [' + +' [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 3 T*7*7*@*@*;*5 5 5 6 6 6 7 7 ' + +'7 7 8 9 9 9 9 9 0 0 0 a a a b b b c c d e e e e e e g g g g g g h h i g M$J' + +'$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$[$q q q q r s s s s s t t t u u u v ' + +'v v v w w x x x x x x x y y y y z z A A B B B B C C C C D D E E E }#h*O@r@r' + +'@r@u%E G i.H H H H H I I J J J c.K K K L L c.g+S+S+S+h+h+h+h+1+O P P Q Q Q ' + +'R R S S S T T T T U V V V V W X X X X X Y Y Z Z Z ` x*G+r$N.>+O.O.O.P.N.N.N' + +'.N.N.,+,+,+,+,+''+''+!+!+!+~+~+~+{+]+]+]+]+]+7+7+7+7+8+9+0+~@_+u&d+).).!.!.' + +'~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.I@9=W@W@W@*#*#]#z' + +'#;#2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.Q#f#f#f#0=a=+=+=+=4=",'#13#10'"( ( ( (' + +' / / / / ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . + + + + + + + + + + ' + +'+ n.@ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % %' + +' % % % & & & & & & & & & & & & & * * * * * * * * * * * * * * * * * * * * * ' + +'* * & & & & & & & & & & & & & % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $' + +' # # # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + . ' + +'~ ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [' + +' [ [ } } } | | | | 1 1 1 1 2 2 2 3 3 3 3 1 C*7*@*@*q&5 5 5 5 6 6 7 7 7 7 8 ' + +'9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e g g g g g g h i j b=J$J$m$m' + +'$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$,$o*q q q r r s s s s t t t u u u v v v v ' + +'v w w x x x x x x y y y y z z z A B B B B B C C C D D D E E b@r@N@ #r@r@d@G' + +' G G H H H H H H I J J J J c.K K L L u@x+S+S+S+x+h+h+f+z$N O P Q Q Q R R R ' + +'S S S T T T U U V V V W W X X X X X Y Z =&D+,%3%-+-+;+;+O.O.O.O.P.N.N.N.N.l' + +'+,+,+,+,+''+''+)+!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+6%j@d+~.~.' + +'~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.c=7%W@W@*#]#]#]#m#d' + +'=2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.e=0=0=0=0=o#7.+=4=",'#13#10'"( ( ( ( ( /' + +' / / / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . . + + + + + + + + + + ' + +'+ g.@ @ @ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % %' + +' % % % % % % & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & ' + +'& & & & & & & & % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ # # # #' + +' @ @ @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + + . . ~ ~ ~ ' + +'~ { { { ] ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [' + +' } } | | | | | 1 1 1 2 2 2 2 3 3 3 3 G*7*@*@*@*S&5 5 5 6 6 6 7 7 7 8 8 9 9 ' + +'9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h h i `$J$J$m$m$m$e$e' + +'$e$5$5$5$5$5$6$3$3$3$<$<$<$>$!$q q q q r s s s s s t t t u u u v v v v w w ' + +'x x x x x x x y y y y z z A A B B B B B C C C D D D E m% #N@O@ #r@M@G G G i' + +'.H H H H H I I J J J J K K K J n%S+S+S+S+S+h+k+|+N N N O P Q Q Q R R S S S ' + +'T T T T U V V V V W r.X X X X i$~&O.=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,' + +'+,+,+''+''+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+0+0+0+m+m+m+J+J+K+K+)=i@{.' + +'].^.^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.J@B@*#]#]#]#]#X@1#1' + +'.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8._*f=0=0=}=g=h=4=",'#13#10'"( ( ( ( ( ( ( /' + +' / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + + ' + +'n.@ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % %' + +' % % % % % % % % % % % & & & & & & & & & & & & & & & & & & & & & & & & & % ' + +'% % % % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @' + +' @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + . ~ ~ ~ { { { ' + ,'{ ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } |' + +' | | | | 1 1 1 2 2 2 2 3 3 3 3 1 C*@*@*@*.*5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 ' + +'0 0 0 a a a b b b c c d d e e e e e e g g g g g g h i j :=J$J$m$m$e$e$e$e$5' + +'$5$5$5$6$6$3$3$<$<$<$>$[$q q q q r r s s s s t t t u u u v v v v v w w x x ' + +'x x x x y y y y y z z A A B B B B C C C D D D E M%O@N@N@ #r@~#G G G G H H H' + +' H H H I I J J J c.K K %@/&R+S+S+S+S+S+x%N N N N O P P Q Q Q R R S S S T T ' + +'T T U V V V V W X X -%U+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,' + +'+''+''+!+!+!+~+~+~+{+{+]+]+]+]+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+i=y*r+' + +'~.^.^././././.(.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.-=m#]#]#]#X@X@8#6=4' + +'.4.4.4.4.5.5.5.5.5.5.6.7.8.8.8.j=f=}=}=}=k=h=",'#13#10'"_ ( ( ( ( ( ( ( / /' + +' / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + + ' + +'n.@ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % % % %' + +' % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % ' + +'% % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @' + +' @ @ @ @ @ n.+ + + + + + + + + + + . . ~ ~ ~ ~ { { { { ] ] ' + +'^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : : < < 0.[ [ [ [ [ [ [ } } | | | |' + +' | 1 1 1 1 2 2 2 3 3 3 3 3 ,=7*@*@*@*3*5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 ' + +'a a a a b b c c d d e e e e e e g g g g g g h i j T$J$J$m$m$m$e$e$e$5$5$5$5' + +'$6$6$3$3$3$<$<$<$,$-$q q q q r s s s s s t t t u u u v v v v w w x x x x x ' + +'x x y y y y z z z A B B B B B C C C D D D ~#N@N@N@N@ #q@G G G G G H H H H H' + +' I I J J J J c.K :@R+R+S+S+S+S+w+K%N N N N N O P Q Q Q R R R S S S T T T U ' + +'U V V V W V+I.*+*+*+*+*+=+=+=+=+-+-+-+;+;+O.O.O.O.P.N.N.N.N.l+,+,+,+,+''+''' + +'+''+!+!+T.b%W.X.X+Y+Y+`+`+/+~@9+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+ @ @.@+@y' + +'*l=/./././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.;#z#]#X@X@X@H@H@-#4.' + +'4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.6.k=}=}=f=k=",'#13#10'": _ ( ( ( ( ( ( ( / / ' + +'/ ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . + + + + + + + + + + + +' + +' n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ' + +'% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % %' + +' % % % $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ @ ' + +'@ @ n.+ + + + + + + + + + + + . ~ ~ ~ ~ { { { { ] ^ ^ ^ ^' + +' ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : : < < 0.[ [ [ [ [ [ [ } } | | | | | 1 ' + +'1 1 1 2 2 2 3 3 3 3 3 4 0*@*@*@*w&5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a' + +' a b b c c d d e e e e e e g g g g g g g h i j I$J$J$m$m$e$e$e$e$5$5$5$5$6$' + +'6$3$3$<$<$<$>$''$q q q q r r s s s s t t t u u u v v v v v w w x x x x x x ' + +'a.y y y y z z A A B B B B C C C C D D M%N@N@N@N@m=m%G G G G G i.H H H H H I' + +' I J J J c.I w+=@R+R+S+S+x+K&M N N N N N N O P Q Q Q R R S S S T T T T U V ' + +'V i$I.V&y+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+''+''+!' + +'+V.Z%*.*.*.=.=.-.-.-.-.-.;.;.;.<+b+Y+(+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@' + +'p%#@]./.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.6==#X@X@H@H@=#f#f.4' + +'.m.5.5.5.5.5.6.7.7.8.8.9.9.9.7.g=}=f=f=",'#13#10'": : _ ( ( ( ( ( ( ( / / /' + +' ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + + ' + +'+ n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' + +' $ $ $ $ $ % % % % % % % % % % % % % % % % % % % % % % % % % % % $ $ $ $ $ ' + +'$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+' + +' + + + + + + + + + + + . ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ ' + +'/ / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1' + +' 2 2 2 3 3 3 3 3 3 n=@*@*@*@*|*5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b ' + +'b b c c d d e e e e e e g g g g g g h i j W$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3' + +'$<$<$<$<$|$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y ' + +'y y z z z A B B B B B C C C D D M%O@N@N@N@ #j#G G G G G G H H H H H H I J J' + +' J J u+&&=@=@R+R+S+w+c.M M N N N N N O P P Q Q Q R R S S S T T T T U -%#%%+' + +'%+&+*+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,+''+''+(*&.&' + +'.*.*.*.=.=.-.-.-.-.;.;.;.;.;.,.,.''.''.,@u&!@>=J+J+K+K+K+K+K+ @.@.@.@.@]@]@' + +']@p%o=T@(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.|.1#H@H@H@=#=#n#p=m' + +'.5.5.5.5.5.6.7.8.8.8.9.9.9.+=7.q=f=f=",'#13#10'": : : _ ( ( ( ( ( ( ( / / /' + +' / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + ' + +'+ + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $' + +' $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ' + +'$ $ $ $ $ $ $ $ $ $ $ # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + +' + +' + + + + + + + + . . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / ' + +'/ ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2' + +' 2 2 3 3 3 3 3 4 A&@*@*@*~* *5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b ' + +'c c d d e e e e e e g g g g g g h h i i M$J$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<' + +'$<$<$>$r=q q q q r r s s s s t t t u u u v v v v v w w x x x x x x a.y y y ' + ,'y z z A A B B B B C C C C D L@s=N@N@N@O@#=F G G G G G i.H H H H H I I J J J' + +' ~==@=@=@R+R+S+v+M M M j.N N N N N O P Q Q Q R R R S S T T T T E*T+%+%+%+&+' + +'&+*+*+*+*+*+*+=+=+=+=+-+-+-+P.r$F+F+E+-&P.N.N.N.N.N.,+,+,+,+''+''+b%&.&.*.*' + +'.*.*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).r+t=&%K+K+K+K+ @.@.@.@{@]@]@' + +']@]@6@6@8@_=_.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.6==#=#=#=#=#=#Q' + +'#5.5.5.5.5.6.7.8.8.8.9.9.+=+=+=h=a=f=",'#13#10'": : : : _ ( ( ( ( ( ( ( / /' + +' / / ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . . + + + + + + + + ' + +'+ + + + + g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # $ $ $ $ $ $ $ $ $' + +' $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ' + +'$ $ $ $ $ $ $ # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.+ + + + + + +' + +' + + + + + + . . ~ ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ ^ / / / / ' + +'( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2' + +' 2 3 3 3 3 3 4 4*~*@*@*@*v&5 6 6 6 7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c ' + +'c c d e e e e e e f g g g g g g h i j W$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<' + +'$<$<$&=q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y ' + +'z z z A B B B B B C C C D m%s=N@N@N@O@m%F G G G G G G H H H H H H I I J J <' + +'@|@=@=@=@R+Q+t&M M M M N N N N N O O P Q Q Q R R S S S T T `.*&6+%+%+%+%+&+' + +'*+*+*+*+*+*+*+=+=+=+O.-&G+`& ...+.+.+.+.L&Q.N.N.N.l+,+,+,+,+''+''+v$&.&.*.*' + +'.*.=.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.).).).!.!.~.~.w@i@y*l@.@.@.@.@{@]@]@]' + +'@]@6@6@7@7@z@7%:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.;#u==#=#=#=#n#' + +'{&P#5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=a=",'#13#10'": : : : : _ ( ( ( ( ( ( ( ( ' + +'/ / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . + + + + + + + +' + +' + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # $ $ $ $ $ ' + +'$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' + +' $ $ # # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + ' + +'+ + + + + . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / ( ( (' + +' ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 ' + +'3 3 3 3 3 4 4 9*@*@*@*@*S&5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b b c c' + +' d d e e e e e e g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$' + +'<$~$o q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z' + +' z A A B B B B C C C C m% #.#N@N@O@q@b.F G G G G G i.H H H H H I I J N+Q+*@' + +'|@=@=@R+<@c.M M M M j.N N N N N O P Q Q Q R R R S S S `.*&5+5+6+%+%+%+%+&+*' + +'+*+*+*+*+*+*+Q&p*j$` ` ` . ...+.+.+.@.#...Q.N.N.l+,+,+,+,+''+''++*&.&.*.*.' + +'*.=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].h@p++@.@{@]@]@]@' + +']@6@6@7@7@7@m@n@0@<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.u#=#=#8#n#n' + +'#0=p#5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=",'#13#10'"< : : : : : _ ( ( ( ( ( ( ( (' + +' / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ ~ . + + + + + + ' + +'+ + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # # #' + +' $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # ' + +'# # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + +' + +' + + . ~ ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ' + +'( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 3 3' + +' 3 3 3 4 4 5 w&@*@*@*w&5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d ' + +'d e e e e e e g g g g g g g h i j x$J$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<' + +'$!$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z ' + +'z A B B B B B C C C j# #.#N@N@N@M%E F G G G G G G H H H H H H I I N+&@*@|@=' + +'@=@=@O+L M M M M M N N N N N N O P Q Q Q R R R S <=*&5+5+5+6+%+%+%+%+&+&+*+' + +'*+*+*+*+*+D+Z Z Z ` ` ` . .+.+.+.+.@.#.#.l*N.N.,+,+,+,+''+''+''+!+&.*.*.*.' + +'*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.H%x@G@]@]@]' + +'@6@6@6@7@7@m@m@m@m@^@a@[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.v==#8#n#' + +'n#n#R&e=6.7.8.8.8.9.9.9.+=+=4=4=4=4=",'#13#10'"< < : : : : : _ _ ( ( ( ( ( ' + +'( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + +' + +' + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # ' + +'# # # # # # # # # # # # # $ $ $ $ $ $ $ $ $ # # # # # # # # # # # # # # # #' + +' # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + ' + +'+ . . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( (' + +' ( ( _ _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 ' + +'3 3 3 4 4 5 !*@*@*@*@*9*6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 0 a a a b b c c d d' + +' e e e e e e f g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$' + +'|$q q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z' + +' A A B B B B B C C t% #.#.#N@N@~#E E F G G G G G G H H H H H I I u+R+*@*@|@' + +'=@=@l&L L M M M M M N N N N N O P P Q Q Q R R F.A$4+5+5+5+5+6+%+%+%+%+&+*+*' + +'+*+*+*+*+w=Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.l+l+,+,+,+,+''+''+!+!+(**.*.*.' + +'*.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.~.~.~.~.~.].].^.^.^.e./.T@o=p' + ,'%6@6@7@7@7@m@m@m@O*z@A@{#`*[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.**R&n#' + +'n#n#n#M#e=7.8.8.8.9.9.+=+=+=4=4=4=4=",'#13#10'"< < < : : : : : : _ ( ( ( ( ' + +'( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ . . + +' + +' + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ' + +'@ @ @ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @ @ @ @ @ @' + +' @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + + + . ' + +'. ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( (' + +' ( _ : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 ' + +'3 3 3 4 5 5 v&@*@*@*~*a&6 6 7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d' + +' e e e e e e g g g g g g h h i j x$J$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$' + +'*$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z' + +' A B B B B B C C t%D%.#.#N@N@~#E E b.G G G G G G i.H H H H H I l&*=*@*@*@=@' + +'+#%@L L M M M M M j.N N N N N O P Q Q Q R R g@-@4+4+5+5+5+6+6+%+%+%+%+&+*+*' + +'+*+*+*+A+Z Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.x=l+,+,+,+,+''+''+!+!+v$*.*.*.' + +'=.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e./././.].' + +'^@7@7@7@7@m@m@m@z@z@A@A@A@_=[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.p#f#n' + +'#n#n#M#R&y=8.8.8.9.9.+=+=+=4=4=4=4=",'#13#10'"[ < < < : : : : : : _ ( ( ( (' + +' ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ ~ . ' + +'. + + + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @' + +' @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ' + +'@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + + + . . ' + +' ~ ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ' + +'_ : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3' + +' 3 4 5 5 *~*@*@*@*/*6 6 7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b b c c d d e ' + +'e e e e e g g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$;$q' + +' q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A ' + +'A B B B B B C C 6#.#.#.#N@M@E E E F G G G G G G H H H H H H X%*@*@*@*@|@&&I' + +' K L L M M M M M N N N N N N O P Q Q Q O z=4+4+4+5+5+5+5+6+%+%+%+%+&+y+*+*+' + +'*+*+*+E.Z Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.A=,+,+,+,+''+''+''+!+!+U.*.*.*.' + +'=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././' + +'.(.!%V@7@7@m@m@m@z@A@A@A@A@A@X@V*}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.m.5.P#' + +'}=n#n#M#R&R&[=8.9.9.9.+=+=+=4=4=4=4=",'#13#10'"[ [ < < < : : : : : : _ ( ( ' + +'( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ~ ' + +' . . + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ' + +'@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @' + +' @ @ @ @ @ @ @ @ @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + . . ' + +' ~ ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _' + +' : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 ' + +'3 4 5 5 5 9*@*@*@*@*I&6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e' + +' e e e e f g g g g g g h i j j B=J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$_$' + +'q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A' + +' B B B B B C C >*.#.#.#N@d@E E E b.G G G G G G i.H H H H H R@}@*@*@*@*@s@I ' + +'K L L M M M M M j.N N N N N O P Q Q Q A$3+3+4+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*' + +'+*+*+*+C=Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.''+,+,+,+''+''+!+!+!+~+*.*.*' + +'.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.!.~.~.~.~.].].^.^.^.e././././.' + +'(.(._._._=Q*m@m@O*z@A@A@A@A@A@W@W@%#c=}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5' + +'.P#u#n#M#R&f#f#t#9.9.+=+=+=4=4=4=4=D=",'#13#10'"[ [ [ 0.< < < : : : : : _ _' + +' ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ' + +' . . + + + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ @ @ @ @' + +' @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ' + +'@ @ @ @ @ @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + + + . . ' + +' ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ _ ' + +': : : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3' + +' 4 5 5 5 5 q&@*@*@*[&6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b c c c d e e ' + +'e e e e e g g g g g g h h i j j M$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$K*q' + +' q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A ' + +'A B B B B B C i#)#.#.#.#u%E E E E F G G G G G G H H H H H (=}@}@*@*@*@Y%K K' + +' K L L M M M M M N N N N N N O P Q }+-@3+3+3+4+4+5+5+5+5+6+6+%+%+%+&+&+*+*+' + +'*+*+*+*+(%Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.d.+*,+,+,+''+''+!+!+!+~+w$*.=' + +'.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(' + +'.(._._._.L*z@m@O*z@A@A@A@A@A@W@W@W@9=V*|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.' + +'5.5.k=R&R&f#f#f#E=9.+=+=+=4=4=4=4=D=",'#13#10'"[ [ [ [ 0.< < < : : : : : : ' + +'_ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ' + +' . . + + + + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ ' + ,'@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @' + +' @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + + + + . . ' + +' ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ : : :' + +' : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 ' + +'4 5 5 5 *@*@*@*H&G&6 7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e' + +' e e e g g g g g g g h i j j W$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$,$o q ' + +'q q q r r s s s s s t t u u u u v v v v w w x x x x x x x y y y y z z z A B' + +' B B B B C Z@.#.#.#.#6#v%E E E b.G G G G G G i.H H H H #=}@}@*@*@*@<@c.K K ' + +'L L L M M M M j.N N N N N O P P |+k+k+3+3+4+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*' + +'+*+*+*+E+Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.G$,+,+''+''+''+!+!+~+~+u**.=' + +'.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.' + +'(.(._._._.:.a@z@z@z@A@A@A@A@A@W@W@W@W@W@1#1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5' + +'.5.5.6.o#R&f#f#0=0=8=+=+=+=4=4=4=4=D=",'#13#10'"[ [ [ [ [ 0.< < < : : : : :' + +' : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] ] { { { { ~ ~ ~ ~ ~ ' + +' . . . + + + + + + + + + + + + + + + + + + + + + + n.n.g.@' + +' @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.' + +'n.n.+ + + + + + + + + + + + + + + + + + + + + + . . . ' + +' ~ ~ ~ ~ ~ { { { { ] ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : ' + +': : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4' + +' 5 5 5 5 9*@*@*H&H&)&7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e ' + +'e e e g g g g g g g h i j j j Z$m$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$''$q ' + +'q q q q r s s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A' + +' B B B B B t%''#)#.#.# #}#E E E E F G G G G G G H H H H :@e@}@}@*@*@w+c.K K' + +' K L L M M M M M N N N N N N O `.x%k+k+3+3+3+4+4+4+5+5+5+F=T+#+%+%+%+&+*+*+' + +'*+*+*+*+*+B+Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.Z%,+,+''+''+!+!+!+~+~+5==' + +'.=.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.].].^.^.^.e././././.' + +'(.(._._._.:.:.:.!%z@A@A@A@A@A@W@W@W@W@W@*#*#8#;#1.f.2.2.3.3.4.4.4.4.4.m.5.5' + +'.5.5.5.6.7.Q#f#f#0=0=0=y=+=4=4=4=4=D=D=",'#13#10'"[ [ [ [ [ [ [ < < < : : :' + +' : : : _ _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ~ ~ ' + +' . . + + + + + + + + + + + + + + + + + + + + + + +' + +' + + + + n.n.n.g.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.g.n.n.n.+ + + + ' + +'+ + + + + + + + + + + + + + + + + + + + + + + . . ' + +' ~ ~ ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ _ : : : ' + +': : : < < < [ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4' + +' 5 5 5 5 5 .*@*H&H&2&7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e ' + +'e e e e g g g g g g h h i j j T$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$,$o q' + +' q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A ' + +'B B B B B C G=)#.#.#s=m%E E E E b.F G G G G G i.H H H ##P@}@}@*@*@<@J c.K K' + +' K L L M M M M M N N N N N O >@4+k+k+k+3+3+4+4+4+5+*&I*F.U V V h$#+&+&+*+*+' + +'*+*+*+*+=+,%Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.a%,+''+''+!+!+!+~+~+~+k' + +'.=.-.-.-.-.-.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(' + +'.(._._._.:.:.:.l.#@A@A@A@A@A@W@W@W@W@W@*#*#]#m#6=2.2.2.3.3.4.4.4.4.4.5.5.5.' + +'5.5.5.7.7.8.t#f#0=0=}=}=H=4=4=4=4=D=D=",'#13#10'"[ [ [ [ [ [ [ [ 0.< < < : ' + +': : : : : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ~ ~ ~' + +' ~ . . . + + + + + + + + + + + + + + + + + + + ' + +'+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +' + +' + + + + + + + + + + + + + + + + + + . . . ~ ~ ' + +'~ ~ ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : : : :' + +' < < < 0.[ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 ' + +'5 5 5 5 ;*@*H&H&H&3*7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c c d e e e e e' + +' e e g g g g g g h h i j j j I$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$''$q q' + +' q q q r s s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A ' + +'B B B B B >*)#)#.#.#C@E E E E E F G G G G G G H H H ##P@e@}@}@*@<@J c.K K K' + +' L L M M M M M j.N N N N N ;@j+k+k+k+3+3+3+4+4+C$H.T T T U V V V E.&+*+*+*+' + +'*+*+*+*+=+Q&Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.v$''+''+)+!+!+~+~+~+~+I' + +'=-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.' + +'(.(._._._.:.:.:.<.T@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#-=2.2.3.3.4.4.4.4.m.5.5.5' + +'.5.5.6.7.7.8.8.v#0=0=}=}=}=J=4=4=4=D=K=",'#13#10'"} [ [ [ [ [ [ [ [ 0.< < <' + +' : : : : : : _ _ ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ' + +'~ ~ ~ ~ . . . + + + + + + + + + + + + + + + +' + +' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + +'+ + + + + + + + + + + + + + + + . . . ~ ~ ~ ~' + +' ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( _ _ : : : : : : ' + +'< < < 0.[ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5' + ,' 5 5 5 5 |*H&H&H&w&5 7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e ' + +'e e g g g g g g g h i j j j k$m$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$~$o q q' + +' q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B ' + +'B B B B Z@)#)#.#.#$&E E E E E b.F G G G G G G H H l&W%e@}@}@*@Z*J J c.K K K' + +' L L M M M M M N N N N N f+i+j+k+k+k+3+3+3+(&<=T T T T U U V V V W *&*+*+*+' + +'*+*+*+=+=+=+=&` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.$.$.Z%''+''+!+!+!+~+~+~+{+)' + +'*-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.!.~.~.~.~.].].^.^.^.e././././.(.' + +'(._._._.:.:.:.:.<.L*A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@1#1.3.3.4.4.4.4.m.5.5.5' + +'.5.5.6.7.7.8.8.9.v#0=}=}=}=f=L=4=4=D=K=",'#13#10'"} } [ [ [ [ [ [ [ [ 0.< <' + +' < : : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { ' + +'{ ~ ~ ~ ~ ~ ~ . . . + + + + + + + + + + + +' + +' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + +'+ + + + + + + + + + + + . . . ~ ~ ~ ~ ~ ~ {' + +' { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : : < ' + +'< < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5' + +' 5 5 5 5 [&H&H&w&A&7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e ' + +'e f g g g g g g h i j j j j +%m$m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$''$q q ' + +'q q r r s s s s s t t t u u u v v v v v w x x x x x x x y y y y y z z A A B' + +' B B B y ''#)#)#.#D%D E E E E E F G G G G G G H H _@[@e@e@}@}@Q+J J J K K K' + +' L L M M M M M j.N N N z$g+i+j+j+k+k+k+3+3+A$S S T T T T U V V V V W U+*+*+' + +'*+*+*+*+=+=+=+E+` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.$.T.''+!+!+!+~+~+~+{+' + +']+-.-.-.-.;.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.' + +'(._._._.:.:.:.l.<.B@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@u=;#4.4.4.4.4.5.5.5.5' + +'.5.5.6.7.8.8.8.9.9.M=}=}=f=f=N=O=D=D=K=",'#13#10'"| } } [ [ [ [ [ [ [ [ [ <' + +' < < < : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] { { { ' + +'{ { { ~ ~ ~ ~ ~ ~ . . . . + + + + + + +' + +' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + +'+ + + + + + + . . . . ~ ~ ~ ~ ~ ~ { { {' + +' { { { ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : < < < ' + +'< [ [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5' + +' 5 5 5 X&H&H&w&w&S&7 8 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e ' + +'e g g g g g g h h i j j j g m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$~$o q q q' + +' q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B ' + +'B B B 6#)#)#.#s=m%E E E E E b.F G G G G G G H _@[@e@e@}@}@Q+N+J J c.K K K L' + +' L M M M M M N N N g@k+h+i+j+k+k+k+3+=%}+S S T T T T U U V V V V W -%*+*+*+' + +'*+*+*+=+=+=+D+` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.u*)+!+!+~+~+~+~+{+]+5' + +'%-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(.' + +'_._._.:.:.:.<.9@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#X@X@X@H@8#,&4.4.4.4.5.5.5.5.5' + +'.6.7.7.8.8.8.9.9.+=P=}=f=f=f=k=Q=K=K=",'#13#10'"| | } } } [ [ [ [ [ [ [ [ 0' + +'.< < < : : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] { ' + +'{ { { { { ~ ~ ~ ~ ~ ~ . . . . . + + +' + +' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + +'+ . . . . . ~ ~ ~ ~ ~ ~ { { { { { { ]' + +' ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : : < < < 0.[ ' + +'[ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5' + +' 5 6 |*H&H&w&w&5 7 8 9 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e e g ' + +'g g g g g g h i j j j j 4$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$''$q q q q ' + +'r r s s s s t t t u u u u v v v v w w x x x x x x x y y y y y z z A A B B B' + +' B (#)#)#)#.#R=D E E E E E F G G G G G G i.H +#e@e@e@}@R+N+J J J K K K L L ' + +'M M M M M j.N N K%Y%h+i+j+j+k+k+k+2+O S S S T T T T U V V V V W W V *+*+*+*' + +'+*+=+=+=+=+-&` ` . .+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.Y.!+!+!+~+~+~+{+{+]+X+' + +'-.-.-.;.;.;.;.>.,.''.''.''.''.).).!.!.!.~.~.~.~.].].^.^.^.e././././.(.(._._' + +'._.:.:.:.:.9@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@H@S=**4.4.m.5.5.5.5.5.' + +'6.7.7.8.8.9.9.9.+=7.o#f=f=f=f=g=T=K=",'#13#10'"| | | } } } [ [ [ [ [ [ [ [ ' + +'[ < < < < : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] ]' + +' { { { { { { ~ ~ ~ ~ ~ ~ ~ . . ' + +'. . . . . + + + + + + + + + + + + + + + + + + + + + + + + + . . . . . . . ' + +' ~ ~ ~ ~ ~ ~ ~ { { { { { { ] ] ] ' + +'^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : < < < < [ [ [ [' + +' [ [ [ [ [ } } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 ' + +'6 6 1*H&w&w&A&7 8 9 9 9 9 9 9 0 0 0 a a a a b b c c c d d e e e e e e g g g' + +' g g g g h i j j j j k l$m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$,$o q q q q r ' + +'s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B B B y' + +' .#)#)#.#$&D E E E E E b.F G G G G G G H <@e@e@e@e@=@%@J J J c.K K K L L M ' + ,'M M M M N N K%h+h+h+i+j+k+k+k+V&R R S S S T T T T U V V V V W X X .=*+*+*+*' + +'+=+=+=+-+O.` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.T.!+~+~+~+~+{+]+]+/+-.' + +'-.;.;.;.;.;.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.' + +':.:.:.^@z@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@=#=#S=U=4.m.5.5.5.5.5.6.7' + +'.8.8.8.9.9.9.+=+=h=k=f=f=1=1=V=K=",'#13#10'"| | | | | } } [ [ [ [ [ [ [ [ [' + +' 0.< < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ' + +'] { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ' + +' . . . . . . . . . . . . . . . . . . . . . ' + +' ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { ] ] ^ ^ ^ ^ ^' + +' ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < 0.[ [ [ [ [ [ ' + +'[ [ [ } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 6 ]' + +'&H&w&w&w&S&8 9 9 9 9 9 9 0 0 0 a a a a b b c c c d d e e e e e e g g g g g ' + +'g g h i j j j j j W=m$e$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$<$>$K*q q q q r s s s' + +' s s t t t u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B G=)#' + +')#)# #j#D E E E E E F G G G G G G i.(=e@e@e@e@*@S@J J J J K K K L L M M M M' + +' M j.N |+h+h+h+i+j+j+k+k+|+R R S S S T T T T U V V V V W W X X I.*+*+*+=+=+' + +'=+=+-+-+Q% . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.G$!+~+~+~+~+{+]+]+]+-.-.;' + +'.;.;.;.>.,.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.' + +':.|#z@z@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#X@X@X@H@H@=#=#=#=#2#5.5.5.5.5.5.7.7.8' + +'.8.8.9.9.+=+=+=4=h=N=f=1=1=N=X=",'#13#10'"| | | | | | } } } [ [ [ [ [ [ [ [' + +' [ < < < < : : : : : : : _ ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ' + +'] ] ] { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ' + +' ' + +' ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^' + +' ^ / / / / / ( ( ( ( ( ( ( ( ( ( _ : : : : : : : < < < < [ [ [ [ [ [ [ [ [ ' + +'} } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 6 6 G&w' + +'&w&w&w&Y=9 9 9 9 9 9 0 0 0 a a a a b b b c c d d e e e e e e f g g g g g g ' + +'h h i j j j j k +%m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$-$q q q r r s s s s' + +' s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B (#)#)#)#' + +'.#M%D E E E E E b.F G G G G G G ##e@e@e@e@e@X%I J J J c.K K K L L M M M M M' + +' N |+h+h+h+h+i+j+k+k+g@R R R S S S T T T T U V V V V W X X X -%*+*+*+=+=+=+' + +'=+-+-+(% . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.Y.!+~+~+~+{+{+]+]+]+b+-.;.;' + +'.;.;.>.,.''.''.''.).).).!.!.~.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.a@&#m' + +'@z@A@A@A@A@A@W@W@W@W@W@*#=#l#l#^#]#X@X@X@H@H@=#=#=#=#8#Q#5.5.5.5.6.7.7.8.8.' + +'8.9.9.+=+=+=4=4=O=Z=1=N=N=N=",'#13#10'"1 | | | | | | } } } [ [ [ [ [ [ [ [ ' + +'[ 0.< < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^' + +' ^ ] ] ] { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ' + +' ' + +' ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / ' + +'/ / / ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < 0.[ [ [ [ [ [ [ [ [ } } }' + +' | | | | | | 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 5 5 5 5 5 5 5 5 6 6 6 6 2&w&w&' + +'w&/*9 9 9 9 9 9 0 0 0 a a a a b b b c c d d e e e e e e e g g g g g g g h i' + +' j j j j k h m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$;$q q q q r s s s s s t ' + +'t t u u u v v v v v w w x x x x x x x y y y y z z z A A B B B y .#)#)#)#`@D' + +' D E E E E E F G G G G G G J%[@e@e@e@e@P+I J J J J K K K L L L M M M M M |+' + +'h+h+h+h+i+i+j+D*z$Q R R S S S T T T T U U V V V W W X X X V *+*+*+=+=+=+-+-' + +'+-+`= ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.k.&.&.T.~+~+~+{+]+]+]+]+Z+;.;.;.;.' + +'>.,.,.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.a@n@m@O*z@' + +'A@A@A@A@A@W@W@W@W@H@J@|.1.1.1.0#1#X@H@H@=#=#=#=#=#8#n# -5.5.5.6.7.7.8.8.9.9' + +'.9.+=+=+=4=4=4=.-1=N=N=N=",'#13#10'"1 1 | | | | | | | } } [ [ [ [ [ [ [ [ [' + +' [ < < < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( / / / / / / ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ] ] ] ] { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ' + +' ' + +'~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / (' + +' ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < < [ [ [ [ [ [ [ [ [ [ } } | | | ' + +'| | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 5 5 5 5 5 5 5 5 6 6 6 6 a&w&w&w&n&+' + +'-9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h i j j ' + +'j j k k B=e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$>$*$q q q r s s s s s t t t u' + +' u u u v v v v w w x x x x x x x a.y y y y z z A A B B B B D%)#)#)# #}#D E ' + +'E E E E b.G G G G G G G Q+e@e@e@e@+#I I J J J c.K K K L L M M M M M 7&h+h+h' + +'+h+h+i+j+D*z$Q R R R S S S T T T T U V V V V W r.X X X X .=*+=+=+=+=+-+-+-+' + +'r$ .+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.u*~+~+{+{+]+]+]+]+8+;.;.;.;.>.,' + +'.''.''.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._.(@n@m@m@z@z@A@' + +'A@A@A@A@W@W@W@^#}.|.1.1.1.1.2.2.-=H@H@=#=#=#=#=#n#n#n#Q#5.5.7.7.8.8.8.9.9.+' + ,'=+=+=4=4=4=4=D=@-N=N=N=",'#13#10'"1 1 1 1 | | | | | | } } } [ [ [ [ [ [ [ [' + +' [ 0.< < < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ ^ ] ] ] ] { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ' + +' ~ ~ ~ ~ ~ ~ ~ ' + +'~ ~ ~ ~ ~ { { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( (' + +' ( ( ( ( ( ( _ _ : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ } } } | | | | | ' + +'| 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 3*w&w&n&[&x&9' + +' 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h i j j j j ' + +'j k k {%e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$|$q q q r r s s s s s t t t u u' + +' u v v v v v w w x x x x x x x y y y y z z z A A B B B k&)#)#)#)##-D D E E ' + +'E E E F G G G G G G (=e@e@e@e@[@%@I J J J J c.K K L L L M M M M t&h+h+h+h+h' + +'+h+i+D*}+Q Q R R R S S T T T T U U V V V V W X X X X X w**+=+=+=+-+-+-+;+P.' + +'..+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.&.Y.~+~+{+{+]+]+]+]+]+,@;.;.;.>.,.' + +'''.''.''.).).).!.!.~.~.~.~.~.].].^.^.^.e././././.(.(._._.(@A@m@m@m@z@A@A@A@' + +'A@A@W@W@W@7%}.|.|.1.1.1.1.2.2.2./=H@=#=#=#=#8#n#n#n#n#[=6.7.7.8.8.8.9.9.+=+' + +'=+=4=4=4=4=D=K=$-N=N=",'#13#10'"1 1 1 1 1 | | | | | | } } } [ [ [ [ [ [ [ [' + +' [ [ 0.< < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / / ^ ^ ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ] ] ] ] { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~' + +' ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { ' + +'{ { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / ( ( ( ( ( ( ( (' + +' ( ( ( _ _ : : : : : : : : < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 ' + +'1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 7 A&w&n&[&%-9 9 9' + +' 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h h i j j j j k ' + +'k W=e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$!$q q q r s s s s s t t t u u u v' + +' v v v v w w x x x x x x x y y y y y z z A A B B B t%s#)#)#)#$&D D E E E E ' + +'E b.G G G G G G v%e@e@e@e@P@##I I J J J c.K K K L L M M M M v@Z*h+h+h+h+h+i' + +'+D*N Q Q Q R R S S S T T T T U V V V V W W X X X X X E.=+=+=+=+-+-+-+;+;+t$' + +'+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.T.~+{+]+]+]+]+]+7+S$;.;.>.,.''.''' + +'.''.''.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._.L*z@m@m@m@O*z@A@A@A@A@' + +'A@W@W@&-}.}.|.|.1.1.1.f.2.2.3.3.-==#=#=#=#8#n#n#n#n#n#*-7.7.8.8.9.9.9.+=+=+' + +'=4=4=4=4=D=K=T=q=N=",'#13#10'"2 2 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [ [' + +' [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / / / ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] { { { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~' + +' ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { ' + +'{ { { { ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( (' + +' _ _ : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 ' + +'1 2 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 Y=w&n&[&[&N&9 9 9 0' + +' 0 0 0 a a a a b b c c c d d e e e e e e f g g g g g g h h i j j j j k k k ' + +'a$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$>$~$=-q q r s s s s s t t t u u u u v v' + +' v v v w x x x x x x x x y y y y z z z A B B B B 5&)#)#)#s#j#D D E E E E E ' + +'F G G G G G G [@e@e@e@e@(=I I J J J J c.K K L L L M M M c./&h+h+h+h+h+h+k+>' + +'@Q Q Q R R R S S S T T T U U V V V V W X X X X X Y ;%=+=+=+-+-+-+;+;+;+Q%+.' + +'+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.&.*.*.v${+{+]+]+]+]+]+7+~@;.;.>.,.''.''.' + +'''.''.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(.N*g%7@m@m@m@z@z@A@A@A@A@A' + +'@9=/#}.}.}.|.1.1.1.1.2.2.2.3.3.4.9#=#=#=#n#n#n#n#n#n#M#--8.8.8.9.9.+=+=+=4=' + +'4=4=4=D=D=K=K=;-N=",'#13#10'"2 2 2 1 1 1 1 1 | | | | | | } } } [ [ [ [ [ [ ' + +'[ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / ^' + +' ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] { { { { { { { { { { { { { { { { ~ ~ ~ ~ ' + +'~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { { { { { { { { ]' + +' ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( _ _ ' + +': : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 1 2' + +' 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 ;*n&[&[&[&)&9 9 0 0 ' + +'0 0 a a a a b b c c c d d e e e e e e f g g g g g g g h i j j j j k k k 0$l' + +'$e$e$5$5$5$5$5$6$3$3$3$<$<$<$<$>$>$>$K*q q r r s s s s s t t t u u u v v v ' + +'v v w w x x x x x x x y y y y z z z A A B B B Z@5#)#)#)#C@D D E E E E E b.G' + +' G G G G G c@e@e@e@e@Q@I I I J J J c.K K K L L M M M ^%]*x+h+h+h+h+h+k+>@P ' + +'Q Q Q R R S S S T T T T U V V V V W W X X X X X Y Z Q&=+=+-+-+-+;+;+>+F++.+' + +'.+.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.*.*.*.Y.{+]+]+]+]+]+7+7+7+,@;.,.,.''.''.''' + +'.).).).!.!.~.~.~.~.~.].^.^.^.^.e././././.(.(.N*g%7@m@m@m@m@z@A@A@A@A@A@X@J@' + +'}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.-#=#8#n#n#n#n#n#M#R&}*P#8.8.9.9.+=+=+=4=4=4' + +'=4=D=K=K=K=K=>-",'#13#10'"2 2 2 2 1 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [' + +' [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / ' + +'/ / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] { { { { { { { { { { { { { { { { {' + +' { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ^ ' + ,'^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : :' + +' : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 2 2 2 ' + +'2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7 ,-[&[&[&2&9 9 0 0 0 0 a' + +' a a a b b c c c d d e e e e e e e g g g g g g g h i j j j j k k k l c$e$e$' + +'5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$_$q q r s s s s s t t t u u u v v v v v w' + +' w x x x x x x x a.y y y y z z A A B B B B D%)#)#)#D%D D E E E E E E F G G ' + +'G G G b@e@e@e@e@[@H I I J J J J K K K L L L M M M <*S+h+h+h+h+h+h+g@P Q Q Q' + +' R R R S S S T T T T U V V V V W X X X X X Y Y Z ,%=+=+-+-+-+;+;+O.F$+.+.@.' + +'#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.]+]+]+]+]+]+7+7+7+b+>.,.''.''.''.''.)' + +'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.S*g%7@7@m@m@m@O*z@A@A@A@A@^@c=}.}.' + +'}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.e#8#n#n#n#n#n#M#R&R&u#5.9.9.9.+=+=+=4=4=4=4' + +'=D=K=K=K=K=K=",'#13#10'"3 2 2 2 2 2 1 1 1 1 1 | | | | | | } } } } [ [ [ [ [' + +' [ [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / ' + +'/ / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] ] ] { { { { { { { { { {' + +' { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : :' + +' : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } } | | | | | | 1 1 1 1 1 2 2 2 2 2 3 ' + +'3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7 8 %-[&[&[&T&9 0 0 0 0 a a a' + +' a b b c c c d d e e e e e e e g g g g g g g h i j j j j j k k k l b=e$5$5$' + +'5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$[$q q r s s s s s t t t u u u u v v v v v w w' + +' x x x x x x x y y y y z z z A A B B B ''-)#)#)#)#L@D D E E E E E F G G G G' + +' G E J&e@e@e@e@##I I J J J J c.K K K L L M M M K&S+x+h+h+h+h+h+g@P P Q Q Q ' + +'R R S S S T T T T U V V V V W W X X X X X Y Y Z >%=+-+-+-+;+;+>+O.L.+.+.#.#' + +'.#.#.#.d.$.$.$.$.%.%.%.&.&.&.*.*.*.=.)*]+]+]+]+]+7+7+7+`+>.,.''.''.''.).).)' + +'.!.!.~.~.~.~.~.].].^.^.^.e././././.(./@7@7@7@7@m@m@m@z@z@A@A@A@U@[.}.}.}.}.' + +'}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.u#n#n#n#n#n#M#R&f#f#9#9.9.+=+=+=4=4=4=4=D=D' + +'=K=K=K=K=K=",'#13#10'"3 3 3 2 2 2 2 1 1 1 1 1 | | | | | | | } } } [ [ [ [ [' + +' [ [ [ [ [ [ 0.< < < < : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( / ' + +'/ / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] ] ] ] ] ] ]' + +' ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : < <' + +' < < 0.[ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 ' + +'3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 8 8 x&[&[&[&[&+-0 0 0 0 a a a a b' + +' b c c c d d e e e e e e e g g g g g g g h i i j j j j k k k l i e$e$5$5$5$' + +'5$6$6$3$3$3$<$<$<$<$>$>$>$>$!$q r r s s s s s t t t u u u v v v v v w w x x' + +' x x x x x y y y y y z z A A B B B B p&)#)#)#$&D D E E E E E b.F G G G G G ' + +'~#e@e@e@e@;=H I I J J J J K K K L L L M M t&S+S+h+h+h+h+h+7&O P Q Q Q R R R' + +' S S S T T T T U V V V V W X X X X X X Y Z Z =&=+-+-+-+;+;+O.O.O.^*@.#.#.#.' + +'#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.*.=.b+]+]+]+]+7+7+7+7+8+,.,.''.''.''.).).).!.' + +'!.~.~.~.~.{.].^.^.^.^.e././././.)-7@7@7@7@m@m@m@O*z@A@A@A@7%[.}.}.}.}.}.|.|' + +'.1.1.1.1.2.2.2.3.3.4.4.4.4.m.p#n#n#n#n#M#R&R&f#f#f#8=9.+=+=+=4=4=4=4=D=D=K=' + +'K=K=K=K=",'#13#10'"3 3 3 3 2 2 2 2 1 1 1 1 1 1 | | | | | | | } } } [ [ [ [ ' + +'[ [ [ [ [ [ [ 0.< < < < : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( (' + +' / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / /' + +' / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : < < < < 0.' + +'[ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3' + +' 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 N%[&[&[&[& &0 0 0 a a a a b b c ' + +'c c d d e e e e e e e g g g g g g g h h i j j j j k k k l l !-e$5$5$5$5$6$6' + +'$3$3$3$<$<$<$<$>$>$>$>$~$=-r r s s s s s t t t u u u v v v v v w w x x x x ' + +'x x x a.y y y y z z z A B B B B (#)#)#)#.#j#D D E E E E E F G G G G G }#.&e' + +'@e@e@d@H I I J J J J c.K K K L L M M c./&S+x+h+h+h+h+1+O P P Q Q Q R R S S ' + +'S T T T T U V V V V W W X X X X X Y Y Z Z Z O.-+-+;+;+>+O.O.O.Q%#.#.#.#.#.#' + +'.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.~-]+]+]+7+7+7+7+8+5%''.''.''.''.).).).!.!.' + +'~.~.~.~.{.].^.^.^.^././././.x@6@6@7@7@7@m@m@m@O*z@A@n@P*[.[.}.}.}.}.}.|.|.1' + +'.1.1.f.2.2.3.3.4.4.4.4.4.m.5.}=n#n#n#M#R&R&f#f#0=0={-+=+=+=4=4=4=4=D=K=K=K=' + +'K=K=]-",'#13#10'"3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | } } } } [ [ [ ' + +'[ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / /' + +' / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : : < < < < 0.[ [ [ ' + +'[ [ [ [ [ [ [ [ } } } } | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 5' + +' 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 T&[&[&[&#&0 0 0 a a a a b b c c c ' + +'d d e e e e e e e g g g g g g g h h i j j j j k k k l l l ^-5$5$5$5$5$6$6$3' + ,'$3$<$<$<$<$>$>$>$>$>$&=q r s s s s s t t t u u u u v v v v v w w x x x x x ' + +'x x y y y y z z z A A B B B B s#)#)#)#,#D D E E E E E b.F G G G G G u%e@e@e' + +'@e@##H I I J J J J K K K L L L M M w+S+S+h+h+h+h+x%N O P Q Q Q R R R S S S ' + +'T T T T U V V V V W X X X X X X Y Z Z Z Z -&-+-+;+;+>+O.O.O.s$#.#.#.#.#.d.$' + +'.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.`+]+]+7+7+7+7+7+9+!@''.''.''.).).).!.!.~.~.~' + +'.~.~.].].^.^.^.e./././.==6@6@6@7@7@m@m@m@m@z@A@n@a@[.[.[.}.}.}.}.}.|.1.1.1.' + +'1.2.2.2.3.3.4.4.4.4.4.5.5.9#n#n#n#M#R&f#f#f#0=0=f=2*+=4=4=4=4=D=D=K=K=K=K=K' + +'=]-",'#13#10'"3 3 3 3 3 3 3 2 2 2 2 1 1 1 1 1 1 | | | | | | | } } } } [ [ [' + +' [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ' + +'( ( ( ( ( ( / / / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^' + +' ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / / / / ( ( ( ( ' + +'( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [' + +' [ [ [ [ } } } } | | | | | | | 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 ' + +'5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 o&[&[&!&/-0 0 a a a a b b c c c d d e' + +' e e e e e e g g g g g g g h h i j j j j k k k l l l i 5$5$5$5$5$6$6$3$3$<$' + +'<$<$<$>$>$>$>$>$>$*$r s s s s s s t t t u u u v v v v v w w x x x x x x x y' + +' y y y y z z A A B B B B (#)#)#)#''#}#D D E E E E E F G G G G G q@.&e@e@e@;' + +'=H I I J J J J c.K K K L L M M f+S+S+x+h+h+h+=%N O P P Q Q Q R R S S S T T ' + +'T T U U V V V V W X X X X X Y Y Z Z Z ` G+-+;+;+>+O.O.O.O.F%#.#.#.#.#.$.$.$' + +'.$.$.%.%.&.&.&.*.*.*.=.=.-.-.%%]+]+7+7+7+7+8+9+0+;.''.''.).).).!.!.~.~.~.~.' + +'{.].^.^.^.^.e././.p+]@6@6@7@7@7@m@m@m@O*z@|#:.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2' + +'.2.2.3.3.4.4.4.4.m.5.5.9#n#n#M#R&R&f#f#f#0=0=}=g=+=4=4=4=4=D=D=K=K=K=K=K=(-' + +'",'#13#10'"4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | | } } } [ [ [ ' + +'[ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ' + +'^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ ' + +'[ [ } } } | | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5' + +' 5 5 5 6 6 6 7 7 7 7 7 8 8 9 9 9 x&[&[&!&!&y%0 a a a a b b c c c d d e e e ' + +'e e e e g g g g g g g h h i j j j j k k k l l l l g$5$5$5$5$6$6$3$3$3$<$<$<' + +'$>$>$>$>$>$>$~$r r s s s s s t t t u u u v v v v v w w x x x x x x x x y y ' + +'y y z z z A A B B B B ''#)#)#)#C@D D E E E E E b.G G G G G E h*e@e@e@[@_@H ' + +'I I J J J J K K K L L M M |%S+S+S+h+h+h+Y%z$N O P Q Q Q R R R S S S T T T T' + +' U V V V V W r.X X X X X Y Z Z Z Z ` (%-+;+;+>+O.O.O.O.Q.#.#.#.#.d.$.$.$.$.' + +'%.%.%.&.&.*.*.*.*.=.=.-.-.-.]+]+7+7+7+7+8+9+0+u&''.''.).).!.!.!.~.~.~.~.{.]' + +'.^.^.^.^./.].G@]@]@6@6@7@7@7@m@m@m@z@9@:.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.' + +'3.3.4.4.4.4.4.m.5.5.}=n#n#M#R&R&f#f#0=0=0=}=}=v#4=4=4=4=D=K=K=K=K=K=]-(-",' + +#13#10'"5 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | | } } } [ [ [ ' + +'[ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / / / / / / / / / ' + +'/ / / / / / / / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( _ _ _ : : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ } } ' + +'} | | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6' + +' 6 6 6 7 7 7 7 7 8 8 9 9 9 9 I&[&!&!&!& &a a a a b b c c c d d e e e e e e ' + +'e g g g g g g g g h i j j j j j k k k l l l m n$5$5$5$6$6$3$3$3$<$<$<$<$>$>' + +'$>$>$>$>$_-r s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y z ' + +'z z A A B B B B (#)#)#)# #D D E E E E E E F G G G G G M@e@e@e@e@:@H I I J J' + +' J J c.K K K L L M c./&S+S+x+h+h+h+|+N O P P Q Q Q R R S S S T T T T U U V ' + +'V V V W X X X X X Y Y Z Z Z ` ` ` >+;+;+O.O.O.O.P.N.#.#.#.#.d.$.$.$.$.%.%.&' + +'.&.&.*.*.*.*.=.-.-.-.-.b%7+7+7+7+8+9+9+0+(+''.).).).!.!.~.~.~.~.{.].^.^.^.^' + +'.e.].L+]@]@6@6@7@7@7@m@m@m@m@U@[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.' + +'4.4.4.4.5.5.p#n#n#n#M#R&f#f#f#0=0=}=}=}=f=.-4=4=D=D=K=K=K=K=K=]-(-",'#13#10 + +'"5 5 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / / / / ' + +'/ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ :' + +' : : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | ' + +'| | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7' + +' 7 7 7 8 8 9 9 9 9 9 9 G&!&!&!&T%a a a a b b c c c d d e e e e e e e g g g ' + +'g g g g g h i j j j j j k k k l l l l :-5$5$5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>' + +'$>$-$s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y y z z A A ' + +'B B B B B ''#)#)#)#C@D D E E E E E F G G G G G v%.&e@e@e@+#H H I I J J J c.' + +'K K K L L M M <*S+S+S+h+h+h+x%N N O P Q Q Q R R R S S S T T T T U V V V V W' + +' r.X X X X X Y Z Z Z Z ` ` ` M.;+>+O.O.O.O.N.N.L&#.#.d.$.$.$.$.%.%.%.&.&.&.' + ,'*.*.*.=.=.-.-.-.-.%%7+7+7+7+8+9+0+0+0+;.).).).!.!.~.~.~.~.{.].^.^.^.^.z*p%]' + +'@]@]@6@6@7@7@7@m@m@m@<-<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.' + +'m.5.P#}=n#n#M#R&R&f#f#f#0=0=}=}=}=f=k=h=4=D=D=K=K=K=K=K=(-(-",'#13#10'"5 5 ' + +'5 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ [' + +' [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ' + +'( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : ' + +': : : : : : < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | 1' + +' 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 ' + +'8 8 9 9 9 9 9 9 9 %-!&!&U%[-a a b b b c c c d d e e e e e e e g g g g g g g' + +' g h i j j j j j k k k l l l l m b$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$>$>$.$s ' + +'s s s s s t t t u u u v v v v v w w x x x x x x x x y y y y z z z A A B B B' + +' B (#)#)#)# #D D E E E E E b.F G G G G G }-e@e@e@e@##H I I J J J J c.K K K ' + +'L L M 7&S+S+S+h+h+h+D*N N O P P Q Q Q R R S S S T T T T U U V V V V W X X X' + +' X X Y Y Z Z Z ` ` ` ` r$;+O.O.O.O.P.N.N.l*#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.*.' + +'=.-.-.-.-.-.,@7+7+7+8+9+9+0+0+0+5@).).!.!.~.~.~.~.~.].].^.^.^.l=p%]@]@]@6@6' + +'@6@7@7@7@m@Q*0@<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.m.5.{&' + +'n#n#n#M#R&R&f#f#0=0=0=}=}=}=f=f=|-4=D=K=K=K=K=K=]-(-(-",'#13#10'"5 5 5 5 5 ' + +'4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 | | | | | | | | | } } } [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : : _ _ _ _ ( ( ( ( ( ' + +'( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ : : : : : : : : : : : : : ' + +'< < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | | 1 1 1 1 1' + +' 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 ' + +'9 9 9 9 0 e&!&!&U%U%1-a b b b c c c d d e e e e e e e g g g g g g g g h i j' + +' j j j j k k k l l l l m m b*5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$>$>$,$_-s s s s ' + +'s t t t u u u v v v v v w w w x x x x x x x y y y y y z z A A B B B B B #)' + +'#)#.#C@D D E E E E E F G G G G G b@.&e@e@e@Q@H I I I J J J c.K K K L L M M ' + +']*S+S+x+h+h+h+g@N N O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y' + +' Z Z Z Z ` ` ` .U*>+O.O.O.O.N.N.N.x=#.d.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.' + +'-.-.-.;.g&7+7+8+9+0+0+0+0+m+).).!.!.~.~.~.~.{.].^.^.^.p+{@]@]@]@]@6@6@7@7@7' + +'@m@z@L*l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.9#n#n#n#n#' + +'R&R&f#f#f#0=0=}=}=}=f=f=f=f=2-D=K=K=K=K=K=]-(-(-",'#13#10'"5 5 5 5 5 5 4 4 ' + +'4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : : : : _ _ _ _ ( ( ( ' + +'( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (' + +' ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ : : : : : : : : : : : : : : : < < < < < < ' + +'0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2' + +' 2 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 ' + +'0 0 3-!&U%U%U%S%b b b c c c d d e e e e e e e g g g g g g g g h i j j j j j' + +' k k k l l l l m m m 8$5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$,$,$o*s s s s t t t ' + +'u u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B B c*)#)#)# #D' + +' D E E E E E b.G G G G G G u%e@e@e@e@_@H I I J J J J c.K K K L L M v+S+S+S+' + +'h+h+h+x%N N O P P Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z' + +' Z ` ` ` .''*O.O.O.O.P.N.N.N.4-#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.;' + +'.;.~@7+7+8+9+0+0+0+m+m+M+!.!.!.~.~.~.~.{.].^.~.y*.@]@]@]@]@]@6@6@7@7@7@n@a@' + +':.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.m.o#n#n#n#n#M#R&R&f' + +'#f#f#0=0=}=}=}=f=f=f=f=g=D=K=K=K=K=K=(-(-(-",'#13#10'"5 5 5 5 5 5 5 5 4 4 3' + +' 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ 0.0.< < < < < < < : : : : : : : : : : : : : : : : _ _ _ _ _' + +' _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ' + +'( ( ( _ _ _ _ _ _ : : : : : : : : : : : : : : : : < < < < < < < 0.0.[ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 ' + +'3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 O' + +'&U%U%U%%-b b b c c c d d e e e e e e e g g g g g g g g h i i j j j j k k k ' + +'l l l l m m m g*5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$.$s s s s t t t t u u u' + +' v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B _#)#)#.#5-D E E ' + +'E E E b.F G G G G G b@.&e@e@e@;=H I I J J J J c.K K K L L M t&S+S+S+x+h+h+Y' + +'%N N O O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ' + +'` . ...6-O.O.O.N.N.N.N.N.#.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.3@7+7' + +'+8+9+0+0+0+0+m+m+f%!.!.~.~.~.~.{.].^.h@$=.@{@]@]@]@]@6@6@7@7@7@|#:.:.:.<.[.' + +'[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4. -n#n#n#n#n#M#R&f#f#f#0=0' + +'=0=}=}=}=f=f=f=f=1=@-K=K=K=K=]-(-(-(-",'#13#10'"5 5 5 5 5 5 5 5 5 4 4 4 3 3' + +' 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [ [ ' + ,'[ [ [ [ [ [ [ [ [ 0.< < < < < < < : : : : : : : : : : : : : : : : : : : : _' + +' _ _ _ _ _ _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ _ _ _ _ _ : ' + +': : : : : : : : : : : : : : : : : : : < < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [' + +' [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 ' + +'4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 7-U%U%U' + +'%i%b b c c c d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l ' + +'m m m m 8-6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$$${$s s s s t t t u u u v v v v' + +' v w w x x x x x x x x y y y y z z z A A B B B B B Z@)#)#)#s=j#D E E E E E ' + +'F G G G G G G }-e@e@e@P@_@H I I J J J J K K K L L L M ]*S+S+S+h+h+h+1+N N O' + +' P Q Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .' + +'+.-&O.O.P.N.N.N.N.N.t$$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.-.;.Y+7+7+8+9+0' + +'+0+0+0+m+m+m+t=!.~.~.~.~.{.].p+.@.@.@{@]@]@]@]@6@6@7@7@#@:.:.:.l.<.[.[.[.[.' + +'[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.4.4.d=n#n#n#n#n#n#R&R&f#f#f#0=0=}=}=}' + +'=f=f=f=f=1=1=N=T=K=K=K=]-(-(-(-",'#13#10'"6 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3' + +' 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | | } } } } [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < : : : : : : : : : : : : : : : : : : :' + +' : : : : : : : : _ _ _ _ _ _ _ _ _ _ _ _ _ : : : : : : : : : : : : : : : : ' + +': : : : : : : : : : : < < < < < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ }' + +' } } } | | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 5 5 ' + +'5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a U%U%U%I%9-b c' + +' c d d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m ' + +'m a$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$0-s s s t t t u u u v v v v v v w w' + +' x x x x x x x y y y y y z z A A B B B B B C y#)#)#.#,#D E E E E E b.F G G ' + +'G G G b@e@e@e@e@;=H I I J J J J c.K K K L L M K&S+S+S+x+h+h+h+z$N O P P Q Q' + +' Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.F$O.' + +'O.N.N.N.N.N.N.v$$.$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.-.;.<+7+7+8+9+9+0+0+0+m' + +'+m+m+J+K+w@~.~.~.~.$@p%.@.@.@{@]@]@]@]@6@6@6@7@o@_.:.:.:.<.<.[.[.[.[.[.}.}.' + +'}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.2#8#n#n#n#n#n#M#R&R&f#f#f#0=f=}=}=}=f=f=f' + +'=f=1=N=N=$-K=K=K=(-(-(-(-",'#13#10'"6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3' + +' 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | } } } } } [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ 0.< < < < < < < < < : : : : : : : : : : : : : : : : : :' + +' : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : ' + +': : : : < < < < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } | |' + +' | | | | | | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 ' + +'5 5 5 6 6 6 6 7 7 7 7 7 8 8 8 9 9 9 9 9 9 0 0 0 0 a a S%U%U%I%I%[%c c d d d' + +' e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m m n 8$3$' + +'3$3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$.$s s s t t t u u u u v v v v v w w x x x x' + +' x x x a.y y y y z z z A A B B B B B t%.#)#.#.#j#E E E E E E F G G G G G G ' + +'}-e@e@e@W%_@H I I J J J c.K K K L L M c./&S+S+S+h+h+h+f+N N O P Q Q Q R R R' + +' S S S T T T T U U V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.F+O.P.N.N.' + +'N.N.N.l++*$.$.$.%.%.%.&.&.*.*.*.*.=.=.-.-.-.-.-.<+K$7+7+8+9+0+0+0+0+m+m+m+J' + +'+J+&%w@~.w@a- @.@.@.@.@{@]@]@]@]@6@6@g%0@_.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.' + +'|.1.1.1.1.2.2.2.3.3.4.4.4.**S=n#n#n#n#n#n#M#R&f#f#f#f=j=9.6.[*}=f=f=f=f=1=N' + +'=N=N=b-K=]-(-(-(-(-",'#13#10'"6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3' + +' 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | } } } } [ [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < < < : : : : : : : : : : : : : : :' + +' : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : < < < < < ' + +'< < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | |' + +' | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 ' + +'6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a a a 1-I%I%I%z%0 c d d e e e e e' + +' e e e g g g g g g g g h i i j j j j k k k l l l l m m m m m m g*6$3$3$<$<$' + +'<$<$>$>$>$>$>$>$>$,$,$$$$$K*s s t t t u u u u v v v v v w w x x x x x x x x' + +' y y y y z z z A A B B B B B C i#)#)#.#D@D E E E E E F G G G G G G }#e@e@e@' + +'e@(=H I I J J J J c.K K K L L M v+S+S+S+x+h+h+h+7&N O P P Q Q Q R R R S S S' + +' T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.+.Q%O.N.N.N.N.N.N.' + +',+!+$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.-.S$7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K' + +'++@K+K+ @.@.@.@.@]@]@]@]@]@6@7@L*_._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.' + +'1.f.2.2.3.3.3.4.4.p=S=8#n#n#n#n#n#M#R&R&f#f#}=j=9.+=+=+=O=N=f=f=1=1=N=N=N=V' + +'=K=c-(-(-(-(-",'#13#10'"7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3' + +' 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | } } } } } [ [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < < < < < < < : : : : : : : : :' + +' : : : : : : : : : : : : : : : : : : : : < < < < < < < < < < < < < < 0.0.[ ' + +'[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } | | | | | | | | | | 1 1 1' + +' 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 ' + ,'7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a a a a 9-I%I%z%d-c d d e e e e e e e e g' + +' g g g g g g g h i i j j j j k k k k l l l l m m m m m n g$3$3$<$<$<$<$>$>$' + +'>$>$>$>$>$>$,$$$$$$$ $s t t t t u u u v v v v v w w x x x x x x x x y y y y' + +' y z z A A B B B B B C C ''#)#.#.#M%E E E E E b.F G G G G G G Q@e@e@e@W%H I' + +' I J J J J c.K K K L L M c.S+S+S+S+h+h+h+h+K%N O P Q Q Q R R R S S S T T T ' + +'T U V V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.+.` P.N.N.N.N.N.l+,+,+I' + +'+$.%.%.%.&.&.&.*.*.*.=.=.-.-.5%Z+5=7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+' + +' @.@.@.@.@{@]@]@]@]@6@6@e-_._.:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2' + +'.2.3.3.4.4.0#n#=#8#n#n#n#n#n#M#R&R&f#}=j=9.9.+=+=+=4=O=Z=f=1=N=N=N=N=N=2-(-' + +'(-(-(-(-",'#13#10'"7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 ' + +'3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } [ [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.< < < < < < < < < < < < < < < < < < < ' + +'< < < < < < < < < < < < < < < < < < < < < < < < < < 0.0.0.[ [ [ [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 ' + +'2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8' + +' 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a b A%I%z%z%O&d d e e e e e e e e g g g g ' + +'g g g g h i i j j j j k k k k l l l l m m m m m n n b*3$3$<$<$<$<$>$>$>$>$>' + +'$>$>$,$$$$$$$$$]$s t t t u u u v v v v v v w w x x x x x x x y y y y y z z ' + +'z A A B B B B B C c*)#.#.# #E E E E E E F G G G G G G f-e@e@e@e@;=H I I J J' + +' J J K K K L L L M v+S+S+S+h+h+h+h+h+g@O P P Q Q Q R R R S S T T T T T U V ' + +'V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.L.N.N.N.N.N.,+,+,+,+S.Y.%' + +'.&.&.&.*.*.*.*.k.Y+`+~-]+]+7+7+7+7+7+9+9+0+0+0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@' + +'.@.@]@]@]@]@]@6@7@_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3' + +'.4.0#n#=#=#n#n#n#n#n#n#M#R&f#}=P#9.9.9.+=+=+=4=4=.-f=1=N=N=N=N=N=V=(-(-(-(-' + +'(-",'#13#10'"7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 3 ' + +'2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } [ [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.0.< < < < < < < < < < < < < < ' + +'< < < < < < < < < < < < < < < 0.0.0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [' + +' [ [ [ [ [ [ [ } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 ' + +'2 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9' + +' 9 9 9 9 0 0 0 0 a a a a a b b I%z%z%z%g-d e e e e e e e e g g g g g g g g ' + +'h i i j j j j k k k k l l l l m m m m m n n o <$3$<$<$<$<$>$>$>$>$>$>$>$,$$' + +'$$$$$$$W#s t t t u u u u v v v v v w w x x x x x x x a.y y y y z z z A A B ' + +'B B B B C C y#)#.#.#,#E E E E E F G G G G G G G s@e@e@e@P@_@I I J J J J c.K' + +' K K L L M c.S+S+S+x+h+h+h+h+h+|+O P Q Q Q R R R S S S T T T T U V V V V W ' + +'W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.+.+.@.R.N.N.N.N.l+,+,+,+,+''+''+T.G$M' + +'&Z%w$X.b%u${+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@' + +']@]@]@]@6@6@z@_._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.f.n' + +'#=#=#8#n#n#n#n#n#M#R&R&}=j=8.9.9.+=+=+=4=4=4=4=h-1=N=N=N=N=N=i-b-(-(-(-(-",' + +#13#10'"8 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 2 ' + +'2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } } [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.0.0.0.' + +'0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [' + +' } } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 ' + +'3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9' + +' 0 0 0 0 a a a a b b b ^&z%z%r%r%S%e e e e e e e e g g g g g g g g h i j j ' + +'j j j k k k k l l l l m m m m m n n o g*3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$' + +'${$t t t u u u u v v v v v w w x x x x x x x x y y y y y z z A A B B B B B ' + +'C C t%.#.#.#.#}#E E E E b.F G G G G G G J%P@e@e@e@s@I I I J J J c.K K K L L' + +' L M f+S+S+S+h+h+h+h+h+h+=%P P Q Q Q R R S S S T T T T U U V V V V W X X X ' + +'X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.@.x=N.N.N.N.l+,+,+,+,+''+''+!+!+!+~+~+~' + +'+~+{+]+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@' + +']@6@6@8@_._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.u==#=#=#n' + +'#n#n#n#n#n#M#R&}*j=8.9.9.9.+=+=+=4=4=4=4=Q=j-N=N=N=N=N=q=$-(-(-(-(-",'#13#10 + +'"8 8 7 7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 2 2 ' + +'2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | } } } } } } } [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } }' + +' | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 ' + +'4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 0' + +' a a a a b b b c &z%r%r%r%b e e e e e e f g g g g g g g g h i j j j j j k ' + +'k k k l l l l m m m m m n n o o ''$<$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$I#t ' + +'t u u u u v v v v v w w x x x x x x x x y y y y y z z z A B B B B B C C C i' + +'#.#.#.#!#E E E E E F G G G G G G i.(=e@e@e@e@l&I I J J J J c.K K K L L M c.' + ,'S+S+S+x+h+h+h+h+h+i+i+D*x%K%N R R R S S S T T T T U V V V V W W X X X X X Y' + +' Y Z Z Z Z ` ` ` . .+.+.+.+.@.#.I+N.N.N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{' + +'+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@{@]@]@]@]@]@6@' + +'6@g%_._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.1.f#=#=#=#8#n#n#n' + +'#n#n#n#R&}*P#8.8.9.9.+=+=+=4=4=4=4=4=D=k-N=N=N=N=k=q=q=;-(-(-l-",'#13#10'"9' + +' 9 8 8 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 ' + +'2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | } } } } } } } } [' + +' [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } | | | | |' + +' | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 ' + +'5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a' + +' a a b b b c c m-r%r%}%A%e e e e e e f g g g g g g g h h i j j j j j k k k ' + +'k l l l l m m m m m n n o o o :$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$+$j&t t u' + +' u u v v v v v w w w x x x x x x x y y y y y z z z A A B B B B B C C C 6#.#' + +'.#.#~#E E E E F G G G G G G G H E@e@e@e@d@I I J J J J c.K K K L L M M v+S+S' + +'+S+h+h+h+h+h+h+i+j+k+k+k+k+(&;@<=S S T T T T U U V V V V W X X X X X X Y Z ' + +'Z Z Z ` ` ` . ...+.+.+.+.@.#.l*N.N.N.l+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]' + +'+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@' + +'N*:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.1.1#=#=#=#=#8#n#n#n#n#n' + +'#M#}*n-8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=q=N=N=N=q=q=q=|-(-(-l-",'#13#10'"9 9 9' + +' 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 ' + +'2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | } } } } } } }' + +' } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ ' + +'[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } } | | | | | | | | | | |' + +' | | | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 ' + +'5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a b b' + +' b b c c c 9-r%}%}%o-e e e e e g g g g g g g g h h i j j j j j k k k l l l ' + +'l l m m m m m n n o o o p 2$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$+$K#t t u u u v' + +' v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B C C C t%.#.#.#O@' + +'}#E E E b.F G G G G G G i.##e@e@e@e@#=I I J J J J K K K L L L M M /&S+S+h+h' + +'+h+h+h+h+i+j+j+k+k+k+3+3+3+4+4+R$ +F.T T U V V V V W W X X X X X Y Y Z Z Z ' + +'` ` ` ` . .+.+.+.+.@.#.#.;&N.N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+]+]+' + +']+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@]@6@6@7@7@#@:' + +'.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.l#=#=#=#=#=#n#n#n#n#n#n#p-' + +'n-8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=q-N=N=N=q=q=q=q=T=(-l-",'#13#10'"9 9 9 9 ' + +'9 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3' + +' 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | } } } ' + +'} } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [' + +' [ [ [ [ [ [ [ [ [ } } } } } } } } } } } | | | | | | | | | | | | | | | | 1 ' + +'1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5' + +' 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b c ' + +'c c d d 7-}%}%}%m-e e e e g g g g g g g g h h i j j j j j k k k l l l l l m' + +' m m m m n n o o o p m <$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$$$+$)$t u u u v v v ' + +'v v v w w x x x x x x x x y y y y y z z A A B B B B B C C C C C@.#.#N@u%E E' + +' E E F G G G G G G i.H s@e@e@e@W%I I J J J J c.K K K L L M M K&S+S+x+h+h+h+' + +'h+h+h+i+j+k+k+k+3+3+3+4+4+4+5+5+5+(&y&H.V V V V W X X X X X X Y Z Z Z Z ` `' + +' ` . ...+.+.+.+.@.#.#.;&N.N.l+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+' + +'7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@Q*a@:.:' + +'.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.l#H@=#=#=#=#8#n#n#n#n#n#p-n-7.8.' + +'8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=q=N=k=q=q=q=q=@-l-l-",'#13#10'"9 9 9 9 9 9 ' + +'8 8 8 7 7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3' + +' 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | ' + +'| } } } } } } } } } } } } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } }' + +' } } } } } } } } } } } } | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 ' + +'1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d ' + +'d r-}%}%}%}%[%e e e g g g g g g g g h h i j j j j j k k k l l l l l m m m m' + +' m n n o o o p p ^$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$$$+$+$N#u u u u v v v v v ' + +'w w x x x x x x x x y y y y y z z z A B B B B B B C C C D 6#.#.#N@M%E E E F' + +' G G G G G G G H H P@e@e@e@P+I J J J J c.K K K L L M M M w+S+S+h+h+h+h+h+h+' + +'i+j+j+k+k+k+3+3+3+4+4+4+5+5+5+5+6+%+%+#+,*F.W X X X X X Y Y Z Z Z ` ` ` ` ' + +'. .+.+.+.+.@.#.#.#.L&N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7' + +'+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@]@]@]@]@]@6@6@7@7@7@m@|#:.l.<.' + +'[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.s-H@=#=#=#=#=#n#n#n#n#n#R&<&7.7.8.8.9' + ,'.9.9.+=+=+=4=4=4=4=D=K=K=K=K=t-N=q=q=q=q=q=q=u-l-",'#13#10'"9 9 9 9 9 9 9 9' + +' 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 ' + +'3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | |' + +' | | | | | | | | } } } } } } } } } } } } } } } } } } } } } } } } } } } } } ' + +'} } | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 2 2' + +' 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 ' + +'6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b b c c c d d e C' + +'%}%}%}%}%~%e f g g g g g g g g h h i j j j j j k k k l l l l l m m m m m n ' + +'n o o o p p p b$>$>$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$s u u u v v v v v w w x' + +' x x x x x x x y y y y y z z z A A B B B B B C C C C j#s=.#N@O@E E E b.F G ' + +'G G G G G i.H :@e@e@e@e@l&I J J J J K K K K L L M M t&S+S+x+h+h+h+h+h+i+i+j' + +'+k+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+*+#%I.E.V X Y Z Z Z Z ` ` ` . ...' + +'+.+.+.+.#.#.#.#.L&N.l+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9' + +'+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@U@<.<.[.[.' + +'[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.W&H@H@=#=#=#=#8#n#n#n#n#R&p#7.7.8.8.8.9.9.9' + +'.+=+=4=4=4=4=4=D=K=K=K=K=T=k=q=q=q=q=q=q=v-w-",'#13#10'"0 0 9 9 9 9 9 9 9 9' + +' 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 ' + +'3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | |' + +' | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ' + +'| | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2' + +' 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 ' + +'7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d d d e e ^&}%}' + +'%}%}%b g g g g g g g g g h i i j j j j j k k k l l l l m m m m m m n n o o ' + +'o p p p p 7$>$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$V#u u u v v v v v w w x x x x' + +' x x x x y y y y y z z z A A B B B B B C C C C D C@.#N@N@u%E E E F G G G G ' + +'G G i.H H Q@e@e@e@=@I J J J J c.K K K L L M M M <*S+S+h+h+h+h+h+h+i+j+k+k+k' + +'+k+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+&+y+*+*+*+*+*+z+w*D+=&Z ` ` ` . . .+.+.+.' + +'+.@.#.#.#.#.$%l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+9+0+' + +'0+0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@m@m@m@O*_=[.[.[.[.[' + +'.[.}.}.}.}.}.|.1.1.1.1.2.2.-=H@H@=#=#=#=#=#8#n#n#n#R&p#6.7.7.8.8.8.9.9.+=+=' + +'+=4=4=4=4=D=D=K=K=K=K=K=x-q=q=q=q=q=y-z-w-",'#13#10'"0 0 0 9 9 9 9 9 9 9 9 ' + +'8 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3' + +' 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | ' + +'| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |' + +' | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 ' + +'3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7' + +' 8 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b b c c c d d e e e e [%}%}%}%' + +'A%g g g g g g g g g h i i j j j j j k k k l l l l m m m m m m n n o o o p p' + +' p p q |$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$W#{$u u v v v v v w w w x x x x x ' + +'x x a.y y y y z z z A A B B B B B C C C C D D $&N@N@N@~#E E F G G G G G G G' + +' H H _@P@e@e@}@A-J J J J c.K K K L L L M M c.x+S+h+h+h+h+h+h+i+j+j+k+k+k+3+' + +'3+3+4+4+4+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+Q&-&D+F+`& ...+.+.+.+.#' + +'.#.#.#.#.I+,+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9+0+0+g&a+' + +'B-0&_+0&0&+@K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@z@z@S*[.[.[.[.}.}' + +'.}.}.}.|.|.1.1.1.1.2./#X@H@H@=#=#=#=#=#n#n#n#R&p#5.6.7.8.8.8.9.9.9.+=+=+=4=' + +'4=4=4=D=K=K=K=K=K=K=C-q=q=q=q=q=y-y-D-",'#13#10'"0 0 0 0 0 9 9 9 9 9 9 9 9 ' + +'8 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 3 3 3 3 3 3' + +' 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | ' + +'| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |' + +' | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 ' + +'3 3 3 3 3 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9' + +' 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d d d e e e e e E-}%}%}%M$g ' + +'g g g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q' + +' o >$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$W#W# $u v v v v v v w w x x x x x x x x ' + +'y y y y y z z A A B B B B B B C C C D D D s=N@N@N@}#E b.G G G G G G G i.H H' + +' :@e@e@e@}@%@J J J J K K K K L L M M M u+S+x+h+h+h+h+h+h+i+j+f+x%f+3+3+3+4+' + +'4+4+5+5+5+5+6+6+%+%+%+%+&+y+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+-&D+F+F+''*L&L&' + +'L&L&G+l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+/+!@)@,@''.''.).' + +').).!.!.!.~.~.w@x@F-L+.@.@.@.@{@]@]@]@]@6@6@7@7@7@m@m@m@m@z@A@A@0@[.[.[.}.}' + +'.}.}.}.|.|.1.1.1.f.J@X@H@H@H@=#=#=#=#8#n#n#M#p#5.6.7.7.8.8.8.9.9.+=+=+=4=4=' + +'4=4=D=D=K=K=K=K=K=]-(-G-q=q=q=y-y-G-H-",'#13#10'"a a 0 0 0 0 0 9 9 9 9 9 9 ' + +'9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3' + +' 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ' + +'1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1' + ,' 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 ' + +'3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9' + +' 9 9 9 9 9 0 0 0 0 0 a a a a a b b b b c c c d d d e e e e e e o-}%I-Y$I$g ' + +'g g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q q' + +' _$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$+$W#W#s*v v v v v v w w x x x x x x x x y ' + +'y y y y z z z A A B B B B B C C C C D D #-N@N@N@6#E b.F G G G G G G i.H H H' + +' Q@e@e@}@+#J J J J c.K K K L L M M M M g+S+h+h+h+h+h+h+i+R$<=Q Q Q R f&2@R$' + +'(&4+5+5+5+5+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N' + +'.N.N.,+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+0+Y+,@>.,.''.''.''.''.).' + +').).!.!.~.~.~.~.~.].^.~.x@y*{@]@]@]@]@]@6@6@7@7@7@m@m@m@O*z@A@A@A@P*[.}.}.}' + +'.}.}.|.|.1.1.1.1.J@:&X@H@H@=#=#=#=#=#n#n#M#J-5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=' + +'4=4=D=D=K=K=K=K=K=(-(-t-q=q=q=y-y-G-K-",'#13#10'"a a a 0 0 0 0 0 9 9 9 9 9 ' + +'9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3' + +' 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 ' + +'1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1' + +' 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ' + +'4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9' + +' 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c c d d e e e e e e e e A%Y$Y$Y$b=g ' + +'g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q q q' + +' ^=>$>$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#t v v v v v w w x x x x x x x x y y ' + +'y y y z z z A A B B B B B C C C C D D D M@N@N@N@M@E F G G G G G G G H H H H' + +' P@e@}@}@A-J J J c.K K K L L L M M M c.S+x+h+h+h+h+h+i+|+P Q Q Q R R R S S ' + +'S F.I*++*&6+6+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N' + +'.N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+]+]+]+(+<+;.;.>.,.,.''.''.''.).).' + +').!.!.!.~.~.~.~.{.].^.^.^.^.H%o=p%]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@L*}.}.}' + +'.}.}.|.|.1.1.1.U==#X@H@H@H@=#=#=#=#8#n#n#J-5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=' + +'4=4=D=K=K=K=K=K=]-(-(-T=q=q=q=y-y-G-G-",'#13#10'"a a a a a 0 0 0 0 0 9 9 9 ' + +'9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4' + +' 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 ' + +'1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1' + +' 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 ' + +'4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9' + +' 9 9 0 0 0 0 0 a a a a a b b b b c c c d d d e e e e e e e e r-Y$Y$Y$Y$x$g ' + +'g g g h i i j j j j j k k k l l l l l m m m m m m n n o o o p p p p q q q q' + +' ''$>$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#O#v v v v v w w x x x x x x x x y y y' + +' y y z z z A A B B B B B C C C C D D D E 6#N@N@N@m%F G G G G G G G H H H H ' + +':@e@}@}@}@Y*J J J K K K K L L M M M M K&x+h+h+h+h+h+h+B$P Q Q Q Q R R S S S' + +' T T T T T U -%,*#%q$&+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.' + +'N.N.,+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+]+]+`+5%;.;.;.;.>.,.''.''.''.''.).)' + +'.).!.!.~.~.~.~.~.].].^.^.^.e././.].o=p%6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@W@P*}.' + +'}.}.}.|.1.1.1.k#=#X@X@H@H@=#=#=#=#=#8#n#[=5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4' + +'=4=D=D=K=K=K=K=K=]-(-(-(-G-q=y-y-G-G-G-",'#13#10'"b a a a a a a 0 0 0 0 0 9' + +' 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2' + +' 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 ' + +'2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 ' + +'9 0 0 0 0 0 a a a a a a b b b c c c d d d e e e e e e e e e g T$Y$Y$Y$Y$L-g' + +' g h h i j j j j j j k k k l l l l l m m m m m n n n o o o p p p p q q q q ' + +'q 7$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#C&v v v v w w x x x x x x x x a.y y y' + +' y z z z A A B B B B B C C C C D D D D }#O@N@N@O@b.F G G G G G G i.H H H H ' + +';=e@}@}@R+J J J c.K K K L L M M M M M w+h+h+h+h+h+h+i+<=P Q Q Q R R R S S S' + +' T T T T U V V V V V T -%U+.=*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.' + +'N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+]+]+8+b+-.;.;.;.;.>.,.,.''.''.''.).).)' + +'.).!.!.~.~.~.~.{.].^.^.^.^.e././././.!%^@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@W@H@' + +'V*}.|.|.1.1.6=m&X@X@X@H@H@=#=#=#=#=#n#Q#5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4' + +'=4=D=K=K=K=K=K=K=(-(-(-(-X=q=y-y-G-G-G-",'#13#10'"b b b a a a a a a 0 0 0 0' + +' 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2' + +' 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ' + +'2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 ' + +'0 0 0 0 a a a a a a b b b b c c c d d d e e e e e e e e e g g W$Y$Y$Y$Y$M-g' + +' h h i j j j j j k k k k l l l l l m m m m m n n n o o o p p p p q q q q q ' + ,'q |$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#X#v v v w w x x x x x x x x a.y y y y' + +' y z z A A B B B B B B C C C C D D D E q@N@N@N@`@F G G G G G G i.H H H H H ' + +'[@}@}@*@A-J J c.K K K L L L M M M M c.x+h+h+h+h+h+i+=%P Q Q Q R R R S S S T' + +' T T T U U V V V V W r.X X X V E.~&A+-+=+=+-+-+-+-+;+;+O.O.O.O.O.N.N.N.N.N.' + +'l+,+,+,+,+,+''+''+!+!+!+~+~+~+~+{+]+]+`+t*-.-.;.;.;.;.>.,.''.''.''.''.).).)' + +'.!.!.~.~.~.~.~.].].^.^.^.^./././././.(.(.e-z@7@m@m@m@O*z@A@A@A@A@A@W@W@W@W@' + +'W@9=N-l#l#8#]#X@X@X@H@H@=#=#=#=#=#8#o#5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4' + +'=D=D=K=K=K=K=K=]-(-(-(-(-b-y-y-G-G-G-G-",'#13#10'"c b b b b a a a a a 0 0 0' + +' 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3' + +' 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 ' + +'3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 ' + +'0 0 0 a a a a a b b b b c c c d d d e e e e e e e e e f g g g 9%Y$Y$Y$Y$g h' + +' i i j j j j j k k k k l l l l m m m m m m n n o o o o p p p p q q q q q q ' + +'q >$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#O-v v w w w x x x x x x x x y y y y y' + +' z z z A A B B B B B C C C C D D D E E M@N@N@N@M%G G G G G G G H H H H H _@' + +'}@}@}@*@u@J J K K K K L L M M M M M P-h+h+h+h+h+h+i+R$Q Q Q Q R R S S S S T' + +' T T T U V V V V W W X X X X X Y Y Z Z j$D+-&O.-+;+;+>+O.O.O.O.P.N.N.N.N.N.' + +'l+,+,+,+,+''+''+''+!+!+!+~+~+~+{+{+5=b+-.-.-.;.;.;.;.>.,.,.''.''.''.).).).)' + +'.!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._.(@V@m@m@m@z@z@A@A@A@A@A@W@W@W@W@' + +'W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#=#{&5.5.5.5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=4' + +'=D=D=K=K=K=K=K=(-(-(-(-(-K=y-y-G-G-G-G-",'#13#10'"c c c b b b b a a a a a 0' + +' 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3' + +' 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ' + +'3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 ' + +'0 a a a a a b b b b c c c c d d d e e e e e e e e e g g g g g b=Y$Y$Y$J$h i' + +' j j j j j j k k k k l l l l m m m m m m n n o o o p p p p p q q q q q q q ' + +'-$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#j&v w w w x x x x x x x x y y y y y z' + +' z z A A B B B B B C C C C D D D E E E 6#N@N@ #}#G G G G G G H H H H H H #=' + +'}@}@*@*@N+J c.K K K L L M M M M M M f+h+h+h+h+h+i+j+ %Q Q Q R R R S S S T T' + +' T T U U V V V V W X X X X X X Y Z Z Z Z ` ` ` `&s$F%-&O.O.O.P.N.N.N.N.N.l+' + +',+,+,+,+,+''+''+!+!+!+~+~+~+~+{+/+[+-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!' + +'.!.~.~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.{#m@O*z@A@A@A@A@A@A@W@W@W@W@' + +'W@*#]#]#]#]#]#X@X@H@H@H@=#=#=#=#=*5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4' + +'=D=K=K=K=K=K=]-(-(-(-(-(-(-Q-y-G-G-G-G-",'#13#10'"d c c c b b b b a a a a a' + +' a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3' + +' 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ' + +'3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a ' + +'a a a a a b b b b c c c d d d e e e e e e e e e f g g g g g g :=Y$Y$V${%i j' + +' j j j j j k k k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q ' + +' =$$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#t w w w x x x x x x x x y y y y y z z' + +' z A A B B B B B C C C C D D D E E E E O@N@O@h*G G G G G G i.H H H H H H R-' + +'}@*@*@Q+J c.K K K L L L M M M M M j.Y%h+h+h+h+i+i+D*Q Q Q R R R S S S T T T' + +' T T U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.x*-&N.N.N.N.N.N.l+' + +',+,+,+,+''+''+''+!+!+!+~+~+~+{+&*-.-.-.-.-.;.;.;.;.>.,.,.''.''.''.).).).).!' + +'.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.U@z@z@A@A@A@A@A@W@W@W@W@W@' + +'*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#u=P#5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D' + +'=D=K=K=K=K=K=]-(-(-(-(-(-l-S-G-G-G-G-T-",'#13#10'"d d d c c c b b b b a a a' + +' a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 ' + +'6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3' + +' 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ' + +'3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6' + +' 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a ' + +'a a a b b b b c c c d d d e e e e e e e e e e g g g g g g g g Z$V$V$P$:=j j' + +' j j j k k k k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q r ' + +'U-$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#O#w w w x x x x x x x x y y y y y z z z' + +' A A B B B B B C C C C D D D D E E E m%N@O@ #d@G G G G G i.H H H H H H I =@' + +'*@*@*@A-c.K K K L L L M M M M M j.g@h+h+h+h+h+i+j+x%Q Q Q R R R S S S T T T' + +' T U V V V V V W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.@.R.N.N.N.N.l+,+' + ,',+,+,+,+''+''+!+!+!+~+~+~+~+&*-.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!' + +'.~.~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.:.:.l.U@A@A@A@A@A@A@W@W@W@W@W@' + +'*#*#]#]#]#]#X@X@H@H@H@=#=#=#n#V-5.5.5.5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D' + +'=K=K=K=K=K=K=(-(-(-(-(-(-l-W-G-G-G-G-T-",'#13#10'"e d d d c c c c b b b b a' + +' a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 ' + +'6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4' + +' 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 ' + +'4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6' + +' 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a ' + +'a b b b b c c c c d d d e e e e e e e e e f g g g g g g g g g {%V$P$J$9%j j' + +' j j k k k k l l l l l m m m m m n n n o o o p p p p p q q q q q q q q r s ' + +'{$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#b&w w x x x x x x x x y y y y y z z z A' + +' A B B B B B B C C C D D D D E E E E ~#N@ #r@~#G G G G G H H H H H H I %@}@' + +'*@*@*@u@c.K K K L L M M M M M M N 1+h+h+h+h+i+j+j+K%Q Q R R R S S S T T T T' + +' U U V V V V W r.X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.#...l+N.N.N.l+,+' + +',+,+,+''+''+)+!+!+~+~+~+~+&*=.-.-.-.-.-.;.;.;.;.>.,.,.''.''.''.).).).).!.!.' + +'~.~.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.:.<.<.%#A@A@A@A@W@W@W@W@W@*#*' + +'#]#]#]#]#X@X@X@H@H@=#=#=#S=**m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=' + +'K=K=K=K=K=]-(-(-(-(-(-l-l-w-K-G-G-G-T-",'#13#10'"e e e d d d c c c c b b b ' + +'b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 7 6 6' + +' 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 ' + +'4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4' + +' 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 ' + +'6 7 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b' + +' b b c c c c d d d e e e e e e e e e e g g g g g g g g g g h X-P$J$J$W$j j ' + +'j k k k k l l l l m m m m m m n n n o o o p p p p h.q q q q q q q r r s s Y' + +'-$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#B#w x x x x x x x x y y y y y z z z A A ' + +'B B B B B B C C C C D D D E E E E E }- #r@r@b@G G G G H H H H H H H I X%*@*' + +'@*@*@v@K K K L L M M M M M M N N f+h+h+h+i+i+j+k+N Q R R R S S S T T T T T ' + +'U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.x=N.N.l+,+,+,' + +'+,+,+''+''+!+!+!+~+~+~+T.[+-.-.-.-.-.-.;.;.;.;.>.,.''.''.''.''.).).).!.!.~.' + +'~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.:.:.:.<.[.[.%#A@A@A@W@W@W@W@W@*#*' + +'#]#]#]#]#X@X@H@H@H@=#=#=#v=4.5.5.5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=' + +'K=K=K=K=K=(-(-(-(-(-(-l-l-w->-G-G-T-T-",'#13#10'"e e e e e d d d c c c c b ' + +'b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7' + +' 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 7 7 ' + +'7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b' + +' c c c c d d d e e e e e e e e e e g g g g g g g g g g h h i P$J$J$J$L-j k ' + +'k k k l l l l l m m m m m m n n o o o o p p p p q q q q q q q q r r s s s W' + +'#$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#Z-x x x x x x x x y y y y y z z z A A B ' + +'B B B B B C C C C D D D E E E E E E D@r@r@r@E G G G i.H H H H H H I I A-*@*' + +'@*@x+K K K L L L M M M M M j.N N Y%h+h+h+i+j+k+f+Q Q R R R S S S T T T T U ' + +'V V V V V W X X X X X X Y Z Z Z Z ` ` ` ` . .+.+.+.+.+.#.#.#.;&N.N.,+,+,+,' + +'+,+''+''+!+!+!+~+~+~+~+H$=.-.-.-.-.-.;.;.;.;.>.,.,.''.''.''.).).).!.!.!.~.~' + +'.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.:.l.<.[.[.[.|#A@W@W@W@W@W@W@*#]#' + +']#]#]#X@X@X@H@H@=#=#=#1#4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K' + +'=K=K=K=]-(-(-(-(-(-(-l-l-w-`-G-G-T-T-",'#13#10'"e e e e e e e d d d c c c c' + +' b b b b a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 ' + +'7 7 7 7 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7' + +' 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a b b b b c c ' + +'c c d d d e e e e e e e e e e e g g g g g g g g g g h h i g J$J$J$J$T$k k k' + +' k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q r r s s s s +$' + +'$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#G# ;x x x x x x x y y y y y y z z A A A B B' + +' B B B C C C C D D D E E E E E E b. #r@r@6#G G G i.H H H H H H I I J Z**@*@' + +'*@P+K K K L L M M M M M M N N z$h+h+h+i+j+j+k+1+Q R R R S S S T T T T U U V' + +' V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#...N.l+,+,+,+,+' + +'''+''+''+!+!+!+~+~+~+_%=.-.-.-.-.-.;.;.;.;.;.>.,.''.''.''.''.).).).!.!.w@r+' + +'r+r+r+].].^.^.^.^./././././.(.(._._._.:.:.:.:.<.[.[.[.[.a@A@W@W@W@W@W@*#*#]' + +'#]#]#]#X@X@X@H@H@=#=#n#f.4.5.5.5.5.5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=' + +'K=K=K=K=c-(-(-(-(-(-l-l-w-w-D-G-T-T-T-",'#13#10'"e e e e e e e e d d d d c ' + ,'c c c b b b b a a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7' + +' 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 ' + +'7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a a b b b b c c c c' + +' d d d d e e e e e e e e e e g g g g g g g g g g h h i i j `$J$J$J$m$`$k k ' + +'l l l l l m m m m m m n n n o o o p p p p p q q q q q q q q r r s s s s ]$$' + +'$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#Z&x x x x x x a.y y y y y z z z A A B B ' + +'B B B C C C C D D D D E E E E E b.j#r@r@r@d@G G G H H H H H H I I I J E@*@*' + +'@|@X%K K L L M M M M M M N N N |+h+h+i+i+j+k+k+1+R R R S S S T T T T T U V ' + +'V V V W W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.@.#.#.#.#.#.l+,+,+,+,+,' + +'+''+''+!+!+!+~+~+~+U.=.=.-.-.-.-.-.;.;.;.;.>.,.,.''.''.''.).).).1&)=n+K+K+K' + +'+K+K+ @+@5@x@~.e././././.(.(.(._._._.:.:.:.l.<.[.[.[.[.[.I@W@W@W@W@W@*#]#]#' + +']#]#X@X@X@H@H@=#=#=#**4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K' + +'=K=K=K=(-(-(-(-(-(-l-l-w-w-w-G-T-T-T-",'#13#10'"e e e e e e e e e e d d d d' + +' c c c c b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 ' + +'8 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8' + +' 8 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b c c c c d d ' + +'d d e e e e e e e e e e g g g g g g g g g g g h h i j j j k$J$J$m$m$1$k l l' + +' l l l m m m m m m n n n o o o p p p p p q q q q q q q q r r s s s s s s*+$' + +'+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#`#x x x x x a.y y y y y z z z A A B B B B' + +' B C C C C D D D D E E E E E b.F P%r@r@r@~#G G H H H H H H H I I J N+*@*@|@' + +'=@%@K L L L M M M M M j.N N N x%h+h+i+j+k+k+k+f+R R R S S S T T T T U U V V' + +' V V W X X X X X X Y Y Z Z Z ` ` ` ` . ...+.+.+.+.#.#.#.#.#.#.l+,+,+,+,+''' + +'+''+''+!+!+!+~+~+<%k.=.-.-.-.-.-.;.;.;.;.;.>.,.''.''.''.''.''=5@m+J+J+K+K+K' + +'+K+K+ @ @.@.@.@+@o=]./././.(.(._._._.:.:.:.:.<.[.[.[.[.[.[.}.B@W@W@W@*#*#]#' + +']#]#]#X@X@X@H@H@=#=#-#4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K' + +'=K=K=K=]-(-(-(-(-(-l-l-w-w-w-w-.;T-T-T-",'#13#10'"g f e e e e e e e e e e d' + +' d d d c c c c b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 ' + +'9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5' + +' 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 ' + +'5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9' + +' 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b c c c c d d d ' + +'d e e e e e e e e e e f g g g g g g g g g g h h i i j j j j T$J$m$m$m$h l l' + +' l l l m m m m m m n n o o o o p p p p p q q q q q q q q r r s s s s s s O-' + +'+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#x#q#x x x x a.y y y y y z z z A A B B B B' + +' B C C C C D D D D E E E E E E F G ~#r@r@r@q@G i.H H H H H H I I J J u@*@|@' + +'=@=@K L L L M M M M M j.N N N N f+h+i+j+j+k+k+k+3+}+R S S S T T T T U U V V' + +' V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.+;,+,+,+,+,+' + +'''+''+!+!+!+~+~+~+W.=.-.-.-.-.-.-.;.;.;.;.>.,.,.''.''.''.5@m+m+J+J+J+K+K+K+' + +'K+K+ @.@.@.@.@{@]@]@o=!%(.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.J@W@W@W@*#]#]#]' + +'#]#X@X@X@H@H@=#=#S=f.4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=' + +'K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-@;T-T-T-",'#13#10'"g g g e e e e e e e e e e ' + +'e d d d d c c c c b b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9' + +' 9 9 9 9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 ' + +'6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6' + +' 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b b c c c c d d d d e e' + +' e e e e e e e e e g g g g g g g g g g h h i i j j j j j j T$m$m$m$e$l l l ' + +'l m m m m m m m n n o o o p p p p p q q q q q q q q q r r s s s s s s t I#+' + +'$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#x#L#x x x x a.y y y y y z z z A A B B B B B ' + +'C C C C C D D D E E E E E E F G G M@r@r@r@}#i.H H H H H H I I J J J O+|@=@=' + +'@&&K L L M M M M M M N N N N N k+i+j+j+k+k+k+3+3+2+S S S T T T T T U V V V ' + +'V W W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.G$,+,+,+,+''+' + +'''+)+!+!+~+~+~+T.=.=.-.-.-.-.-.;.;.;.;.;.>.,.''.''.;.a+m+m+m+J+J+K+K+K+K+K+' + +' @ @.@.@.@.@]@]@]@]@L+T@(._._._.:.:.:.:.<.[.[.[.[.[.[.}.}.}.z#W@*#*#]#]#]#]' + +'#X@X@X@H@H@=#=#9#4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=' + +'K=]-(-(-(-(-(-(-l-l-w-w-w-w-#;T-T-K-",'#13#10'"g g g g g e e e e e e e e e ' + +'e e d d d d c c c c b b b b b a a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9' + +' 9 9 9 9 9 9 9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 ' + +'6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6' + +' 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 ' + ,'9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a a b b b b b c c c c d d d d e e e e e' + +' e e e e e e g g g g g g g g g g g h h i j j j j j j k k $;m$m$e$l$l l l m ' + +'m m m m m n n n o o o p p p p p q q q q q q q q q r s s s s s s s t t X#W#W' + +'#W#W#W#K#K#K#K#K#K#G#G#x#x#x#x#C&x x x a.y y y y y z z z A A B B B B B B C ' + +'C C C D D D E E E E E E F G G G W%r@r@r@J%H H H H H H I I I J J J w+|@=@=@P' + +'+L L M M M M M M N N N N N N h+i+j+k+k+k+k+3+3+3+C$P S T T T T U U V V V V ' + +'W X X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.+*,+,+,+''+''+' + +'''+!+!+!+~+~+~+X.=.-.-.-.-.-.-.;.;.;.;.>.,.''.''.3@g&m+m+m+J+J+J+K+K+K+K+K+' + +' @.@.@.@.@{@]@]@]@]@6@7@%;_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.c=W@*#]#]#]#]#X' + +'@X@X@H@H@H@=#=#**4.m.5.5.5.5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=' + +'K=(-(-(-(-(-(-l-l-w-w-w-w-&;*;T-T-K-",'#13#10'"g g g g g g g e e e e e e e ' + +'e e e e d d d d c c c c c b b b b b a a a a a a a 0 0 0 0 0 0 0 0 9 9 9 9 9' + +' 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 ' + +'6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7' + +' 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ' + +'9 9 0 0 0 0 0 0 0 0 a a a a a a a b b b b b c c c c c d d d d e e e e e e e' + +' e e e e g g g g g g g g g g g h h i i j j j j j j k k k 4$e$e$e$d$l m m m ' + +'m m m m n n o o o o p p p p p q q q q q q q q r r s s s s s s s t t t U-W#W' + +'#W#K#K#K#K#K#K#G#G#G#x#x#x#x#x#4&x x a.y y y y y z z z A A B B B B B B C C ' + +'C C D D D E E E E E E b.F G G G h*r@r@h*H H H H H H I I I J J J J /&=@=@=@O' + +'+L L M M M M M j.N N N N N g@i+j+j+k+k+k+3+3+3+4+4+4+++`.T T U U V V V V W ' + +'W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.=;,+,+,+''+''+!' + +'+!+!+~+~+~+<%=.=.-.-.-.-.-.;.;.;.;.;.,.,.''.,@g&0+m+m+m+J+J+K+K+K+K+K+ @ @.' + +'@.@.@.@]@]@]@]@]@6@6@g%N*_.:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.-;*#]#]#]#]#X@X@' + +'X@H@H@=#=#=#v=4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=]-(' + +'-(-(-(-(-(-l-l-w-w-w-w-&;&;T-T-K-",'#13#10'"g g g g g g g g g e e e e e e e' + +' e e e e e d d d c c c c c b b b b b a a a a a a a a 0 0 0 0 0 0 0 0 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7' + +' 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 ' + +'7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0' + +' 0 0 0 0 0 a a a a a a a a b b b b b c c c c c d d d e e e e e e e e e e e ' + +'e g g g g g g g g g g g h h i i j j j j j j k k k k k B=e$e$e$Q$m m m m m m' + +' n n n o o o p p p p p h.q q q q q q q q r r s s s s s s t t t t u ;;W#W#K#' + +'K#K#K#K#K#G#G#G#x#x#x#x#x#x#B#x a.y y y y y z z z A A B B B B B B C C C C D' + +' D D E E E E E E b.F G G G E r@r@r@d@H H H H H H I I J J J J c.+#=@=@R+u@L ' + +'M M M M M j.N N N N N N 2@j+j+k+k+k+3+3+3+4+4+4+5+5+5+&+++>;V V V V W W X X' + +' X X X X Y Z Z Z Z ` ` ` ` . .+.+.+.+.+.#.#.#.#.#.#.d.M&,+,+,+''+''+''+!+!' + +'+!+~+~+~+v$=.-.-.-.-.-.-.;.;.;.;.>.,.''.;.g&0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.' + +'@.@{@]@]@]@]@6@6@6@7@g%L*:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.d=]#]#]#]#]#X@X@H@' + +'H@H@=#=#=#S=,;5.5.5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=c-(-(' + +'-(-(-(-l-l-w-w-w-w-&;&;&;'';K-K-",'#13#10'"g g g g g g g g g g g e e e e e ' + +'e e e e e e e d d d d c c c c b b b b b b a a a a a a a a 0 0 0 0 0 0 0 0 0' + +' 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 ' + +'7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7' + +' 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 ' + +'0 0 a a a a a a a a b b b b b b c c c c d d d d e e e e e e e e e e e e g g' + +' g g g g g g g g g h h h i j j j j j j j k k k k l l a$e$e$5$2$m m m m m n ' + +'n n o o o p p p p p q q q q q q q q q r r s s s s s s t t t t u u {$W#K#K#K' + +'#K#K#K#G#G#G#x#x#x#x#x#x#x#%$a.y y y y y z z z A A B B B B B B C C C C D D ' + +'D E E E E E E b.F G G G G v%r@r@e@Q@H H H H H I I J J J J c.|&=@=@=@R+N+M M' + +' M M M M N N N N N N O ;@j+k+k+k+k+3+3+4+4+4+4+5+5+5+5+6+%+#+I.h$V W X X X ' + +'X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.);,+,+,+''+''+!+!+!+~' + +'+~+~+~+H$=.-.-.-.-.-.;.;.;.;.>.,.,.''.(+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@.' + +'@]@]@]@]@]@6@6@7@7@7@n@:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.|.=#]#]#]#X@X@X@H@H@' + +'=#=#=#=#=#8#u#p#5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(' + +'-(-(-l-l-w-w-w-w-&;&;&;!;K-K-",'#13#10'"h g g g g g g g g g g g g e e e e e' + +' e e e e e e e d d d d c c c c c b b b b b b a a a a a a a a 0 0 0 0 0 0 0 ' + +'0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 8 8 7 7 7 7' + +' 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 ' + +'8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 a a a' + +' a a a a a b b b b b b c c c c c d d d d e e e e e e e e e e e e g g g g g ' + +'g g g g g g g h h i i j j j j j j k k k k k l l l b=e$5$5$n$m m m m n n o o' + +' o o p p p p p q q q q q q q q q r r s s s s s s t t t t u u u F#K#K#K#K#K#' + +'K#G#G#G#x#x#x#x#x#x#x#x#~;y y y y y z z z A A B B B B B B C C C C D D D D E' + ,' E E E E b.F G G G G G b@r@.&e@;=H H H H I I J J J J J K +&=@=@R+R+c.M M M ' + +'M M N N N N N N N O R$k+k+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+V&H.;%X X X X' + +' Y Y Z Z Z Z ` ` ` . . .+.+.+.+.@.#.#.#.#.#.#.$.$.S.,+,+''+''+''+!+!+!+~+~' + +'+~+u$=.-.-.-.-.-.;.;.;.;.;.>.,.''.u&0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]' + +'@]@]@]@6@6@6@7@7@7@m@%#:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.-#]#]#]#X@X@H@H@H@=#' + +'=#=#=#8#n#n#n#u#e=5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(' + +'-l-l-w-w-w-w-w-&;&;&;{;K-K-",'#13#10'"i h h g g g g g g g g g g g g e e e e' + +' e e e e e e e e e d d d d c c c c c b b b b b b a a a a a a a a a 0 0 0 0 ' + +'0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8' + +' 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 a a a a a a a a' + +' a b b b b b b c c c c c d d d d e e e e e e e e e e e e e g g g g g g g g ' + +'g g g g h h i i j j j j j j k k k k k l l l l l 2$5$5$5$/$m m n n n o o o p' + +' p p p p p q q q q q q q q r r s s s s s s s t t t t u u u u b&K#K#K#K#K#G#' + +'G#G#x#x#x#x#x#x#x#x#r&5&y y y y z z z A A B B B B B B C C C C D D D D E E E' + +' E E E F G G G G G G q@.&e@e@(=H H H I I J J J J J c.K u+=@R+R+S+M M M M M ' + +'j.N N N N N N O P C$k+k+k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+&+&+*+*+U+E.X X Y' + +' Z Z Z Z ` ` ` ` . ...+.+.+.+.#.#.#.#.#.#.d.$.Z%,+,+''+''+''+!+!+!+~+~+~+~' + +'+$*-.-.-.-.-.-.;.;.;.;.>.,.,.''.~@0+m+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]' + +'@]@]@6@6@7@7@7@m@m@Q*a@<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.k#]#]#X@X@X@H@H@=#=#=#' + +'=#=#8#n#n#n#n#n#7=e=7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l' + +'-l-w-w-w-w-&;&;&;&;H-K-'';",'#13#10'"j i i h h g g g g g g g g g g g g e e ' + +'e e e e e e e e e e e d d d d c c c c c b b b b b b b a a a a a a a a a 0 0' + +' 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9' + +' 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a b b ' + +'b b b b b c c c c c d d d d e e e e e e e e e e e e e g g g g g g g g g g g' + +' g h h i i j j j j j j j k k k k l l l l l l m ];5$5$5$b*m n n o o o o p p ' + +'p p p q q q q q q q q q r r s s s s s s s t t t t u u u u v J#K#K#K#K#G#G#G' + +'#x#x#x#x#x#x#x#x#r&h#^;y y y z z z A A B B B B B B C C C C D D D D E E E E ' + +'E E F G G G G G G G c@e@e@e@#=H H I I I J J J J c.K K u@R+R+R+/&M M M M j.N' + +' N N N N N O P P f+k+k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+w*/;' + +'Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.$.G$,+,+''+''+!+!+!+~+~+~+~+{+Y' + +'.-.-.-.-.-.;.;.;.;.;.>.,.''.u&0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]' + +'@6@6@6@7@7@7@m@m@m@)-<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.m#]#X@X@H@H@H@=#=#=#=#' + +'=#n#n#n#n#n#n#M#R&}*t#P#9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-w' + +'-w-w-w-w-&;&;&;(;_;K-'';",'#13#10'"j j j i i h h g g g g g g g g g g g g e ' + +'e e e e e e e e e e e e e d d d d c c c c c b b b b b b b a a a a a a a a a' + +' a 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9' + +' 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a b b b b b b ' + +'b c c c c c d d d d e e e e e e e e e e e e e e g g g g g g g g g g g g h h' + +' i i j j j j j j j k k k k k l l l l l m m m ];5$5$5$H*n n o o o o p p p p ' + +'p q q q q q q q q q r r s s s s s s t t t t u u u u u v v O#K#K#K#G#G#x#x#x' + +'#x#x#x#x#x#x#r&h#h#(#y y z z z A A B B B B B B C C C C D D D D E E E E E E ' + +'F G G G G G G G i.s@e@e@e@##H I I I J J J J c.K K K :;R+R+S+w+M M M M N N N' + +' N N N O O P Q g+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+Q&' + +'B+j$` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.d.$.$.S.,+''+''+''+!+!+!+~+~+~+~+{+t*' + +'-.-.-.-.-.;.;.;.;.>.,.''.''.~@0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]' + +'@6@6@7@7@7@m@m@m@m@8@[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.!=X@X@X@H@H@=#=#=#=#=#' + +'8#n#n#n#n#n#M#R&R&f#f#f#k=<;6.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w' + +'-w-w-w-&;&;&;&;(;[;'';'';",'#13#10'"j j j j j i h h h g g g g g g g g g g g' + +' g f e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b a a a a a ' + +'a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9' + +' 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 ' + +'9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a b b b b b b c c c' + +' c c c d d d d d e e e e e e e e e e e e e f g g g g g g g g g g g g h h h ' + +'i j j j j j j j k k k k k l l l l l l m m m m 8$5$5$6$H*o o o o p p p p p h' + +'.q q q q q q q q r r s s s s s s s t t t t u u u u v v v v O#K#K#G#G#x#x#x#' + +'x#x#x#x#x#x#h#h#h#h#};y z z z A A B B B B B B C C C C D D D D E E E E E E F' + +' G G G G G G G G H Q@e@e@e@l&H I I J J J J c.K K K K O+R+S+S+<*M M M N N N ' + +'N N N N O P Q Q k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=' + +'+=+-+O.F$Q% . ...+.+.+.+.@.#.#.#.#.#.d.$.$.M&,+,+''+''+!+!+!+~+~+~+~+{+8+-.' + +'-.-.-.-.;.;.;.;.;.,.,.''.;.0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@{@]@]@]@]@6@' + ,'6@7@7@7@7@m@m@m@z@z@p@[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.-#X@X@H@H@H@=#=#=#=#=#n' + +'#n#n#n#n#n#M#R&R&f#f#0=0=0=}=P=--h=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-' + +'w-w-w-&;&;&;&;(;[;'';'';",'#13#10'"j j j j j j j i h h h g g g g g g g g g ' + +'g g g g e e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b b a a' + +' a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 ' + +'9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0' + +' 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a a b b b b b b b c c c c c c ' + +'d d d d d e e e e e e e e e e e e e e g g g g g g g g g g g g g h h h i j j' + +' j j j j j k k k k k l l l l l l m m m m m m 8$6$6$6$|;o o o p p p p p q q ' + +'q q q q q q q r r s s s s s s s t t t t u u u u v v v v v K#K#G#G#x#x#x#x#x' + +'#x#x#x#x#h#h#h#h#h#H#z z z A A B B B B B B C C C C D D D D E E E E E E F G ' + +'G G G G G G G H H d@e@e@e@_@I I J J J J c.K K K K L A-S+S+S+<*M M j.N N N N' + +' N N O P P Q Q k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+*+*+=+=+=+' + +'=+-+-+-+;+M.I+` +.+.+.@.#.#.#.#.#.d.$.$.Z%!+,+''+''+)+!+!+!+~+~+~+{+{+`+-.-' + +'.-.-.;.;.;.;.;.>.,.''.''.u&0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@' + +'6@7@7@7@m@m@m@O*z@A@%#[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.J@X@X@H@H@=#=#=#=#=#8#n' + +'#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=V=t-h=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-' + +'w-w-&;&;&;&;(;(;H-'';'';",'#13#10'"k j j j j j j j j i h h h g g g g g g g ' + +'g g g g g g e e e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b' + +' b b a a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ' + +'0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0' + +' 0 0 0 0 0 0 a a a a a a a a a a a a a a b b b b b b b b c c c c c c d d d ' + +'d d e e e e e e e e e e e e e e e g g g g g g g g g g g g g h h h i j j j j' + +' j j j j k k k k l l l l l l m m m m m m m n 8$6$3$3$K*o p p p p p h.q q q ' + +'q q q q q q r r s s s s s s s t t t t u u u u v v v v v v G#G#G#x#x#x#x#x#x' + +'#x#x#x#h#h#h#h#h#h#`#z z A A B B B B B B C C C C D D D D E E E E E E b.F G ' + +'G G G G G G H H H E@e@e@e@+&I J J J J J c.K K K L L g+S+S+S+v+M j.N N N N N' + +' N O P P Q Q N 3+3+3+4+4+4+4+5+5+5+5+6+6+%+%+%+%+&+y+*+*+*+*+*+*+*+=+=+=+=+' + +'-+-+-+;+;+;+O.O.-&(%^*#.#.#.#.#.#.d.$.$.S.,+''+''+''+!+!+!+~+~+~+~+{+]+Z+-.' + +'-.-.-.;.;.;.;.>.,.,.''.''._+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@]@6@6' + +'@7@7@7@7@m@m@m@z@z@A@^@[.[.[.}.}.}.}.}.}.|.1.1.1.1.f.%=X@H@H@H@=#=#=#=#=#n#' + +'n#n#n#n#n#M#R&R&f#f#0=0=0=}=}=}=f=f=f=f=1=1;2;T=K=K=K=]-(-(-(-(-(-(-l-l-w-w' + +'-w-w-&;&;&;&;(;(;3;'';'';",'#13#10'"k k k j j j j j j j j i i h h g g g g g' + +' g g g g g g g g f e e e e e e e e e e e e e e e d d d d d c c c c c c c b ' + +'b b b b b b b b a a a a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0' + +' 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ' + +'a a a a a a a a a a a a a a a a b b b b b b b b b c c c c c c c d d d d d e' + +' e e e e e e e e e e e e e e f g g g g g g g g g g g g g h h i i j j j j j ' + +'j j j k k k k k l l l l l l m m m m m m m n n 8$3$3$3$K*p p p p p q q q q q' + +' q q q q r r s s s s s s s t t t t u u u u u v v v v v v `#G#G#x#x#x#x#x#x#' + +'x#x#x#h#h#h#h#h#h#d#`#z A A B B B B B B C C C C D D D D E E E E E E b.F G G' + +' G G G G G H H H H [@e@e@}@I J J J J J c.K K K L L L w+S+S+S+4;j.N N N N N ' + +'N O O P Q Q Q N 3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=+=+-' + +'+-+-+-+;+;+>+O.O.O.O.N.x=;&#.#.#.d.$.M&!+,+,+''+''+!+!+!+~+~+~+~+{+{+]+Y+-.' + +'-.-.;.;.;.;.;.>.,.''.''.''.~@m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@' + +'6@7@7@7@m@m@m@O*z@A@A@n@[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.|.X@H@H@=#=#=#=#=#8#n' + +'#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=N=N=N=q=@-S-c-(-(-(-(-(-l-l-w-w-' + +'w-w-&;&;&;&;(;(;(;'';'';'';",'#13#10'"k k k k k j j j j j j j j i i h h g g' + +' g g g g g g g g g g g g e e e e e e e e e e e e e e e e d d d d d d c c c ' + +'c c c c b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a 0 0 0' + +' 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a ' + +'a a a a a a a a a a a a b b b b b b b b b b c c c c c c c d d d d d d e e e' + +' e e e e e e e e e e e e e g g g g g g g g g g g g g g h h i i j j j j j j ' + +'j j k k k k k l l l l l l m m m m m m m n n n o <$3$3$<$''$p p p p q q q q ' + +'q q q q q r r s s s s s s s t t t t u u u u u v v v v v v w `#G#x#x#x#x#x#x' + +'#x#x#x#h#h#h#h#h#h#d#d#q#A A B B B B B B C C C C D D D D E E E E E E b.F G ' + +'G G G G G G i.H H H H e@e@}@[@I J J J J c.K K K L L L M ]*S+S+S+K&N N N N N' + +' N N O P Q Q Q Q f&3+3+4+4+4+5+5+5+5+5+@+#%$+%+%+&+&+*+*+*+*+*+*+*+*+=+=+=+' + +'=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.l+x=l*v$+*,+,+,+''+''+''+!+!+!+~+~+~+~+{+]+]+' + +'b+-.-.-.;.;.;.;.>.,.,.''.''.''.m+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@.@]@]@]@]@]@' + +'6@6@7@7@7@m@m@m@m@z@z@A@A@A@:.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.H@H@H@=#=#=#=#=' + +'#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=k=G-X=b-K=(-(-l-l-' + +'w-w-w-w-&;&;&;&;(;(;5;'';'';'';",'#13#10'"l l k k k k k j j j j j j j j i i' + +' h h h g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e e d d d ' + ,'d d d c c c c c c c c b b b b b b b b b b b a a a a a a a a a a a a a a a a' + +' a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a ' + +'a a a a a a a a b b b b b b b b b b b c c c c c c c c d d d d d d e e e e e' + +' e e e e e e e e e e e e g g g g g g g g g g g g g g h h h i i j j j j j j ' + +'j j k k k k k l l l l l l m m m m m m m n n n o o o <$<$<$<$b$p p q q q q q' + +' q q q q q r r s s s s s s s t t t t u u u u v v v v v v v w w `#x#x#x#x#x#' + +'x#x#x#x#h#h#h#h#h#h#d#d#d#Z@A B B B B B B C C C C D D D D E E E E E E b.F G' + +' G G G G G G i.H H H H H e@}@}@=@J J J J c.K K K L L L M M ]*S+S+S+1+N N N ' + +'N N N O P P Q Q Q R f&3+4+4+4+5+5+5+5+(&-%V V V -%U+V&*+*+*+*+*+*+*+*+=+=+=' + +'+=+-+-+-+;+;+;+O.O.O.O.O.N.N.N.N.N.N.l+,+,+,+,+''+''+''+!+!+!+~+~+~+~+{+{+]' + +'+]+:%-.-.;.;.;.;.;.>.,.''.''.''.''.m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@' + +']@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@:.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.=#H@=#=#=#=' + +'#=#8#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=N=N=N=N=N=N=i-q=q=q=q=|-k-' + +'W-w-w-w-w-w-&;&;&;(;(;(;6;'';'';'';",'#13#10'"l l l l k k k k k j j j j j j' + +' j j i i h h h g g g g g g g g g g g g g g g e e e e e e e e e e e e e e e ' + +'e e e d d d d d d c c c c c c c c c b b b b b b b b b b b b b a a a a a a a' + +' a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a ' + +'a a a b b b b b b b b b b b b b c c c c c c c c c d d d d d d e e e e e e e' + +' e e e e e e e e e e e g g g g g g g g g g g g g g g h h h i i j j j j j j ' + +'j j k k k k k l l l l l l m m m m m m m m n n n o o o o <$<$<$<$b$h.q q q q' + +' q q q q q r r s s s s s s s t t t t t u u u u v v v v v v v w w x `#x#x#x#' + +'x#x#x#x#x#h#h#h#h#h#d#d#d#d#:#O%B B B B B B C C C C D D D D E E E E E E b.F' + +' G G G G G G G i.H H H H H %@}@}@}@Q+J J J c.K K K K L L M M M /&S+S+x+7&N ' + +'N N N N O P P Q Q Q R R }+4+4+4+4+5+5+5+5+H.U V V V V W W V -%U+#+*+*+*+=+=' + +'+=+=+-+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+,+''+''+!+!+!+~+~+~+~+{+{+' + +']+]+]+H$-.;.;.;.;.;.>.,.''.''.''.''.).m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]' + +'@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@:.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.8#H@=#=#' + +'=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=N=q=q=q=q=q=q' + +'=y-y-K-7;8;9;&;&;&;&;(;(;0;'';'';'';'';",'#13#10'"l l l l l l k k k k k j j' + +' j j j j j j i i i h h g g g g g g g g g g g g g g g f e e e e e e e e e e ' + +'e e e e e e e e e d d d d d d d c c c c c c c c c c b b b b b b b b b b b b' + +' b b b b b b a a a a a a a a a a a a a a a a a a a a a a a a a b b b b b b ' + +'b b b b b b b b b b b b c c c c c c c c c c d d d d d d d e e e e e e e e e' + +' e e e e e e e e e e f g g g g g g g g g g g g g g g h h i i i j j j j j j ' + +'j j k k k k k l l l l l l m m m m m m m m n n n o o o o p p <$<$<$<$!$q q q' + +' q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v w w w x x `#x#' + +'x#x#x#x#x#r&h#h#h#h#h#d#d#d#d#:#:#y B B B B B C C C C D D D D E E E E E E b' + +'.F G G G G G G G i.H H H H H H +&}@}@*@Q+J J c.K K K K L L M M M M /&S+x+h+' + +'7&N N N N O P P Q Q Q R R R <=4+4+4+5+5+5+5+q$U V V V V W W X X X X X h$~&K' + +'.=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.N.,+,+,+,+,+''+''+''+!+!+!+~+~+~+~' + +'+{+]+]+]+]+b+-.;.;.;.;.>.,.,.''.''.''.''.).a;m+J+J+K+K+K+K+K+ @ @.@.@.@.@{@' + +']@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.8#=' + +'#=#=#=#=#8#n#n#n#n#n#M#R&R&o#v#b;*-7=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=i-q=q=q=' + +'q=q=y-y-G-G-G-G-G-!;c;d;(;(;e;!;'';'';'';'';",'#13#10'"m m l l l l l l k k ' + +'k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g g g e e e e e' + +' e e e e e e e e e e e e e e e d d d d d d d d c c c c c c c c c c c c b b ' + +'b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b' + +' b b b b b b b c c c c c c c c c c c c d d d d d d d d e e e e e e e e e e ' + +'e e e e e e e e e e g g g g g g g g g g g g g g g g h h h i i j j j j j j j' + +' j k k k k k k l l l l l l m m m m m m m m n n n o o o o p p p p ];<$>$>$!$' + +'q q q q q q q r r s s s s s s s s t t t t u u u u v v v v v v v w w w x x x' + +' `#x#x#x#x#x#r&h#h#h#h#h#d#d#d#:#:#:#:#y B B B B C C C C D D D D E E E E E ' + +'E b.F G G G G G G G i.H H H H H H H %@}@*@*@Q+J c.K K K K L L M M M M M x+x' + +'+h+h+|+N N N O O P Q Q Q Q R R R <=4+4+5+5+5+5+6+,*V V V V V W X X X X X X ' + +'Y Y Z =&>%,%O.-+-+;+;+;+O.O.O.O.O.N.N.N.N.N.N.l+,+,+,+,+''+''+''+!+!+!+~+~+' + +'~+~+{+{+]+]+]+]+b+;.;.;.;.;.>.,.''.''.''.''.).).0&J+J+K+K+K+K+K+K+ @.@.@.@.' + +'@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@&#}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.' + +'3.H@=#=#=#=#=#n#n#n#n#n#n#M#}=n-8.8.8.9.9.2*f;1=f=f=f=f=f=1=N=N=N=N=N=N=q=q' + +'=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-g;'';'';'';'';'';'';",'#13#10'"m m m m l l l' + +' l l l k k k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g g ' + +'g g e e e e e e e e e e e e e e e e e e e e e e d d d d d d d d d c c c c c' + +' c c c c c c c c c c c c c b b b b b b b b b b b b b b b b b b b b b b b c ' + +'c c c c c c c c c c c c c c c c c d d d d d d d d d e e e e e e e e e e e e' + +' e e e e e e e e e e g g g g g g g g g g g g g g g g g h h h i i j j j j j ' + ,'j j j k k k k k k l l l l l l m m m m m m m m n n n n o o o o p p p p p ];>' + +'$>$>$!$q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v v w w w ' + +'x x x x `#x#x#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#y B B B C C C C D D D D E E E' + +' E E E b.F G G G G G G G i.H H H H H H H I %@*@*@*@/&J K K K K L L M M M M ' + +'M M x+h+h+h+|+N N O O P Q Q Q Q R R R S `.4+5+5+5+5+5+6+-%V V V V W X X X X' + +' X X Y Y Z Z Z Z ` ` (%F%M.;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+,+''+''+!+!+!+' + +'~+~+~+~+{+{+]+]+]+]+]+Y+;.;.;.;.>.,.,.''.''.''.).).).`%J+J+K+K+K+K+K+ @ @.@' + +'.@.@.@{@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@B@}.}.}.}.}.|.|.1.1.1.f.2.2' + +'.2.3.1.=#=#=#=#=#8#n#n#n#n#n#M#}=2*8.8.8.9.9.9.+=+=+=.-V=f=f=1=1=N=N=N=N=N=' + +'k=q=q=q=q=q=y-y-G-G-G-G-G-T-T-T-K-K-K-'';'';'';'';'';'';",'#13#10'"m m m m ' + +'m m l l l l l l k k k k k k j j j j j j j j j i i h h h g g g g g g g g g g' + +' g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e d d d d d ' + +'d d d d d d c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c' + +' c c c c c c c c c c c d d d d d d d d d d d e e e e e e e e e e e e e e e ' + +'e e e e e e e e e f g g g g g g g g g g g g g g g g g h h h i i j j j j j j' + +' j j j k k k k k k l l l l l l m m m m m m m m m n n n o o o o p p p p p p ' + +'q ~$>$>$>${$q q q q r r s s s s s s s s t t t t u u u u u v v v v v v v w w' + +' x x x x x x `#x#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#y B B C C C C D D D D E ' + +'E E E E E b.F G G G G G G G i.H H H H H H H I I N+*@*@*@/&c.K K K L L L M M' + +' M M M M h+h+h+h+|+N N O P P Q Q Q R R R S S `.5+5+5+5+5+6+6+h$V V V W W X ' + +'X X X X X Y Z Z Z Z ` ` ` ` . .L&F+-&O.O.P.N.N.N.N.N.N.,+,+,+,+,+''+''+''+' + +'!+!+!+~+~+~+~+{+]+]+]+]+]+]+_+;.;.;.>.,.,.''.''.''.''.).).).1&J+K+K+K+K+K+K' + +'+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@A@A@A@A@A@A@z&}.}.}.}.|.|.1.1.1.' + +'1.2.2.2.3.3.0#=#=#=#=#8#n#n#n#n#n#n#p-n-7.8.8.8.9.9.+=+=+=4=4=4=h=--h;N=N=N' + +'=N=N=N=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-'';'';'';'';'';'';i;",'#13#10'"m' + +' m m m m m m m l l l l l l l k k k k k j j j j j j j j j i i i h h h g g g ' + +'g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e' + +' e e e e e d d d d d d d d d d d d d d d d d d d c c c c c c c c c c c c c ' + +'d d d d d d d d d d d d d d d d d d d e e e e e e e e e e e e e e e e e e e' + +' e e e e e e e e f g g g g g g g g g g g g g g g g g g h h h i i i j j j j ' + +'j j j j j k k k k k l l l l l l l m m m m m m m m m n n n o o o o p p p p p' + +' p h.q q |$>$>$>${$q q q r r s s s s s s s t t t t t u u u u v v v v v v v ' + +'w w w x x x x x x x q#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#:#L@B C C C C D D D' + +' D E E E E E E b.F G G G G G G G i.H H H H H H H I I J N+*@*@*@Y%K K K L L ' + +'L M M M M M M N h+h+h+h+|+N O P P Q Q Q R R R S S S P 5+5+5+5+6+6+%+V V V W' + +' W X X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.` j;R.N.N.N.N.N.l+,+,+,+,+''+' + +'''+''+!+!+!+~+~+~+~+{+{+]+]+]+]+]+7+(+;.;.;.>.,.''.''.''.''.).).).!.9&J+K+K' + +'+K+K+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@7@m@m@m@z@z@A@A@A@A@A@W@J@}.}.}.}.|.' + +'|.1.1.1.f.2.2.2.3.3.p==#=#=#=#8#n#n#n#n#n#M#y=7.8.8.8.9.9.9.+=+=+=4=4=4=4=D' + +'=D=;-X=j-N=N=k=q=q=q=q=q=q=y-G-G-G-G-G-T-T-T-T-K-K-'';'';'';'';'';'';k;",' + +#13#10'"n m m m m m m m m m l l l l l l l k k k k k k j j j j j j j j j i i ' + +'i h h h g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e' + +' e e e e e e e e e e e e e e e e e e e d d d d d d d d d d d d d d d d d d ' + +'d d d d d d d d d d d d d e e e e e e e e e e e e e e e e e e e e e e e e e' + +' e e e e e e e e f g g g g g g g g g g g g g g g g g g g h h h i i i j j j ' + +'j j j j j j k k k k k k l l l l l l l m m m m m m m m m n n n o o o o p p p' + +' p p p p q q q q |$>$>$>$l;q r r s s s s s s s s t t t t u u u u u v v v v ' + +'v v v w w w x x x x x x x x q#r&h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#:#:#m%C C C C' + +' D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J N+*@*@|@R-K ' + +'K L L L M M M M M M N N m;h+h+h+|+O P P Q Q Q R R R S S S S F.5+5+5+6+6+%+$' + +'+V V W W X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.^*I+Q.N.l+,+,+,+' + +',+,+''+''+!+!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+0+;.;.>.,.,.''.''.''.''.).).).!.!' + +'.y*K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@A@B@}.}.}.' + +'}.|.|.1.1.1.1.2.2.2.3.3.4.d==#=#=#8#n#n#n#n#n#n#0=7.7.8.8.8.9.9.+=+=+=4=4=4' + +'=4=4=D=K=K=K=K=2-|-q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-'';'';'';'';'';i;k' + +';",'#13#10'"n n n m m m m m m m m m l l l l l l l k k k k k k j j j j j j j' + +' j j j i i h h h h g g g g g g g g g g g g g g g g g g g g f e e e e e e e ' + +'e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e' + +' e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e ' + +'e e e e e e e f g g g g g g g g g g g g g g g g g g g g h h h h i i j j j j' + +' j j j j j j k k k k k k l l l l l l l m m m m m m m m m n n n o o o o o p ' + +'p p p p p q q q q q q |$>$>$>$_-r r s s s s s s s t t t t t u u u u u v v v' + +' v v v v w w x x x x x x x x x x y h#h#h#h#h#d#d#d#d#:#:#:#:#:#:#:#:#)#{*C ' + +'C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J N+*@|@=' + ,'@R-K L L L M M M M M M j.N N Y%h+h+h+|+P P Q Q Q Q R R R S S S T T 5+5+5+6+' + +'%+%+q$V V W X X X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.t$V.!' + +'+,+,+,+''+''+''+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+;.;.>.,.''.''.''.''.).).)' + +'.!.!.~.r+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@V*' + +'}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.e#=#=#=#8#n#n#n#n#n#M#f;7.8.8.8.9.9.9.+=+=+' + +'=4=4=4=4=D=D=K=K=K=K=K=]-K=2-|-q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-'';'';'';'';''' + +';'';k;k;",'#13#10'"o o n n n m m m m m m m m m l l l l l l l l k k k k k k ' + +'j j j j j j j j j j i i i h h h g g g g g g g g g g g g g g g g g g g g g g' + +' f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e ' + +'e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e' + +' e e e e e f g g g g g g g g g g g g g g g g g g g g g g h h h i i i j j j ' + +'j j j j j j j k k k k k k l l l l l l l l m m m m m m m m m n n n o o o o o' + +' p p p p p p q q q q q q q q [$>$>$,$_-s s s s s s s s t t t t u u u u u v ' + +'v v v v v v w w w x x x x x x x x x x y y h#h#h#h#d#d#d#d#:#:#:#:#:#:#:#:#)' + +'#)#Z@C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J J ' + +'N+|@=@=@s@L L L M M M M M M j.N N N Y%h+h+i+|+P Q Q Q Q R R R S S S T T T 5' + +'+5+6+%+%+%+%+V W r.X X X X X X Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.' + +'#.d.$.$.M&+*n;''+''+!+!+!+~+~+~+~+{+{+]+]+]+]+]+7+7+7+7+<+>.,.''.''.''.''.)' + +'.).).).!.!.~.~.&%K+K+ @ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@' + +'A@H@}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.n#=#=#8#n#n#n#n#n#n#M#n-7.8.8.9.9.9.+' + +'=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-K=k-z-y-G-G-G-G-T-T-T-T-K-K-K-'';'';''' + +';'';'';i;k;k;",'#13#10'"o o o o n n n m m m m m m m m m l l l l l l l l k k' + +' k k k k k j j j j j j j j j j i i i h h h h g g g g g g g g g g g g g g g ' + +'g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e' + +' e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e ' + +'e e f g g g g g g g g g g g g g g g g g g g g g g g g h h h h i i i j j j j' + +' j j j j j j k k k k k k k l l l l l l l l m m m m m m m m m n n n o o o o ' + +'o p p p p p p q q q q q q q q q q [$,$,$$$F#s s s s s s t t t t t u u u u u' + +' v v v v v v v w w w x x x x x x x x x x y y y :#h#h#d#d#d#:#:#:#:#:#:#:#:#' + +':#)#)#)#Z@C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J' + +' J J v@=@=@=@s@L L M M M M M M j.N N N N k+h+i+i+A$Q Q Q Q R R R S S S T T ' + +'T T (&6+6+%+%+%+%+W W X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.#.#' + +'.#.#.d.$.$.$.$.$.%.u*S.!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+7+Y+,.,.''.''.''.' + +'''.).).).!.!.~.~.~.w@K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@' + +'A@A@W@7#}.}.}.}.|.1.1.1.1.f.2.2.3.3.3.4.0#=#=#=#n#n#n#n#n#n#M#}=7.8.8.8.9.9' + +'.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-W-H-T-G-G-T-T-T-T-K-K-'';'';' + +''';'';'';'';k;k;k;",'#13#10'"p o o o o o n n n n m m m m m m m m m l l l l ' + +'l l l l k k k k k k j j j j j j j j j j j i i i h h h h h g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e ' + +'e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g' + +' g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h i i i j j j j ' + +'j j j j j j j k k k k k k l l l l l l l l m m m m m m m m m n n n n o o o o' + +' o p p p p p p h.q q q q q q q q q q r V#,$$$$$($s s s s s t t t t u u u u ' + +'u v v v v v v v w w w x x x x x x x x x x a.y y y y :#h#d#d#d#:#:#:#:#:#:#:' + +'#:#:#)#)#)#)#c*D D D E E E E E E E b.F G G G G G G G i.H H H H H H H I I J ' + +'J J J J c.K =@=@=@/&L M M M M M M j.N N N N N g+h+i+j+7&Q Q Q R R R S S S S' + +' T T T T T+6+%+%+%+%+&+T X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.@.#.' + +'#.#.#.#.d.$.$.$.$.$.%.%.%.&.t$T.~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+7+`+,.''.''.' + +'''.''.).).).!.!.!.~.~.~.~.R% @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A' + +'@A@A@A@A@I@}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.d==#=#8#n#n#n#n#n#n#M#P=7.8.8.' + +'9.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-8;3;T-T-T-K-K-K-' + +''';'';'';'';'';i;k;k;k;",'#13#10'"p p p o o o o o n n n n m m m m m m m m m' + +' l l l l l l l l k k k k k k k j j j j j j j j j j j j i i i h h h h g g g ' + +'g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g f e e e e e e e' + +' e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g ' + +'g g g g g g g g g g g g g g g g g g g g g g g g g h h h h i i i j j j j j j' + +' j j j j j j k k k k k k k l l l l l l l l m m m m m m m m m n n n n o o o ' + +'o o p p p p p p h.q q q q q q q q q q q r r K*$$$$$$K*s s s t t t t t u u u' + +' u u v v v v v v v w w w x x x x x x x x x x y y y y y y B#d#d#d#:#:#:#:#:#' + +':#:#:#:#)#)#)#)#.#''-D D E E E E E E E F F G G G G G G G i.H H H H H H H I ' + +'I J J J J J c.K K R+=@R+/&M M M M M M j.N N N N N N g+i+j+j+1+Q Q R R R S S' + +' S S T T T T U #+%+%+%+%+%+&+V X X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.' + +'@.#.#.#.#.#.#.$.$.$.$.$.%.%.%.&.&.&.*.(*T.~+{+{+]+]+]+]+]+7+7+7+7+7+8+9+;.' + +'''.''.''.).).).).!.!.~.~.~.~.~.].G@.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@' + ,'A@A@A@A@A@A@^#}.}.}.}.}.|.1.1.1.1.f.2.2.3.3.3.4.4.f#=#=#n#n#n#n#n#n#M#R&H=8' + +'.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;e;o;!;K-' + +'K-'';'';'';'';'';'';i;k;k;k;",'#13#10'"p p p p p o o o o o n n n n m m m m ' + +'m m m m m m l l l l l l l l k k k k k k k k j j j j j j j j j j j j i i i h' + +' h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g ' + +'g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g g g g g h h h h h i i i j j j j j j j ' + +'j j j j j k k k k k k k k l l l l l l l l m m m m m m m m m m n n n n o o o' + +' o o p p p p p p h.q q q q q q q q q q q r r s s F#$$$$$$Y-s t t t t t u u ' + +'u u u v v v v v v v v w w w x x x x x x x x x x y y y y y y z p;d#d#:#:#:#:' + +'#:#:#:#:#:#)#)#)#)#.#.#C@D E E E E E E E F G G G G G G G G i.H H H H H H H ' + +'I I J J J J J c.K K K &&R+R+x+M M M M M M N N N N N N N 2+j+j+k+R$Q R R R S' + +' S S S T T T T T U #%%+%+%+%+&+y+E.X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.' + +'+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.*.*.*.q;&*{+]+]+]+]+]+]+7+7+7+7+8+9+9' + +'+:+''.''.''.).).).!.!.~.~.~.~.~.{.].^.5@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z' + +'@z@A@A@A@A@A@^#}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.b;=#=#8#n#n#n#n#n#n#M#}*' + +'8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(' + +';r;c;k;'';'';'';'';i;k;k;k;k;",'#13#10'"h.p p p p p p p o o o o o n n n m m' + +' m m m m m m m m l l l l l l l l l k k k k k k k k j j j j j j j j j j j j ' + +'j i i i h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g ' + +'g g g g g g g g g g g g g g g g g g h h h h h h i i i j j j j j j j j j j j' + +' j j k k k k k k k k l l l l l l l l l m m m m m m m m m m n n n o o o o o ' + +'p p p p p p p h.q q q q q q q q q q q r r r s s s {$$$$$$$V#t t t t t u u u' + +' u u v v v v v v v w w w x x x x x x x x x x a.y y y y y y z z g#:#:#:#:#:#' + +':#:#:#:#)#)#)#)#)#.#.#.#C@E E E E E E E F G G G G G G G G i.H H H H H H H I' + +' I J J J J J c.K K K K Z*R+S+S+M M M M M N N N N N N N O C$j+k+k+x%R R R R ' + +'S S S T T T T T U V ,*%+%+%+&+&+*+G.X X X X Y Z Z Z Z Z ` ` ` . . .+.+.+.+' + +'.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.k.]+]+]+]+]+]+7+7+7+7+7+8+9+' + +'0+~@''.''.).).).!.!.!.~.~.~.~.{.].^.^.^.p+.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z' + +'@A@A@A@A@A@B@}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.4.9#=#=#n#n#n#n#n#n#M#R&s;' + +'8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(' + +';(;t;t;u;!;'';'';i;k;k;k;k;",'#13#10'"q q h.p p p p p p p o o o o o n n n n' + +' m m m m m m m m m m l l l l l l l l l k k k k k k k k j j j j j j j j j j ' + +'j j j j i i i i h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g ' + +'g g g g g g g g g g g g h h h h h h i i i i j j j j j j j j j j j j j j k k' + +' k k k k k k l l l l l l l l l m m m m m m m m m m n n n n o o o o o p p p ' + +'p p p p h.q q q q q q q q q q q r r r s s s s s {$$$$$+$K#t t t u u u u u v' + +' v v v v v v v w w w x x x x x x x x x x y y y y y y y z z z 4#:#:#:#:#:#:#' + +':#:#)#)#)#)#)#.#.#.#.#M@E E E E E E F G G G G G G G G i.H H H H H H H I I J' + +' J J J J c.K K K K L <@S+S+S+c.M M M N N N N N N N O P R$k+k+k+f+R R R S S ' + +'S T T T T T U V V H.%+%+&+&+*+*+I.X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.@' + +'.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.]+]+]+]+]+7+7+7+7+7+8+9+0+0+' + +'0+<+).).).).!.!.~.~.~.~.~.{.].^.^.^.^.x@p%]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A' + +'@A@A@7%}.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.4.f.n#=#8#n#n#n#n#n#n#R&R&b;8.8.' + +'9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t' + +';t;v;e;!;i;k;k;k;k;k;",'#13#10'"q q q q h.p p p p p p p o o o o o n n n n m' + +' m m m m m m m m m m l l l l l l l l l k k k k k k k k k j j j j j j j j j ' + +'j j j j j j i i i i h h h h h h h h g g g g g g g g g g g g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g ' + +'g g h h h h h h h h i i i i j j j j j j j j j j j j j j j k k k k k k k k k' + +' l l l l l l l l l m m m m m m m m m m m n n n n o o o o o p p p p p p p h.' + +'q q q q q q q q q q q r r r s s s s s s s U-+$+$+$Y#t u u u u u u v v v v v' + +' v v w w w x x x x x x x x x x x y y y y y y z z z z A y#:#:#:#:#:#:#:#)#)#' + +')#)#)#.#.#.#.#N@d@E E E E b.F G G G G G G G G i.H H H H H H H I I J J J J J' + +' c.K K K K L L <*S+S+S+c.M M N N N N N N N O P P |+k+k+k+=%R R S S S T T T ' + +'T T U U V V -%%+&+&+*+*+*+#%X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#' + +'.#.#.d.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.Y+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+~@).' + +').).!.!.~.~.~.~.~.{.].^.^.^.^.e./.].p+]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@B@a@[' + +'.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.4./==#=#n#n#n#n#n#n#M#R&R&5.8.8.9.9.+=' + +'+=+=+=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v' + +';w;i;k;k;k;k;k;",'#13#10'"q q q q q q h.p p p p p p p o o o o o o n n n n m' + +' m m m m m m m m m m l l l l l l l l l l k k k k k k k k k j j j j j j j j ' + ,'j j j j j j j j i i i i i i h h h h h h h h h g g g g g g g g g g g g g g g' + +' g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h h h h h ' + +'i i i i i i j j j j j j j j j j j j j j j j k k k k k k k k k l l l l l l l' + +' l l l m m m m m m m m m m m n n n n o o o o o o p p p p p p p h.q q q q q ' + +'q q q q q q r r r s s s s s s s s s X#+$+$+$+$u u u u u v v v v v v v v w w' + +' w x x x x x x x x x x a.y y y y y y z z z z A A 5&:#:#:#:#:#:#)#)#)#)#.#.#' + +'.#.#.#N@N@u%E E E b.F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K' + +' K L L M f+S+S+S+t&M N N N N N N N O P P Q K%k+k+3+3+R S S S T T T T T U U ' + +'V V V h$&+&+*+*+*+*+*+X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$' + +'.$.$.$.$.%.%.&.&.&.&.*.*.*.*.=.-.8+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+c+).!.!.' + +'!.~.~.~.~.{.].].^.^.^.^.e./././.].^@7@6@7@7@7@m@m@m@m@z@A@A@%#a@[.[.}.}.}.}' + +'.}.}.|.1.1.1.1.f.2.2.2.3.3.4.4.f.n#=#8#n#n#n#n#n#M#R&R&f#8.8.9.9.9.+=+=+=4=' + +'4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;3;k' + +';k;k;k;k;",'#13#10'"q q q q q q q q q p p p p p p p p o o o o o n n n n n m' + +' m m m m m m m m m m l l l l l l l l l l k k k k k k k k k k j j j j j j j ' + +'j j j j j j j j j j j j i i i i i i i h h h h h h h h h h h h h h h h h h g' + +' g g g g g g g g h h h h h h h h h h h h h h h h h h i i i i i i i j j j j ' + +'j j j j j j j j j j j j j j j k k k k k k k k k k l l l l l l l l l l m m m' + +' m m m m m m m m n n n n n o o o o o p p p p p p p p q q q q q q q q q q q ' + +'q r r r s s s s s s s s s t t N#+$+$W#W#t u u v v v v v v v v w w w x x x x' + +' x x x x x x x y y y y y y y z z z A A A B (#:#:#:#:#)#)#)#)#)#.#.#.#.#N@N@' + +'N@N@6#E E b.F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K L L L' + +' M v+S+S+S+u+N N N N N N N O O P Q Q z$k+3+3+3+P S S S T T T T U U V V V V ' + +'V *&*+*+*+*+*+*+;%Y Z Z Z Z ` ` ` ` . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$' + +'.%.%.%.&.&.&.*.*.*.*.=.=.Z+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+a;''=!.!.~.~.~' + +'.~.~.{.].^.^.^.^.e./././././.(.(.N*U@9@&#&#&# @_=a@[.[.[.[.}.}.}.}.}.}.|.' + +'|.1.1.1.1.2.2.2.3.3.4.4.4.9#=#8#n#n#n#n#n#n#M#R&R&f#8.8.9.9.+=+=+=4=4=4=4=4' + +'=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;y;k;k;k;' + +'k;k;",'#13#10'"q q q q q q q q q q q p p p p p p p p o o o o o o n n n n m ' + +'m m m m m m m m m m m l l l l l l l l l l l k k k k k k k k k k k j j j j j' + +' j j j j j j j j j j j j j j j j j j i i i i i i i i i i i i h h h h h h h ' + +'h h h h h h h h h h i i i i i i i i i i i i j j j j j j j j j j j j j j j j' + +' j j j j j j j k k k k k k k k k k k l l l l l l l l l l l m m m m m m m m ' + +'m m m m n n n n o o o o o o p p p p p p p p q q q q q q q q q q q q r r r s' + +' s s s s s s s s t t t t I#W#W#W#W#s u v v v v v v v v w w w x x x x x x x ' + +'x x x a.y y y y y y z z z z A A B B B H#:#:#:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@r' + +'@E b.F G G G G G G G G H H H H H H H H I I J J J J J c.K K K K L L L M M K&' + +'S+S+S+1+N N N N N N O O P Q Q Q N 3+3+3+4+<=S S T T T T U U V V V V V W z;*' + +'+*+*+*+*+*+/;Z Z Z Z Z ` ` ` . . .+.+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.$.%.%.%.' + +'&.&.&.*.*.*.*.=.=.H$]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+0+m+m+m+)=!.~.~.~.~.~.{.]' + +'.^.^.^.^.^./././././.(.(.(._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.' + +'1.f.2.2.3.3.3.4.4.0#=#=#n#n#n#n#n#n#M#R&R&f#f#j=9.9.9.+=+=+=4=4=4=4=D=D=K=K' + +'=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;#;k;k;k;k;k;",' + +#13#10'"r q q q q q q q q q q q q h.p p p p p p p p o o o o o o n n n n m m ' + +'m m m m m m m m m m m l l l l l l l l l l l l k k k k k k k k k k k j j j j' + +' j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j ' + +'j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j k k' + +' k k k k k k k k k l l l l l l l l l l l l m m m m m m m m m m m m m n n n ' + +'n o o o o o o p p p p p p p p h.q q q q q q q q q q q q r r r s s s s s s s' + +' s s t t t t t u j&W#W#W#W#j&v v v v v v v w w w x x x x x x x x x x x y y ' + +'y y y y y z z z z A A B B B B k&:#:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@N@O@F F G G' + +' G G G G G G H H H H H H H H I I J J J J J c.K K K K L L L M M M |%S+S+h+x%' + +'N N N N N O O P Q Q Q Q R 3+3+4+4+ %S T T T T T U V V V V V W W I.*+*+*+*+*' + +'+*+~&Z Z Z Z ` ` ` . . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.*.*.' + +'*.*.=.=.-.8+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+1&~.~.~.~.{.].].^.^.^.^' + +'.e././././.(.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.f.2.2.2.' + +'3.3.4.4.4.f#=#8#n#n#n#n#n#n#M#R&R&f#f#b;9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c' + +'-(-(-(-(-(-l-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;B;k;k;k;k;C;",'#13#10 + +'"r r r q q q q q q q q q q q q q p p p p p p p p o o o o o o n n n n n m m ' + +'m m m m m m m m m m m l l l l l l l l l l l l l k k k k k k k k k k k k k k' + +' j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j ' + +'j j j j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k k' + +' k k l l l l l l l l l l l l l m m m m m m m m m m m m m n n n n n o o o o ' + +'o o p p p p p p p p q q q q q q q q q q q q q r r r s s s s s s s s s t t t' + ,' t t t u u s W#W#K#K#E#v v v v v w w w x x x x x x x x x x x x y y y y y y ' + +'z z z z A A A B B B B B Z@:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@N@N@ #E G G G G G G' + +' G G H H H H H H H H I I J J J J J c.K K K K L L L M M M M t&S+x+h+f+N N N ' + +'N O O P Q Q Q Q R R D;4+4+4+E;T T T T T U V V V V V W W X G.*+*+*+*+*+*+K.Z' + +' Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.=.' + +'-.`+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+m+J+J+K+w@~.~.~.{.].^.^.^.^.e./././' + +'././.(.(._._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.' + +'4.e#=#=#n#n#n#n#n#n#M#R&R&f#f#f#a=9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(' + +'-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;F;k;k;k;k;C;",'#13#10'"s s r' + +' r r q q q q q q q q q q q q q p p p p p p p p p o o o o o o n n n n n m m ' + +'m m m m m m m m m m m m l l l l l l l l l l l l l l k k k k k k k k k k k k' + +' k k k k k j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j ' + +'j j j j j j j j j j j j j j j k k k k k k k k k k k k k k k k k l l l l l l' + +' l l l l l l l l m m m m m m m m m m m m m m n n n n n o o o o o o p p p p ' + +'p p p p p q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u' + +' u u u K#K#K#K#I#v v v v w w w x x x x x x x x x x x y y y y y y y z z z z ' + +'A A B B B B B B B t%)#)#)#)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@v%G G G G G G i.H H' + +' H H H H H I I I J J J J J c.K K K K L L M M M M M M c.x+h+h+g+N N N O O P ' + +'Q Q Q Q R R R R$4+4+4+V&T T T T U V V V V V W W X X V *+*+*+*+*+=+-+=&Z ` `' + +' ` ` . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.%%]+]+' + +']+]+7+7+7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+K+K+&%~.~.{.].^.^.^.^.^./././././.(.(' + +'.(._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.2.3.3.4.4.2#=#=#' + +'8#n#n#n#n#n#n#M#R&f#f#f#0=0=j=+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-l-l' + +'-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;G;k;k;k;k;C;C;",'#13#10'"s s s s r r' + +' r q q q q q q q q q q q q q q p p p p p p p p p o o o o o o o n n n n n m ' + +'m m m m m m m m m m m m m m l l l l l l l l l l l l l l l k k k k k k k k k' + +' k k k k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j ' + +'k k k k k k k k k k k k k k k k k k k k k k k l l l l l l l l l l l l l l l' + +' m m m m m m m m m m m m m m m n n n n n o o o o o o o p p p p p p p p p q ' + +'q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u u u u u v' + +' O#K#K#K#%$v v w w w x x x x x x x x x x x x y y y y y y z z z z A A A B B ' + +'B B B B B C C .#)#)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@r@b@G G G G G i.H H H H H H' + +' H I I I J J J J J c.K K K K L L M M M M M M M j.m;h+h+Y%N N O O P P Q Q Q ' + +'R R R R A$4+4+5+*&T T T U U V V V V W W X X X X *+*+*+*+=+=+=+E+` ` ` ` . ' + +'...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.H$]+]+]+]+]+7+' + +'7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+J+K+K+K+)={.].].^.^.^.^.e./././././.(.(._._._' + +'.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.**S==#=#n#n#n#' + +'n#n#n#M#R&R&f#f#f#0=0=7=7.+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w' + +'-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;H;k;k;k;k;C;I;",'#13#10'"s s s s s s r r r' + +' q q q q q q q q q q q q q q h.p p p p p p p p p o o o o o o o n n n n n m ' + +'m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l k k k k' + +' k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k ' + +'k k k k k k k k k k k k l l l l l l l l l l l l l l l l l l l m m m m m m m' + +' m m m m m m m m m n n n n n o o o o o o o p p p p p p p p p h.q q q q q q ' + +'q q q q q q q q r r r s s s s s s s s s t t t t t t u u u u u u v v v J#K#K' + +'#K#X#w w w w x x x x x x x x x x x y y y y y y y z z z z A A A B B B B B B ' + +'B C C C #)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@r@r@q@G G G G i.H H H H H H H I I I' + +' J J J J J c.K K K K L L M M M M M M M j.N g+h+h+h+N O O P P Q Q Q R R R R ' + +'S %4+5+5+5+T T U U V V V V W W X X X X X #%*+*+=+=+=+=+B+` ` ` . . .+.+.+' + +'.+.+.@.#.#.#.#.#.#.$.$.$.$.$.$.%.%.&.&.&.*.*.*.*.*.=.[+5=]+]+]+]+7+7+7+7+7+' + +'8+9+0+0+0+0+m+m+m+m+J+J+K+K+K+K+K+a-].^.^.^.^.e./././././.(.(._._._._.:.:.:' + +'.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.f.2.2.3.3.3.4.p=S==#=#8#n#n#n#n#n#M#' + +'R&R&f#f#f#0=0=0=}=1=e=h=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&' + +';&;&;(;(;t;t;v;v;v;v;x;A;A;J;k;k;k;C;C;I;",'#13#10'"s s s s s s s s s r r r' + +' q q q q q q q q q q q q q q p p p p p p p p p p o o o o o o o n n n n n n ' + +'m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l l' + +' l l l k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k l ' + +'l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m' + +' m m n n n n n n o o o o o o o p p p p p p p p p p q q q q q q q q q q q q ' + +'q q r r r s s s s s s s s s s t t t t t t u u u u u u v v v v v C&K#K#K#;;w' + +' w x x x x x x x x x x x a.y y y y y y y z z z A A A B B B B B B B C C C C ' + +'C D@.#.#.#.#N@N@N@N@N@N@N@ # #r@r@r@r@~#G G G i.H H H H H H H I I I J J J J' + +' J c.K K K K L L M M M M M M M j.N N f+h+h+h+g@O P P Q Q Q R R R R S S `.5+' + +'5+5+5+F.U U V V V V V W X X X X X X I.*+=+=+=+=+=+L.` ` . . ...+.+.+.+.@.#' + ,'.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.=.t*8+]+]+]+]+7+7+7+7+7+8+9+9+0+' + +'0+0+0+m+m+m+J+J+K+K+K+K+K+K+ @p+^.^.^.^./././././.(.(.(._._._.:.:.:.:.<.<.[' + +'.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.p=n#=#=#8#n#n#n#n#n#n#M#R&R&f#' + +'f#f#0=0=}=}=}=}=f=N=8=--;-D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(' + +';(;t;t;v;v;v;x;A;A;K;k;k;k;k;C;I;I;",'#13#10'"t s s s s s s s s s s r r r q' + +' q q q q q q q q q q q q q q p p p p p p p p p p o o o o o o o o n n n n n ' + +'n m m m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l' + +' l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l ' + +'l l l l l l l l l l l l l m m m m m m m m m m m m m m m m m m m m n n n n n' + +' n o o o o o o o o p p p p p p p p p p q q q q q q q q q q q q q q q r r r ' + +'s s s s s s s s s s t t t t t t u u u u u u v v v v v v v )$K#G#G#C&x x x x' + +' x x x x x x x x y y y y y y y z z z z A A A B B B B B B B C C C C C D `@.#' + +'.#N@N@N@N@N@N@N@N@ #r@r@r@r@r@r@M@G G H H H H H H H H I I I J J J J J c.K K' + +' K K L L M M M M M M M N N N N x%h+h+h+|+P P Q Q Q R R R R S S S T 5+5+5+5+' + +'H.U V V V V V W r.X X X X X X i$*+=+=+=+=+-+-+Q% . . ...+.+.+.+.@.#.#.#.#.#' + +'.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=./+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+0+m+' + +'m+m+J+J+J+K+K+K+K+K+ @ @.@p+^.^.e./././././.(.(._._._.:.:.:.:.<.<.[.[.[.[.[' + +'.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.t#S==#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=' + +'0=}=}=}=f=f=f=f=1=1=j-$-t-;-K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;v' + +';v;v;v;x;A;A;d;k;k;k;C;C;I;I;",'#13#10'"t t t s s s s s s s s s s r r r r q' + +' q q q q q q q q q q q q q q p p p p p p p p p p p o o o o o o o o n n n n ' + +'n n n m m m m m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l' + +' l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l ' + +'l l l m m m m m m m m m m m m m m m m m m m m m m n n n n n n n o o o o o o' + +' o o p p p p p p p p p p p q q q q q q q q q q q q q q q r r r r s s s s s ' + +'s s s s s t t t t t t u u u u u u v v v v v v v v v U#G#G#x#L#x x x x x x x' + +' x x x a.y y y y y y z z z z A A A B B B B B B B C C C C C C D D C@.#N@N@N@' + +'N@N@N@N@O@ #r@r@r@r@r@r@r@W%G H H H H H H H H I I I J J J J J c.K K K L L L' + +' M M M M M M M N N N N N |+h+h+i+R$P Q Q Q R R R R S S S T T &+5+5+6+y&V V ' + +'V V V W r.X X X X X X Y Y -+=+=+=+-+-+-+G+ . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$' + +'.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.u*]+]+]+]+]+7+7+7+7+7+8+a+_+_+~@0+m+m+m+m+J+' + +'J+K+K+K+K+K+K+ @.@.@.@y*H%/././././.(.(._._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}' + +'.}.}.}.|.1.1.1.1.f.2.2.2.3.3.9#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=' + +'}=f=f=f=f=1=1=N=N=N=N=N=|-k-L;(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;x' + +';A;A;A;M;k;k;k;C;I;I;I;",'#13#10'"t t t t t s s s s s s s s s s s r r r q q' + +' q q q q q q q q q q q q q q p p p p p p p p p p p o o o o o o o o o n n n ' + +'n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m m l l l l l' + +' l l l l l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m ' + +'m m m m m m m m m m m m m m m m m m n n n n n n n o o o o o o o o o p p p p' + +' p p p p p p p q q q q q q q q q q q q q q q q r r r s s s s s s s s s s s ' + +'t t t t t t u u u u u u v v v v v v v v v w w O-G#x#x#x#x x x x x x x x x y' + +' y y y y y y z z z z A A A B B B B B B B C C C C C D D D D ''-N@N@N@N@N@N@N' + +'@O@ #r@r@r@r@r@r@r@r@e@H H H H H H H H I I J J J J J c.K K K K L L L M M M ' + +'M M M M N N N N N N g@h+i+j+C$Q Q Q Q R R R S S S T T T V&5+6+6+#+V V V V W' + +' W X X X X X X Y Y Z K.=+=+-+-+-+-+r$ ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.' + +'%.%.%.&.&.&.*.*.*.*.*.=.X+]+]+]+]+]+7+7+7+7+7+`+)%''.''.''.''.''=5@m+m+J+J+' + +'K+K+K+K+K+K+ @.@.@.@.@.@y@/@/././.(.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}' + +'.}.}.|.|.1.1.1.1.2.2.2.3.;#]==#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=' + +'f=f=f=f=1=1=N=N=N=N=N=N=q=q=q=q=|-2;N;l-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x' + +';A;A;t;!;k;k;C;C;I;I;I;",'#13#10'"u t t t t t t s s s s s s s s s s s r r r' + +' r q q q q q q q q q q q q q q q q h.p p p p p p p p p p p o o o o o o o o ' + +'o o n n n n n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m' + +' m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m ' + +'m m m m m m m m m m m m n n n n n n n n o o o o o o o o o o p p p p p p p p' + +' p p p h.q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s t t ' + +'t t t t u u u u u u v v v v v v v v v w w w w C#x#x#x#x#`#x x x x x x y y y' + +' y y y y z z z z A A A B B B B B B B B C C C C C D D D D E m%N@N@N@N@N@N@ #' + +'r@r@r@r@r@r@r@r@r@.&e@_@H H H H H I I I J J J J J c.K K K K L L L M M M M M' + +' M M N N N N N N N O i+j+j+=%Q Q Q R R R S S S T T T T +6+6+%+%+V V V W W ' + +'X X X X X X Y Y Z Z p*=+-+-+-+-+;+>+L&+.+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%' + +'.%.&.&.&.&.*.*.*.*.=.Y+]+]+]+]+]+7+7+7+7+7+`+,.,.''.''.''.''.).).''=6%J+J+K' + +'+K+K+K+K+ @ @.@.@.@.@{@]@]@o=]./.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.[.}.}.}.}.' + +'}.|.|.1.1.1.1.f.2.2.3.E&H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&f#f#f#0=0=0=}=}=}=f=f' + +'=f=f=f=1=N=N=N=N=N=N=k=q=q=q=q=q=q=y-y-.;k-8;9;&;&;&;&;(;(;t;t;t;v;v;v;x;A;' + ,'A;A;O;k;k;k;C;I;I;I;I;",'#13#10'"u u u u t t t t t t s s s s s s s s s s s ' + +'r r r r q q q q q q q q q q q q q q q q q p p p p p p p p p p p p p o o o o' + +' o o o o o o n n n n n n n n n n m m m m m m m m m m m m m m m m m m m m m ' + +'m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m' + +' m m m m n n n n n n n n n n o o o o o o o o o o p p p p p p p p p p p p p ' + +'q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s t t t t t t' + +' u u u u u u u v v v v v v v v v w w w w x x 3#x#x#x#x#Z&x x x x a.y y y y ' + +'y y y z z z z A A A B B B B B B B C C C C C D D D D D E E }#N@N@N@N@O@ #r@r' + +'@r@r@r@r@r@r@r@.&e@e@:@H H H H I I I J J J J J c.K K K K L L L M M M M M M ' + +'M N N N N N N N O O -@j+k+k+Q Q R R R S S S T T T T T E*6+%+%+%+E.V W W X X' + +' X X X X Y Y Z Z Z (%-+-+-+-+;+;+;+(%+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.' + +'k.&.&.&.*.*.*.*.=.I=]+]+]+]+]+]+7+7+7+7+P;Q;,.''.''.''.''.).).).!.!.6%K+K+K' + +'+K+K+K+ @.@.@.@.@{@]@]@]@]@p%#@!%_._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.}.' + +'|.|.1.1.1.f.2.2.J@!=H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f' + +'=f=1=1=N=N=N=N=N=k=q=q=q=q=q=q=y-y-G-G-G-G-G-R;M;u;S;(;(;t;t;v;v;v;v;A;A;A;' + +'T;!;k;k;C;C;I;I;I;I;",'#13#10'"u u u u u u t t t t t t s s s s s s s s s s ' + +'s s r r r q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p o' + +' o o o o o o o o o o o n n n n n n n n n n n n m m m m m m m m m m m m m m ' + +'m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m n n n n n' + +' n n n n n n n o o o o o o o o o o o o p p p p p p p p p p p p p h.q q q q ' + +'q q q q q q q q q q q q q q r r r s s s s s s s s s s s s t t t t t t u u u' + +' u u u u v v v v v v v v v w w w w x x x x q#x#x#x#x# ;x x x y y y y y y y ' + +'z z z z A A A B B B B B B B B C C C C C D D D D D E E E E #N@N@O@ #r@r@r@r' + +'@r@r@r@r@r@e@e@e@e@(=H H H I I I J J J J J c.K K K K L L L M M M M M M M N ' + +'N N N N N N O O P C$k+k+k+z$R R R S S S T T T T T U U %+%+%+%+I.W W X X X X' + +' X X Y Y Z Z Z Z Z O.-+-+;+;+;+O.F$+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.' + +'&.&.*.*.*.*.=.%%]+]+]+]+]+]+7+7+7+7+0+-.,.''.''.''.''.).).).).!.!.~.&%K+K+K' + +'+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@#@(@_.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.' + +'1.1.1.1.2.J@-;H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=f' + +'=1=N=N=N=N=N=N=i-q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-U;[;d;G;v;v;v;x;A;A;5;V;' + +'k;k;k;C;I;I;I;I;I;",'#13#10'"v u u u u u u u t t t t t t t s s s s s s s s ' + +'s s s r r r r q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p' + +' p p p o o o o o o o o o o o o o o n n n n n n n n n n n n n n n n n n n m ' + +'m m m m m m m m m m m m m m m m m m n n n n n n n n n n n n n n n n n n n o' + +' o o o o o o o o o o o o o p p p p p p p p p p p p p p h.q q q q q q q q q ' + +'q q q q q q q q q q r r r r s s s s s s s s s s s t t t t t t t u u u u u u' + +' u v v v v v v v v v w w w w x x x x x x x L#x#x#x#%$x y y y y y y y z z z ' + +'z z A A A B B B B B B B C C C C C C D D D D E E E E E E !#N@ # #r@r@r@r@r@r' + +'@r@r@.&e@e@e@e@e@<@H H I I I J J J J J c.K K K K L L L M M M M M M M N N N ' + +'N N N N O O P P |+k+k+k+2@R R S S S S T T T T T U V #+%+%+%+++W X X X X X X' + +' Y Y Z Z Z Z Z ` F$-+;+;+;+>+O.6-+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.' + +'*.*.*.*.=.Z.]+]+]+]+]+]+7+7+7+7+9+-.,.''.''.''.''.''.).).).!.!.~.~.~.&%K+K+' + +' @.@.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@B@P*:.:.<.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1' + +'.1.**9#=#X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=f=}=}=}=}=f=f=f=f=1=' + +'1=N=N=N=N=N=k=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-'';'';'';!;@;N;W;H;M;k;' + +'k;k;k;C;C;I;I;I;I;I;",'#13#10'"v v v u u u u u u u t t t t t t t s s s s s ' + +'s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q q q p p p p p p' + +' p p p p p p p p p p o o o o o o o o o o o o o o o o o o n n n n n n n n n ' + +'n n n n n n n n n n n n n n n n n n n n n n n n n n n n o o o o o o o o o o' + +' o o o o o o o o p p p p p p p p p p p p p p p p q q q q q q q q q q q q q ' + +'q q q q q q q q r r r r s s s s s s s s s s s s t t t t t t t u u u u u u u' + +' v v v v v v v v v w w w w x x x x x x x x x c#x#x#x#g#y y y y y y y z z z ' + +'z A A A B B B B B B B B C C C C C D D D D D E E E E E E E X; #r@r@r@r@r@r@r' + +'@r@r@.&e@e@e@e@e@e@d@H I I J J J J J J c.K K K K L L L M M M M M M j.N N N ' + +'N N N N O O P P Q z$k+3+3+B$R S S S S T T T T T U V V U+%+%+&+*&X X X X X X' + +' Y Y Z Z Z Z Z ` ` (%;+;+;+>+O.O.O.(%@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.' + +'*.*.*.*.*.H$~-]+]+]+]+]+7+7+7+7+K$[+,.,.''.''.''.''.).).).!.!.!.~.~.~.~.&% ' + +'@ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@7@m@m@Q*{#e-`*[.[.[.[.[.}.}.}.}.}.}.|.%=6=' + +'l#=#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#t#6.9.9.+=+=O=O=.-X=f;h;1' + +'=N=N=N=N=N=N=q=q=q=q=q=q=y-y-G-G-G-G-G-T-T-T-T-K-K-'';'';'';'';'';'';i;k;k;' + +'k;k;k;k;k;C;I;I;I;I;I;I;",'#13#10'"v v v v v v u u u u u u u t t t t t t t ' + +'s s s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q q q' + +' h.p p p p p p p p p p p p p p p p p p p o o o o o o o o o o o o o o o o o ' + +'o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o' + ,' o o p p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q ' + +'q q q q q q r r r r s s s s s s s s s s s s s t t t t t t t u u u u u u u v' + +' v v v v v v v v v w w w w x x x x x x x x x x x B#x#h#h#s#y y y y y z z z ' + +'z A A A B B B B B B B B C C C C C C D D D D E E E E E E E E b.M%r@r@r@r@r@r' + +'@r@r@r@e@e@e@e@e@e@e@e@*@I I J J J J J c.c.K K K K L L M M M M M M M j.N N ' + +'N N N N N O O P Q Q Q Q 3+3+3+=%S S S S T T T T T U V V V G.%+&+&+*+i$X X X' + +' X X Y Z Z Z Z Z ` ` ` ` P.;+>+O.O.O.O.-&#.#.#.#.#.#.d.$.$.$.$.$.$.%.%.&.&.' + +'&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+[+,.,.''.''.''.''.).).).).!.!.~.~.~.~.~' + +'.{.L+.@.@.@.@{@]@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@|#B@%#z#z#7%z#z#B@N-]#' + +']#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&f#f#{&8.9.9.+=+=+=4=4=4=4=4' + +'=D=K=;-q-.;G-N=k=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-'';'';'';'';'';'';k;' + +'k;k;k;k;k;k;C;C;I;I;I;I;I;Y;",'#13#10'"v v v v v v v v u u u u u u u t t t ' + +'t t t t t s s s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q' + +' q q q q q q q h.p p p p p p p p p p p p p p p p p p p p p p p o o o o o o ' + +'o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o p p p p p' + +' p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q ' + +'q q q q q r r r r s s s s s s s s s s s s s t t t t t t t t u u u u u u u v' + +' v v v v v v v v v w w w w x x x x x x x x x x x x x Z;h#h#h#:#y y y z z z ' + +'z z A A A B B B B B B B B C C C C C D D D D D E E E E E E E E F G }#r@r@r@r' + +'@r@r@r@.&e@e@e@e@e@e@e@e@}@}@S@J J J J J c.K K K K L L L M M M M M M M j.N ' + +'N N N N N N O O P Q Q Q Q R 2+3+3+4+P S S T T T T T U V V V V V &+&+*+*+H.X' + +' X X X Y Y Z Z Z Z ` ` ` ` .F%>+O.O.O.O.O.N...#.#.#.#.#.d.$.$.$.$.$.%.%.k.' + +'&.&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+''@>.,.''.''.''.''.).).).).!.!.~.~.~.' + +'~.~.{.].^.y*.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@W' + +'@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f# -9.9.9.+=+=+=4=4=' + +'4=4=D=D=K=K=K=K=K=T=;-2;|-q=q=q=y-y-G-G-G-G-G-T-T-T-T-K-K-'';'';'';'';'';''' + +';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;`;",'#13#10'"v v v v v v v v v v v u u u u u' + +' u u t t t t t t t s s s s s s s s s s s s s s r r r r r q q q q q q q q q ' + +'q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p p p p p p p p' + +' p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p ' + +'p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q q' + +' q q q r r r r r s s s s s s s s s s s s s s t t t t t t t u u u u u u u v ' + +'v v v v v v v v v v w w w w x x x x x x x x x x x x x a.y 5&h#h#h#h#a#y z z' + +' z z A A A B B B B B B B B C C C C C C D D D D E E E E E E E E b.F G G E r@' + +'r@r@r@r@r@.&e@e@e@e@e@e@e@e@}@}@}@%&J J J J c.K K K K L L L M M M M M M M j' + +'.N N N N N N N O O P Q Q Q Q R R ;@3+4+4+>@T T T T T T U V V V V V W V&*+*+' + +'*+U+X X X Y Y Z Z Z Z ` ` ` ` . .L&O.O.O.O.O.P.N.I+#.#.#.#.d.$.$.$.$.$.%.%' + +'.%.&.&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+:+>.,.''.''.''.''.''.).).).!.!.~.~' + +'.~.~.~.{.].].^.~.$=.@{@]@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W@' + +'W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#8=9.9.+=+=+=4' + +'=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-T=q- >T-G-G-G-G-T-T-T-T-K-K-K-'';'';'';'';' + +''';'';k;k;k;k;k;k;k;C;C;I;I;I;I;I;.>+>",'#13#10'"w w v v v v v v v v v v v ' + +'u u u u u u u u t t t t t t t s s s s s s s s s s s s s s s r r r r r q q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p ' + +'p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p' + +' p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q r r r r r s s s s s s s s s s s s s s s t t t t t t t u u u u u u u u v' + +' v v v v v v v v v v w w w w x x x x x x x x x x x x x x y y y H#h#h#h#h#`#' + +'z z z A A A B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G G G' + +' G 6#r@r@r@r@e@e@e@e@e@e@e@e@e@}@}@}@*@A-J J J c.K K K K L L L M M M M M M ' + +'M j.N N N N N N N O P P Q Q Q Q R R R @>4+4+4+++T T T T T U V V V V V W W H' + +'.*+*+*+*+;%X Y Y Z Z Z Z ` ` ` ` . ...+.-&O.O.O.P.N.N.Q.#.#.#.d.$.$.$.$.$.' + +'%.%.%.&.&.&.*.*.*.*.k.u$]+]+]+]+]+]+7+7+7+7+_+>.,.,.''.''.''.''.).).).!.!.!' + +'.~.~.~.~.~.{.].^.^.^.r+{@]@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@W@W@' + +'W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#8=9.+=+=+' + +'=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-9;#>>-T-T-T-T-T-K-K-'';'';'';' + +''';'';'';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;Y;$>",'#13#10'"x w w w w v v v v v v' + +' v v v v v u u u u u u u u t t t t t t t t s s s s s s s s s s s s s s s r ' + +'r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q h' + +'.h.p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p ' + +'p p p h.h.q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q' + +' r r r r r s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u v ' + +'v v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y a#h#h#d' + +'#d#s&z A A A A B B B B B B B B C C C C C D D D D D E E E E E E E E b.F G G ' + ,'G G G G }-r@r@.&e@e@e@e@e@e@e@e@}@}@}@*@*@*@Q+J J c.K K K K L L L M M M M M' + +' M M N N N N N N N N O P P Q Q Q Q R R R R P 4+4+5+&+T T T T U V V V V V W ' + +'W X i$*+*+*+*+-%Y Y Z Z Z Z ` ` ` ` . . .+.+.F+O.O.P.N.N.N.N.$%#.d.$.$.$.$' + +'.$.%.%.%.&.&.&.*.*.*.*.%.u$]+]+]+]+]+]+7+7+7+7+(+>.,.,.''.''.''.''.).).).).' + +'!.!.~.~.~.~.~.{.].^.^.^.^.^.L+]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W' + +'@W@W@W@W@W@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=8=9.' + +'+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;*;c;Q-K-K-K-'';''' + +';'';'';'';'';k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>%>&>",'#13#10'"x x x w w w w v v' + +' v v v v v v v v v u u u u u u u u u t t t t t t t t s s s s s s s s s s s ' + +'s s s s s r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r' + +' r r r s s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u u v ' + +'v v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y y y y c' + +'#d#d#d#r#A A A B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G ' + +'G G G G G G ~#r@.&e@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*=c.K K K K K L L L M M M M' + +' M M M N N N N N N N N O P P Q Q Q Q R R R R S S C$5+5+5+F.T T U V V V V V ' + +'W W X X X .=*+*+*+w*Y Z Z Z Z ` ` ` ` . . .+.+.+.+.6-P.N.N.N.N.N.Q.d.$.$.$' + +'.$.$.%.%.%.&.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+`+;.>.,.''.''.''.''.).).).' + +').!.!.~.~.~.~.~.{.].^.^.^.^.^.e.*%]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A' + +'@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=' + +'*>+=+=+=4=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;(;S;=>[;k' + +';'';'';'';'';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>->&>",'#13#10'"x x x x x w w w' + +' w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t s s s s s s s ' + +'s s s s s s s s s s r r r r r r r q q q q q q q q q q q q q q q q q q q q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r' + +' s s s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u u v v v ' + +'v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y y y y z z' + +' p;d#d#:#4#A B B B B B B B B C C C C C C D D D D D E E E E E E E E b.F G G ' + +'G G G G G G G m%e@e@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*@|@%@K K K L L L M M M M M' + +' M M j.N N N N N N N N O P P Q Q Q Q R R R R S S S ++5+5+5+,*T U V V V V V ' + +'W W X X X X I.*+*+*+;>Z Z Z Z ` ` ` ` . . .+.+.+.+.+.U*N.N.N.N.N.N.l+G$$.$' + +'.$.$.$.%.%.&.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+~@;.>.,.''.''.''.''.''.).)' + +'.).!.!.~.~.~.~.~.{.].].^.^.^.^.e./.F@]@]@]@6@6@7@7@7@7@m@m@m@m@z@z@A@A@A@A@' + +'A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0' + +'=0=>>+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;' + +'t;t;,>N;H-U;k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>''>&>)>",'#13#10'"x x x x x x x x' + +' w w w w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t t s s s ' + +'s s s s s s s s s s s s s s s r r r r r r r q q q q q q q q q q q q q q q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r s s s s s' + +' s s s s s s s s s s s s s t t t t t t t t t u u u u u u u u u v v v v v v ' + +'v v v v v v w w w w x x x x x x x x x x x x x x x a.y y y y y y y y z z z z' + +' b#:#:#:#s#B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G G G ' + +'G G G G G G i._@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*@*@|@=@X%K K L L L M M M M M M' + +' M j.N N N N N N N N O P P Q Q Q Q R R R S S S S T I*5+5+5+@+U V V V V V W ' + +'W X X X X X h$*+*+*+=+E+Z Z ` ` ` ` . . ...+.+.+.+.@.^*N.N.N.N.N.l+,+!+$.$' + +'.$.$.%.%.k.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+0+,@>.,.,.''.''.''.''.).).).' + +'!.!.!.~.~.~.~.~.{.].^.^.^.^.e././.].]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A' + +'@W@W@W@W@W@W@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=' + +'0={&+=+=4=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t' + +';v;v;v;v;A;5;#;!;k;k;k;C;C;I;I;I;I;I;.>.>!>&>)>",'#13#10'"x x x x x x x x x' + +' x w w w w w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t t t ' + +'s s s s s s s s s s s s s s s s s s s s r r r r r r r r q q q q q q q q q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q q q q q q q q q q q q q q q q q r r r r r r r r s s s s s s s s s s s s' + +' s s s s s s s s t t t t t t t t t t u u u u u u u u u v v v v v v v v v v ' + +'v v w w w w w x x x x x x x x x x x x x x x a.y y y y y y y y z z z z z A L' + +'%:#:#:#:#y B B B B B C C C C C C D D D D D E E E E E E E E b.F G G G G G G ' + +'G G G G i.H H W%e@e@e@e@e@}@}@}@*@*@*@*@*@|@|@=@=@P+K L L L M M M M M M M j' + +'.N N N N N N N O O P P Q Q Q Q R R R S S S S T T T 5+5+6+F=V V V V V W W X ' + +'X X X X X X .=*+=+=+,%Z ` ` ` ` . . ...+.+.+.+.+.#.#.R.N.N.N.N.,+,+,+);$.$' + +'.%.%.%.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+0+,@>.,.,.''.''.''.''.).).).!.!.' + ,'!.~.~.~.~.~.{.].^.^.^.^.^./././.].]@6@6@6@7@7@7@m@m@m@m@O*z@A@A@A@A@A@A@W@W' + +'@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#f#|=o#o#u#R&n#n#M#R&f#f#f#f#0=0=}=' + +'}=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v' + +';v;v;x;A;A;A;0;k;k;k;C;I;I;I;I;I;I;.>~>&>)>)>",'#13#10'"x x x x x x x x x x' + +' x x x w w w w v v v v v v v v v v v v v u u u u u u u u u u t t t t t t t ' + +'t t t s s s s s s s s s s s s s s s s s s s s s s r r r r r r r r r r r q q' + +' q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q ' + +'q q q q q q q q r r r r r r r r r r r s s s s s s s s s s s s s s s s s s s' + +' s s s t t t t t t t t t t u u u u u u u u u u v v v v v v v v v v v v v w ' + +'w w w x x x x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A a#:' + +'#:#:#:#a#B B B C C C C C C D D D D D E E E E E E E E E F F G G G G G G G G ' + +'G i.H H H H Q+e@e@e@e@}@}@}@*@*@*@*@*@|@=@=@=@=@&&L L L M M M M M M M j.N N' + +' N N N N N O O P P Q Q Q Q R R R S S S S T T T T V&6+6+%+-%V V V W W X X X ' + +'X X X X Y ~&=+=+=+-+` ` ` ` . . ...+.+.+.+.+.#.#.#.L&N.N.N.l+,+,+,+,+Z%%.%' + +'.%.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+K$c+>.,.,.''.''.''.''.).).).).!.!.~.' + +'~.~.~.~.{.].^.^.^.^.^.e./././.z*]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W' + +'@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#f#/=**f.m.5.5.5.5.5.J-f=R&R&f#f#f#0=0=0=}=}=' + +'2*4=4=4=4=D=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v' + +';v;A;A;A;A;A;V;k;C;C;I;I;I;I;I;.>.>{>&>)>)>",'#13#10'"x x x x x x x x x x x' + +' x x x x w w w w w v v v v v v v v v v v v v u u u u u u u u u u t t t t t ' + +'t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s r r r r r r' + +' r r r r r r r r r r r q q q q q q q q q q q q q q q q q q q r r r r r r r ' + +'r r r r r r r r r r s s s s s s s s s s s s s s s s s s s s s s s s s s t t' + +' t t t t t t t t t u u u u u u u u u u v v v v v v v v v v v v v w w w w w ' + +'x x x x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A B B y :#:' + +'#:#:#(#B C C C C C C D D D D D D E E E E E E E E b.F G G G G G G G G G G i.' + +'H H H H H @#e@e@}@}@}@*@*@*@*@*@*@|@=@=@=@=@R+R+J M M M M M M M M N N N N N' + +' N N N O O P Q Q Q Q Q R R R S S S S T T T T T ,*6+%+%+U+V V W W X X X X X ' + +'X X Y Y j$=+=+=+=+E+` ` . . ...+.+.+.+.+.#.#.#.#.#.R.N.l+,+,+,+,+,+T.%.%.&' + +'.&.&.*.*.*.%.T.{+]+]+]+]+]+]+]+7+7+7+<+>.>.,.''.''.''.''.).).).).!.!.~.~.~.' + +'~.~.{.].].^.^.^.^.e././././.G@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W' + +'@*#*#]#]#]#]#]#X@X@X@8#/=p=f.4.4.4.4.5.5.5.5.5.5.6.7.P=f#f#f#0=0=0=}=}=}=.-' + +'4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x' + +';A;A;A;A;A;J;k;C;I;I;I;I;I;I;.>Y;$>)>)>]>",'#13#10'"y x x x x x x x x x x x' + +' x x x x x x w w w w w v v v v v v v v v v v v v v u u u u u u u u u u t t ' + +'t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s' + +' s s s s s r r r r r r r r r r r r r r r r r r r r r r r r r r r r r s s s ' + +'s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t' + +' t t t t u u u u u u u u u u v v v v v v v v v v v v v v w w w w w x x x x ' + +'x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A B B B B B s#:#:' + +'#:#u%C C C C C C D D D D D E E E E E E E E b.F F G G G G G G G G G i.H H H ' + +'H H H H ##e@}@}@}@*@*@*@*@*@*@|@=@=@=@=@R+R+R+K&M M M M M M j.N N N N N N N' + +' N O P P Q Q Q Q R R R R S S S S T T T T T U >;%+%+%+q$V W W X X X X X X X ' + +'Y Y Z Z O.=+=+-+L.` . . ...+.+.+.+.+.#.#.#.#.#.#.;&l+,+,+,+,+,+''+''+U.&.&' + +'.&.*.*.*.:%T.{+{+]+]+]+]+]+]+7+7+7+u&;.>.,.''.''.''.''.''.).).).!.!.!.~.~.~' + +'.~.~.].].^.^.^.^.e././././.#@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@' + +'W@*#*#]#]#]#]#X@X@^>k#3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.o#f#f#0=0=}=}=}=}=X' + +'=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;' + +'A;A;A;A;A;A;V;C;C;I;I;I;I;I;.>.>/>&>)>)>]>",'#13#10'"y y y a.x x x x x x x ' + +'x x x x x x x x x x w w w w w v v v v v v v v v v v v v v u u u u u u u u u' + +' u u u t t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s ' + +'s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s' + +' s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t ' + +'u u u u u u u u u u u u v v v v v v v v v v v v v v w w w w w x x x x x x x' + +' x x x x x x x x x x a.y y y y y y y y y z z z z z A A A B B B B B B B b#:#' + +':#)#_#C C C C D D D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H' + +' H H H H %@}@}@}@*@*@*@*@*@|@=@=@=@=@=@R+R+R+S+<*M M M M M j.N N N N N N N ' + +'N O P P Q Q Q Q R R R R S S S S T T T T T U U V #+%+%+%+V+W X X X X X X X Y' + +' Y Z Z Z D+=+-+-+3%`& . ...+.+.+.+.+.@.#.#.#.#.#.#.d.e%,+,+,+,+''+''+''+!+V' + +'.&.&.*.*.:%(>{+{+]+]+]+]+]+]+7+7+7+:+;.>.,.,.''.''.''.''.).).).!.!.!.~.~.~.' + +'~.~.{.].^.^.^.^.^././././.o=6@6@6@7@7@7@m@m@m@m@O*z@A@A@n@B@U@A@W@W@W@W@W@W' + +'@*#*#]#]#]#]#X@X@1#1.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.p#f#0=0=0=}=}=}=f=' + +'8=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;x' + +';A;A;A;A;A;t;k;C;I;I;I;I;I;I;.>Y;$>)>)>]>]>",'#13#10'"y y y y y y x x x x x' + ,' x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v v u u u u ' + +'u u u u u u u u u t t t t t t t t t t t t t t s s s s s s s s s s s s s s s' + +' s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s ' + +'s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t t u u u u u' + +' u u u u u u u u v v v v v v v v v v v v v v v w w w w w x x x x x x x x x ' + +'x x x x x x x x x y y y y y y y y y y z z z z z A A A B B B B B B B B B (#)' + +'#)#)#.#C C D D D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H H ' + +'H H H H I I R+*@*@*@*@*@*@|@=@=@=@=@R+R+R+R+S+S+/&M M M M j.N N N N N N N O' + +' O P P Q Q Q Q R R R R S S S S T T T T T U U V V ,*%+%+&+>@X X X X X X X Y ' + +'Y Z Z Z Z =&3%-+-+;+F% ...+.+.+.+.+.@.#.#.#.#.#.#.d.$.W.,+,+,+''+''+''+!+!+' + +'!+S.u*Y.w$<%{+{+]+]+]+]+]+]+7+7+7+S$;.>.,.,.''.''.''.''.).).).!.!.!.~.~.~.~' + +'.~.{.].^.^.^.^.^.e./././.G@]@6@6@7@7@7@7@m@m@m@O*z@z@n@_=:.[.[.V*W@W@W@W@W@' + +'*#*#]#]#]#]#]#X@1#2.3.3.4.4.4.4.4.4.5.5.5.5.5.5.6.7.7.8.8.5.0=0=0=}=}=}=f=f' + +'=q=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;' + +'A;A;A;A;A;A;W;C;I;I;I;I;I;I;.>.>/>&>)>)>]>]>",'#13#10'"y y y y y y y y a.x ' + +'x x x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v v v' + +' u u u u u u u u u u u u u u t t t t t t t t t t t t t t t t t s s s s s s ' + +'s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s' + +' s s s s s s s s s s s t t t t t t t t t t t t t t t t t u u u u u u u u u ' + +'u u u u u v v v v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x' + +' x x x x x x a.y y y y y y y y y z z z z z z A A A B B B B B B B B B B C Z@' + +')#)#)#)#L@D D D D D E E E E E E E E E F F G G G G G G G G G G i.H H H H H H' + +' H H H I I I A-*@*@*@*@|@|@=@=@=@=@R+R+R+S+S+S+S+S+v@M M N N N N N N N N O ' + +'O P P Q Q Q Q R R R R S S S S T T T T T U U V V V h$%+&+&+.=X X X X X X Y Y' + +' Z Z Z Z Z ` q*-+;+;+P...+.+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.S.,+''+''+''+!+!+!' + +'+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+Z+;.>.,.,.''.''.''.''.).).).).!.!.~.~.~.~.' + +'~.{.].].^.^.^.^.e././.].L+]@6@6@6@7@7@7@m@m@m@m@z@z@|#a@[.[.[.[.}.7%W@W@W@W' + +'@*#*#]#]#]#]#X@N-|.3.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.8.8.f=0=0=}=}=}=f=' + +'f=f=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A' + +';A;A;A;A;A;A;_>C;I;I;I;I;I;I;.>Y;$>)>)>]>]>:>",'#13#10'"z y y y y y y y y y' + +' y x x x x x x x x x x x x x x x x x x x w w w w w w v v v v v v v v v v v ' + +'v v v v v v u u u u u u u u u u u u u u u t t t t t t t t t t t t t t t t t' + +' t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s ' + +'s t t t t t t t t t t t t t t t t t t t t t t u u u u u u u u u u u u u u u' + +' v v v v v v v v v v v v v v v v v w w w w w w x x x x x x x x x x x x x x ' + +'x x x x x y y y y y y y y y y z z z z z z A A A B B B B B B B B B B C C C t' + +'%)#)#.#.#C@D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H H H H ' + +'H H I I I J J u@*@*@*@|@=@=@=@=@=@R+R+R+S+S+S+S+S+S+K&j.N N N N N N N N O O' + +' P P Q Q Q Q R R R S S S S T T T T T T U U V V V V V V&&+*+*+i$X X X X Y Y ' + +'Z Z Z Z Z ` ` F+;+;+;+>+(%+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.M&,+''+''+)+!+!+!' + +'+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+(+;.>.,.,.''.''.''.''.).).).).!.!.~.~.~.~.~.' + +'{.].].^.^.^.^.e././.T@L+]@6@6@6@7@7@7@m@m@m@m@O*z@)-[.[.[.[.[.}.}.c=9=W@W@*' + +'#*#]#]#]#]#X@X@/#3.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.8.8.9.a=0=}=}=}=f=f=' + +'f=f=O=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;A' + +';A;A;A;A;A;,>C;I;I;I;I;I;I;.>.>{>&>)>)>]>:>:>",'#13#10'"z z z y y y y y y y' + +' y y y y x x x x x x x x x x x x x x x x x x x w w w w w w v v v v v v v v ' + +'v v v v v v v v v v v u u u u u u u u u u u u u u u u u t t t t t t t t t t' + +' t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t ' + +'t t t t t t t t t t t t t t t t u u u u u u u u u u u u u u u u u v v v v v' + +' v v v v v v v v v v v v v v w w w w w w x x x x x x x x x x x x x x x x x ' + +'x x y y y y y y y y y y y z z z z z A A A A B B B B B B B B B B C C C C C C' + +' D%.#.#.#u%D E E E E E E E E E b.F G G G G G G G G G G G H H H H H H H H H ' + +'H I I I J J J N+*@*@|@=@=@=@=@R+R+R+R+S+S+S+S+S+S+S+n%N N N N N N N N O P P' + +' Q Q Q Q Q R R R S S S S T T T T T T U U V V V V V W I**+*+*+I.X X X Y Y Z ' + +'Z Z Z Z ` ` ` .P.;+>+O.-&+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.$.a%''+)+!+!+!+~+' + +'~+~+~+{+{+]+]+]+]+]+]+7+7+7+~@;.>.>.,.''.''.''.''.).).).).!.!.!.~.~.~.~.~.{' + +'.].^.^.^.^.^././.z*p%]@6@6@6@7@7@7@m@m@m@m@O*&#p@[.[.[.[.[.}.}.}.}.&-W@*#*#' + +']#]#]#]#]#X@X@,&3.3.4.4.4.4.4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.E=}=}=}=}=f=f=f' + +'=f=2;D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;' + +'A;A;A;A;A;<>C;I;I;I;I;I;.>.>_>&>)>)>]>]>:>:>"};'#13#10 +]); diff --git a/indy/Core/IdAboutVCL.pas b/indy/Core/IdAboutVCL.pas new file mode 100644 index 0000000..36579bb --- /dev/null +++ b/indy/Core/IdAboutVCL.pas @@ -0,0 +1,420 @@ +unit IdAboutVCL; + +interface + +{$I IdCompilerDefines.inc} + +uses +{$IFDEF WIDGET_KYLIX} + QStdCtrls, QForms, QExtCtrls, QControls, QComCtrls, QGraphics, Qt, +{$ENDIF} +{$IFDEF WIDGET_VCL_LIKE} + StdCtrls, Buttons, ExtCtrls, Graphics, Controls, ComCtrls, Forms, +{$ENDIF} +{$IFDEF HAS_UNIT_Types} + Types, +{$ENDIF} +{$IFDEF WIDGET_LCL} + LResources, +{$ENDIF} + Classes, SysUtils; + +type + TfrmAbout = class(TForm) + protected + FimLogo : TImage; + FlblCopyRight : TLabel; + FlblName : TLabel; + FlblName2 : TLabel; + FlblVersion : TLabel; + FlblBuiltFor : TLabel; + FlblLicense : TLabel; + FlblPleaseVisitUs : TLabel; + FlblURL : TLabel; + //for LCL, we use a TBitBtn to be consistant with some GUI interfaces + //and the Lazarus IDE. + {$IFDEF USE_TBitBtn} + FbbtnOk : TBitBtn; + {$ELSE} + FbbtnOk : TButton; + {$ENDIF} + procedure lblURLClick(Sender: TObject); + function GetProductName: String; + procedure SetProductName(const AValue: String); + function GetProductName2: String; + procedure SetProductName2(const AValue: String); + function GetVersion: String; + procedure SetVersion(const AValue: String); + public + //we have a method for providing a product name and version in case + //we ever want to make another product. + class procedure ShowDlg; + class procedure ShowAboutBox(const AProductName, AProductName2, AProductVersion: String); + constructor Create(AOwner : TComponent); overload; override; + constructor Create; reintroduce; overload; + property ProductName : String read GetProductName write SetProductName; + property ProductName2 : String read GetProductName2 write SetProductName2; + property Version : String read GetVersion write SetVersion; + end; + +implementation + +{$IFNDEF WIDGET_LCL} + {$IFDEF WIN32_OR_WIN64} + {$R IdAboutVCL.RES} + {$ENDIF} + {$IFDEF KYLIX} + {$R IdAboutVCL.RES} + {$ENDIF} +{$ENDIF} + +uses + {$IFDEF WIN32_OR_WIN64}ShellApi, {$ENDIF} + {$IFNDEF WIDGET_LCL} + //done this way because we reference HInstance in Delphi for loading + //resources. Lazarus does something different. + {$IFDEF WIN32_OR_WIN64} + Windows, + {$ENDIF} + {$ENDIF} + IdDsnCoreResourceStrings, + IdGlobal; + +{$IFNDEF WIDGET_LCL} +function RGBToColor(R, G, B: Byte): TColor; +begin + Result := RGB(R, G, B); +end; +{$ENDIF} + +{ TfrmAbout } + +constructor TfrmAbout.Create(AOwner: TComponent); +begin + inherited CreateNew(AOwner,0); + + FimLogo := TImage.Create(Self); + FlblCopyRight := TLabel.Create(Self); + FlblName := TLabel.Create(Self); + FlblName2 := TLabel.Create(Self); + FlblVersion := TLabel.Create(Self); + FlblBuiltFor := TLabel.Create(Self); + FlblLicense := TLabel.Create(Self); + FlblPleaseVisitUs := TLabel.Create(Self); + FlblURL := TLabel.Create(Self); + {$IFDEF USE_TBitBtn} + FbbtnOk := TBitBtn.Create(Self); + {$ELSE} + FbbtnOk := TButton.Create(Self); + {$ENDIF} + + Name := 'formAbout'; + Left := 0; + Top := 0; + Anchors := [];//[akLeft, akTop, akRight,akBottom]; + BorderIcons := [biSystemMenu]; + BorderStyle := bsDialog; + + Caption := RSAAboutFormCaption; + ClientHeight := 336; + ClientWidth := 554; + Color := 2520226; // RGBToColor(38, 116, 162) + + Font.Color := 16776138; // RGBToColor(202, 251, 255) + Font.Height := -12; + Font.Size := 9; + Font.Name := 'Arial'; + Font.Style := []; + Position := poScreenCenter; + {$IFDEF WIDGET_VCL} + Scaled := True; + {$ENDIF} + Constraints.MinHeight := Height; + Constraints.MinWidth := Width; + // PixelsPerInch := 96; + + FimLogo.Name := 'imLogo'; + FimLogo.Parent := Self; + FimLogo.Left := 0; + FimLogo.Top := 0; + FimLogo.Width := 388; + FimLogo.Height := 240; + + {$IFDEF WIDGET_LCL} + FimLogo.Picture.Pixmap.LoadFromLazarusResource('IndyAboutBkgnd'); //this is XPM format, so Pixmap is used + FimLogo.Align := alClient; + FimLogo.Stretch := True; + {$ELSE} // Because Lazarus is also WIDGET_VCL_LIKE_OR_KYLIX + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + FimLogo.Picture.Bitmap.LoadFromResourceName(HInstance, 'INDY_ABOUT_BACKGROUND'); {Do not Localize} + FimLogo.Align := alClient; + FimLogo.Stretch := True; + {$ENDIF} + {$ENDIF} + + FlblName.Name := 'lblName'; + FlblName.Parent := Self; + FlblName.Left := 51; + FlblName.Top := 28; + FlblName.Width := 200; + FlblName.Height := 101; + FlblName.Anchors := [akLeft, akTop]; + {$IFDEF WIDGET_VCL} + FlblName.Font.Charset := DEFAULT_CHARSET; + FlblName.Transparent := True; + {$ENDIF} + FlblName.Font.Color := clWhite; + FlblName.Font.Height := -72; + FlblName.Font.Name := 'Arial Black'; + FlblName.Font.Style := []; + FlblName.ParentFont := False; + FlblName.WordWrap := False; + FlblName.Caption := RSAAboutBoxTitle1; + + FlblName2.Name := 'lblName2'; + FlblName2.Parent := Self; + FlblName2.Left := 54; + FlblName2.Top := 110; + FlblName2.Width := 192; + FlblName2.Height := 35; + FlblName2.Anchors := [akLeft, akTop]; + {$IFDEF WIDGET_VCL} + FlblName2.Font.Charset := DEFAULT_CHARSET; + FlblName2.Transparent := True; + {$ENDIF} + FlblName2.Font.Color := clWhite; + FlblName2.Font.Height := -31; + FlblName2.Font.Name := 'Arial'; + FlblName2.Font.Style := []; + FlblName2.ParentFont := False; + FlblName2.WordWrap := False; + FlblName2.Caption := RSAAboutBoxTitle2; + + FlblVersion.Name := 'lblVersion'; + FlblVersion.Parent := Self; + FlblVersion.Left := 300; + FlblVersion.Top := 170; + FlblVersion.Width := 200; + FlblVersion.Height := 17; + FlblVersion.Alignment := taRightJustify; + FlblVersion.AutoSize := False; + {$IFDEF WIDGET_VCL} + FlblVersion.Font.Charset := DEFAULT_CHARSET; + FlblVersion.Transparent := True; + {$ENDIF} + FlblVersion.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblVersion.Font.Height := -15; + FlblVersion.Font.Name := 'Arial'; + FlblVersion.Font.Style := [fsBold]; + FlblVersion.ParentFont := False; + FlblVersion.Anchors := [akTop, akRight]; + + FlblBuiltFor.Name := 'lblBuiltFor'; + FlblBuiltFor.Parent := Self; + FlblBuiltFor.Left := 300; + FlblBuiltFor.Top := 188; + FlblBuiltFor.Width := 200; + FlblBuiltFor.Height := 17; + FlblBuiltFor.Alignment := taRightJustify; + FlblBuiltFor.AutoSize := False; + {$IFDEF WIDGET_VCL} + FlblBuiltFor.Font.Charset := DEFAULT_CHARSET; + FlblBuiltFor.Transparent := True; + {$ENDIF} + FlblBuiltFor.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblBuiltFor.Font.Height := -14; + FlblBuiltFor.Font.Name := 'Arial'; + FlblBuiltFor.Font.Style := []; + FlblBuiltFor.ParentFont := False; + FlblBuiltFor.Anchors := [akTop, akRight]; + + // RLebeau: not using resouce strings for the product names because: + // 1. the names are pretty specific and not likely to change with localization; + // 2. we are trying to avoid using IFDEFs in resource units, per Embarcadero's request; + // 3. I don't want to create more product-specific resource units unless we really need them; + {$IFDEF WIDGET_KYLIX} + FlblBuiltFor.Caption := IndyFormat(RSAAboutBoxBuiltFor, ['Kylix']); + {$ELSE} + {$IFDEF WIDGET_VCL} + FlblBuiltFor.Caption := IndyFormat(RSAAboutBoxBuiltFor, ['VCL']); + {$ELSE} + {$IFDEF WIDGET_LCL} + FlblBuiltFor.Caption := IndyFormat(RSAAboutBoxBuiltFor, ['Lazarus']); + {$ELSE} + FlblBuiltFor.Caption := IndyFormat(RSAAboutBoxBuiltFor, ['Unknown']); + {$ENDIF} + {$ENDIF} + {$ENDIF} + + FlblLicense.Name := 'lblLicense'; + FlblLicense.Parent := Self; + FlblLicense.Left := 300; + FlblLicense.Top := 227; + FlblLicense.Width := 200; + FlblLicense.Height := 45; + FlblLicense.Alignment := taRightJustify; + FlblLicense.AutoSize := False; + {$IFDEF WIDGET_VCL} + FlblLicense.Font.Charset := DEFAULT_CHARSET; + FlblLicense.Transparent := True; + {$ENDIF} + FlblLicense.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblLicense.Font.Height := -12; + FlblLicense.Font.Name := 'Arial'; + FlblLicense.Font.Style := []; + FlblLicense.ParentFont := False; + FlblLicense.WordWrap := True; + FlblLicense.Anchors := [akTop, akRight]; + FlblLicense.Caption := RSAAboutBoxLicences; + + FlblCopyRight.Name := 'lblCopyRight'; + FlblCopyRight.Parent := Self; + FlblCopyRight.Left := 58; + FlblCopyRight.Top := 171; + FlblCopyRight.Width := 138; + FlblCopyRight.Height := 15; + FlblCopyRight.Caption := RSAAboutBoxCopyright; + {$IFDEF WIDGET_VCL} + FlblCopyRight.Font.Charset := DEFAULT_CHARSET; + FlblCopyRight.Transparent := True; + {$ENDIF} + FlblCopyRight.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblCopyRight.Font.Height := -12; + FlblCopyRight.Font.Name := 'Arial'; + FlblCopyRight.Font.Style := []; + FlblCopyRight.ParentFont := False; + FlblCopyRight.WordWrap := True; + + FlblPleaseVisitUs.Name := 'lblPleaseVisitUs'; + FlblPleaseVisitUs.Parent := Self; + FlblPleaseVisitUs.Left := 58; + FlblPleaseVisitUs.Top := 278; + FlblPleaseVisitUs.Width := 276; + FlblPleaseVisitUs.Height := 15; + {$IFDEF WIDGET_VCL} + FlblPleaseVisitUs.Font.Charset := DEFAULT_CHARSET; + FlblPleaseVisitUs.Transparent := True; + {$ENDIF} + FlblPleaseVisitUs.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblPleaseVisitUs.Font.Height := -12; + FlblPleaseVisitUs.Font.Name := 'Arial'; + FlblPleaseVisitUs.ParentFont := False; + FlblPleaseVisitUs.Caption := RSAAboutBoxPleaseVisit; + FlblPleaseVisitUs.Anchors := [akLeft, akTop]; + + FlblURL.Name := 'lblURL'; + FlblURL.Left := 58; + FlblURL.Top := 292; + FlblURL.Width := 141; + FlblURL.Height := 15; + FlblURL.Cursor := crHandPoint; + {$IFDEF WIDGET_VCL} + FlblURL.Font.Charset := DEFAULT_CHARSET; + FlblURL.Transparent := True; + {$ENDIF} + FlblURL.Font.Color := 16776138; // RGBToColor(202, 251, 255) + FlblURL.Font.Height := -12; + FlblURL.Font.Name := 'Arial'; + FlblURL.ParentFont := False; + FlblURL.OnClick := lblURLClick; + FlblURL.Caption := RSAAboutBoxIndyWebsite; + FlblURL.Anchors := [akLeft, akTop]; + FlblURL.Parent := Self; + + FbbtnOk.Name := 'bbtnOk'; + FbbtnOk.Left := 475; + {$IFDEF USE_TBitBtn} + FbbtnOk.Top := 297; + {$ELSE} + FbbtnOk.Top := 302; + FbbtnOk.Height := 25; + {$ENDIF} + FbbtnOk.Width := 75; + FbbtnOk.Anchors := [akRight, akBottom]; + {$IFDEF USE_TBitBtn} + FbbtnOk.Font.Color := clBlack; + FbbtnOk.ParentFont := False; + FbbtnOk.Kind := bkOk; + {$ELSE} + FbbtnOk.Caption := RSOk; + {$ENDIF} + FbbtnOk.Cancel := True; + FbbtnOk.Default := True; + FbbtnOk.ModalResult := 1; + FbbtnOk.TabOrder := 0; + FbbtnOk.Anchors := [akLeft, akTop, akRight]; + FbbtnOk.Parent := Self; +end; + +function TfrmAbout.GetVersion: String; +begin + Result := FlblVersion.Caption; +end; + +function TfrmAbout.GetProductName: String; +begin + Result := FlblName.Caption; +end; + +function TfrmAbout.GetProductName2: String; +begin + Result := FlblName2.Caption; +end; + +procedure TfrmAbout.lblURLClick(Sender: TObject); +begin + {$IFDEF WIN32_OR_WIN64} + ShellAPI.ShellExecute(Handle, nil, PChar(FlblURL.Caption), nil, nil, 0); {Do not Localize} + FlblURL.Font.Color := clPurple; + {$ENDIF} +end; + +procedure TfrmAbout.SetVersion(const AValue: String); +begin + FlblVersion.Caption := AValue; +end; + +procedure TfrmAbout.SetProductName(const AValue: String); +begin + FlblName.Caption := AValue; +end; + +procedure TfrmAbout.SetProductName2(const AValue: String); +begin + FlblName2.Caption := AValue; +end; + +class procedure TfrmAbout.ShowAboutBox(const AProductName, AProductName2, AProductVersion: String); +var + LFrm: TfrmAbout; +begin + LFrm := TfrmAbout.Create; + {$IFNDEF USE_OBJECT_ARC} + try + {$ENDIF} + LFrm.Version := IndyFormat(RSAAboutBoxVersion, [AProductVersion]); + LFrm.ProductName := AProductName; + LFrm.ProductName2 := AProductName2; + LFrm.ShowModal; + {$IFNDEF USE_OBJECT_ARC} + finally + LFrm.Free; + end; + {$ENDIF} +end; + +class procedure TfrmAbout.ShowDlg; +begin + ShowAboutBox(RSAAboutBoxTitle1, RSAAboutBoxTitle2, gsIdVersion); +end; + +constructor TfrmAbout.Create; +begin + Create(nil); +end; + +{$IFDEF WIDGET_LCL} +initialization + {$i IdAboutVCL.lrs} +{$ENDIF} +end. diff --git a/indy/Core/IdAboutVCL.rc b/indy/Core/IdAboutVCL.rc new file mode 100644 index 0000000..46e9022 --- /dev/null +++ b/indy/Core/IdAboutVCL.rc @@ -0,0 +1 @@ +INDY_ABOUT_BACKGROUND BITMAP AboutBackground.bmp \ No newline at end of file diff --git a/indy/Core/IdAboutVCL.xpm b/indy/Core/IdAboutVCL.xpm new file mode 100644 index 0000000..e9ae954 --- /dev/null +++ b/indy/Core/IdAboutVCL.xpm @@ -0,0 +1,1469 @@ +/* XPM */ +static char *Pixmap[] = { +"554 336 1130 2", +" c #256993", +". c #256A93", +"+ c #256A94", +"@ c #266B95", +"# c #266B96", +"$ c #266C96", +"% c #266C97", +"& c #266D97", +"* c #276D97", +"= c #276D98", +"- c #276E98", +"; c #276E99", +"> c #276F99", +", c #276F9A", +"' c #286F9A", +") c #286F9B", +"! c #28709B", +"~ c #256992", +"{ c #256892", +"] c #246892", +"^ c #246891", +"/ c #246791", +"( c #246790", +"_ c #246690", +": c #24668F", +"< c #23668F", +"[ c #23658E", +"} c #23658D", +"| c #23648D", +"1 c #23648C", +"2 c #22638C", +"3 c #22638B", +"4 c #22628B", +"5 c #22628A", +"6 c #226189", +"7 c #216189", +"8 c #216188", +"9 c #216088", +"0 c #216087", +"a c #215F87", +"b c #215F86", +"c c #205F86", +"d c #205E86", +"e c #205E85", +"f c #205D85", +"g c #205D84", +"h c #205D83", +"i c #205C83", +"j c #1F5C83", +"k c #1F5C82", +"l c #1F5B82", +"m c #1F5B81", +"n c #1F5A81", +"o c #1F5A80", +"p c #1E5A80", +"q c #1E597F", +"r c #1E597E", +"s c #1E587E", +"t c #1E587D", +"u c #1D577D", +"v c #1D577C", +"w c #1D567C", +"x c #1D567B", +"y c #1D557A", +"z c #1C557A", +"A c #1C5579", +"B c #1C5479", +"C c #1C5478", +"D c #1C5378", +"E c #1C5377", +"F c #1B5277", +"G c #1B5276", +"H c #1B5175", +"I c #1B5174", +"J c #1B5074", +"K c #1A5073", +"L c #1A4F73", +"M c #1A4F72", +"N c #1A4E71", +"O c #1A4E70", +"P c #1A4D70", +"Q c #194D70", +"R c #194D6F", +"S c #194C6F", +"T c #194C6E", +"U c #194B6E", +"V c #194B6D", +"W c #184B6D", +"X c #184A6C", +"Y c #184A6B", +"Z c #18496B", +"` c #18496A", +" . c #18486A", +".. c #184869", +"+. c #174869", +"@. c #174769", +"#. c #174768", +"$. c #174667", +"%. c #174666", +"&. c #164566", +"*. c #164565", +"=. c #164465", +"-. c #164464", +";. c #164363", +">. c #154363", +",. c #154362", +"'. c #154262", +"). c #154261", +"!. c #154161", +"~. c #154160", +"{. c #154060", +"]. c #15405F", +"^. c #14405F", +"/. c #143F5E", +"(. c #143F5D", +"_. c #143E5D", +":. c #143E5C", +"<. c #133D5C", +"[. c #133D5B", +"}. c #133C5A", +"|. c #133C59", +"1. c #133B59", +"2. c #123B58", +"3. c #123A58", +"4. c #123A57", +"5. c #123956", +"6. c #123955", +"7. c #123855", +"8. c #113855", +"9. c #113854", +"0. c #23668E", +"a. c #1D557B", +"b. c #1C5277", +"c. c #1B5073", +"d. c #174767", +"e. c #14405E", +"f. c #133B58", +"g. c #266A95", +"h. c #1E5980", +"i. c #1B5275", +"j. c #1A4E72", +"k. c #174566", +"l. c #133E5C", +"m. c #123957", +"n. c #256A95", +"o. c #28709C", +"p. c #28719C", +"q. c #28719D", +"r. c #184B6C", +"s. c #29719D", +"t. c #29729D", +"u. c #29729E", +"v. c #29739E", +"w. c #29739F", +"x. c #2973A0", +"y. c #2974A0", +"z. c #2A74A0", +"A. c #2A74A1", +"B. c #2A75A1", +"C. c #2A75A2", +"D. c #2A76A2", +"E. c #1B4D6E", +"F. c #1A4D6F", +"G. c #1C4E6F", +"H. c #1C4E70", +"I. c #1D4F70", +"J. c #1D4E70", +"K. c #1F5071", +"L. c #1F5070", +"M. c #1F4F70", +"N. c #205070", +"O. c #205171", +"P. c #205071", +"Q. c #1F4F6F", +"R. c #1E4E6E", +"S. c #1E4D6D", +"T. c #1E4D6C", +"U. c #1D4C6B", +"V. c #1C4B6B", +"W. c #1A4969", +"X. c #1A4968", +"Y. c #184767", +"Z. c #184667", +"`. c #1B4E70", +" + c #1D5071", +".+ c #1D5072", +"++ c #1E5172", +"@+ c #1F5173", +"#+ c #205273", +"$+ c #225374", +"%+ c #225474", +"&+ c #215474", +"*+ c #215373", +"=+ c #215273", +"-+ c #215272", +";+ c #215172", +">+ c #215171", +",+ c #204F6F", +"'+ c #204F6E", +")+ c #204E6E", +"!+ c #1F4E6E", +"~+ c #1F4E6D", +"{+ c #1F4D6D", +"]+ c #1F4D6C", +"^+ c #1D4C6A", +"/+ c #1D4B6A", +"(+ c #1C4968", +"_+ c #1B4867", +":+ c #1A4766", +"<+ c #184565", +"[+ c #174565", +"}+ c #1B4F71", +"|+ c #1D5173", +"1+ c #1E5274", +"2+ c #205475", +"3+ c #225676", +"4+ c #225576", +"5+ c #225575", +"6+ c #225475", +"7+ c #1F4C6B", +"8+ c #1E4C6B", +"9+ c #1E4C6A", +"0+ c #1E4B6A", +"a+ c #1C4A68", +"b+ c #194766", +"c+ c #184564", +"d+ c #174462", +"e+ c #1B4F73", +"f+ c #205476", +"g+ c #215577", +"h+ c #235778", +"i+ c #235777", +"j+ c #235677", +"k+ c #225677", +"l+ c #20506F", +"m+ c #1E4B69", +"n+ c #1D4968", +"o+ c #1B4767", +"p+ c #1A4664", +"q+ c #184563", +"r+ c #174362", +"s+ c #2A76A3", +"t+ c #2B76A3", +"u+ c #1D5275", +"v+ c #1F5476", +"w+ c #215678", +"x+ c #235779", +"y+ c #215473", +"z+ c #205271", +"A+ c #1F5171", +"B+ c #1D4E6F", +"C+ c #1D4D6F", +"D+ c #1C4D6E", +"E+ c #1B4C6D", +"F+ c #1B4C6C", +"G+ c #1C4D6D", +"H+ c #1C4D6C", +"I+ c #1C4C6C", +"J+ c #1E4A69", +"K+ c #1E4A68", +"L+ c #1C4765", +"M+ c #194664", +"N+ c #1C5175", +"O+ c #1F5477", +"P+ c #205678", +"Q+ c #22577A", +"R+ c #23587A", +"S+ c #235879", +"T+ c #205274", +"U+ c #1E5071", +"V+ c #1A4D6E", +"W+ c #194C6D", +"X+ c #1A4868", +"Y+ c #1A4867", +"Z+ c #1B4968", +"`+ c #1C4A69", +" @ c #1E4A67", +".@ c #1D4A67", +"+@ c #1D4967", +"@@ c #1B4764", +"#@ c #194462", +"$@ c #174261", +"%@ c #1C5275", +"&@ c #23577A", +"*@ c #24597B", +"=@ c #23597A", +"-@ c #215476", +";@ c #1E5273", +">@ c #1C4F71", +",@ c #174464", +"'@ c #194666", +")@ c #194765", +"!@ c #1B4967", +"~@ c #1D4A69", +"{@ c #1D4A66", +"]@ c #1D4966", +"^@ c #1A4562", +"/@ c #174260", +"(@ c #153F5E", +"_@ c #1C5276", +":@ c #1E5477", +"<@ c #215679", +"[@ c #23597B", +"}@ c #245A7B", +"|@ c #24597A", +"1@ c #1E5275", +"2@ c #1D5172", +"3@ c #174463", +"4@ c #184664", +"5@ c #1A4765", +"6@ c #1D4965", +"7@ c #1D4865", +"8@ c #1C4664", +"9@ c #1A4462", +"0@ c #17415F", +"a@ c #153F5D", +"b@ c #1E5578", +"c@ c #205779", +"d@ c #22587B", +"e@ c #245A7C", +"f@ c #215677", +"g@ c #1C5072", +"h@ c #174361", +"i@ c #184463", +"j@ c #1A4665", +"k@ c #1C4766", +"l@ c #1E4967", +"m@ c #1D4864", +"n@ c #1C4663", +"o@ c #184261", +"p@ c #16405D", +"q@ c #1F5679", +"r@ c #245B7D", +"s@ c #215779", +"t@ c #205477", +"u@ c #1E5376", +"v@ c #1C5173", +"w@ c #164261", +"x@ c #184462", +"y@ c #1C4865", +"z@ c #1C4764", +"A@ c #1C4763", +"B@ c #1A4461", +"C@ c #20577B", +"D@ c #23597D", +"E@ c #23587B", +"F@ c #164160", +"G@ c #1B4664", +"H@ c #1B4461", +"I@ c #18415E", +"J@ c #153E5B", +"K@ c #2B77A3", +"L@ c #1E557A", +"M@ c #21587B", +"N@ c #255C7E", +"O@ c #255B7E", +"P@ c #24597C", +"Q@ c #21577A", +"R@ c #1F5577", +"S@ c #1D5375", +"T@ c #16415F", +"U@ c #194361", +"V@ c #1B4563", +"W@ c #1C4662", +"X@ c #1B4561", +"Y@ c #18415F", +"Z@ c #1E567A", +"`@ c #22587C", +" # c #245B7E", +".# c #255C7F", +"+# c #22587A", +"@# c #205579", +"## c #1D5377", +"$# c #16415E", +"%# c #194360", +"&# c #1B4663", +"*# c #1C4661", +"=# c #1B4460", +"-# c #18405D", +";# c #143C5A", +"># c #2B77A4", +",# c #21587C", +"'# c #245C7E", +")# c #255D7F", +"!# c #235A7C", +"~# c #20577A", +"{# c #194461", +"]# c #1C4561", +"^# c #1A4360", +"/# c #163F5C", +"(# c #20587C", +"_# c #235B7E", +":# c #255D80", +"<# c #24597D", +"[# c #20567A", +"}# c #1D5478", +"|# c #1B4562", +"1# c #19425E", +"2# c #163E5B", +"3# c #1F587C", +"4# c #225B7E", +"5# c #255C80", +"6# c #235A7D", +"7# c #153E5C", +"8# c #1B4360", +"9# c #18405C", +"0# c #143C59", +"a# c #1E567B", +"b# c #225A7E", +"c# c #245D80", +"d# c #255D81", +"e# c #18415D", +"f# c #1A425E", +"g# c #235B7F", +"h# c #265D81", +"i# c #20587B", +"j# c #1D5479", +"k# c #153D5B", +"l# c #19415E", +"m# c #1C4461", +"n# c #1B435F", +"o# c #183F5B", +"p# c #143B58", +"q# c #1E577B", +"r# c #215A7E", +"s# c #245C7F", +"t# c #163E5A", +"u# c #19415D", +"v# c #163D59", +"w# c #265E81", +"x# c #265E82", +"y# c #225A7D", +"z# c #19425F", +"A# c #20597E", +"B# c #235C80", +"C# c #20597D", +"D# c #1D5679", +"E# c #205A7E", +"F# c #245D82", +"G# c #265E83", +"H# c #1F577C", +"I# c #215B7F", +"J# c #255E82", +"K# c #265F83", +"L# c #255E81", +"M# c #1B435E", +"N# c #215B80", +"O# c #255F82", +"P# c #133A57", +"Q# c #173E5A", +"R# c #2B78A4", +"S# c #2B78A5", +"T# c #1F597D", +"U# c #225B80", +"V# c #255F83", +"W# c #265F84", +"X# c #225C80", +"Y# c #266083", +"Z# c #235B80", +"`# c #1E577C", +" $ c #225B81", +".$ c #266084", +"+$ c #276084", +"@$ c #265F82", +"#$ c #1E577D", +"$$ c #276085", +"%$ c #225B7F", +"&$ c #1E587C", +"*$ c #215C81", +"=$ c #256083", +"-$ c #205B80", +";$ c #255F84", +">$ c #276186", +",$ c #276185", +"'$ c #235E83", +")$ c #235D81", +"!$ c #225D82", +"~$ c #266185", +"{$ c #235D82", +"]$ c #1F597F", +"^$ c #205C82", +"/$ c #256185", +"($ c #245E82", +"_$ c #205B81", +":$ c #246084", +"<$ c #276286", +"[$ c #256084", +"}$ c #205B7F", +"|$ c #266085", +"1$ c #215D83", +"2$ c #266186", +"3$ c #286286", +"4$ c #246085", +"5$ c #286387", +"6$ c #286287", +"7$ c #245F84", +"8$ c #276287", +"9$ c #215D82", +"0$ c #205B82", +"a$ c #256086", +"b$ c #225E83", +"c$ c #225E84", +"d$ c #276288", +"e$ c #286388", +"f$ c #256286", +"g$ c #235E84", +"h$ c #1A4C6E", +"i$ c #1A4C6D", +"j$ c #1A4B6D", +"k$ c #215E85", +"l$ c #276388", +"m$ c #286488", +"n$ c #256186", +"o$ c #1B4E6F", +"p$ c #1E4F72", +"q$ c #215374", +"r$ c #1E4E6F", +"s$ c #1B4B6C", +"t$ c #194969", +"u$ c #1E4C6C", +"v$ c #1B4A6A", +"w$ c #194867", +"x$ c #246087", +"y$ c #276387", +"z$ c #1B4F72", +"A$ c #1E5173", +"B$ c #1F5374", +"C$ c #205375", +"D$ c #225476", +"E$ c #215170", +"F$ c #1D4E6E", +"G$ c #1A496A", +"H$ c #184666", +"I$ c #266388", +"J$ c #286489", +"K$ c #1F4C6A", +"L$ c #245F86", +"M$ c #276489", +"N$ c #215D84", +"O$ c #266387", +"P$ c #296489", +"Q$ c #266287", +"R$ c #1F5274", +"S$ c #1A4767", +"T$ c #225F85", +"U$ c #28648A", +"V$ c #296589", +"W$ c #236086", +"X$ c #28658A", +"Y$ c #29658A", +"Z$ c #276389", +"`$ c #215E84", +" % c #1C4F72", +".% c #256288", +"+% c #256287", +"@% c #205576", +"#% c #1F5172", +"$% c #1A4A6B", +"%% c #194767", +"&% c #1C4866", +"*% c #184362", +"=% c #215576", +"-% c #1B4D6F", +";% c #194B6C", +">% c #1B4C6E", +",% c #1E4F70", +"'% c #1D4D6B", +")% c #164463", +"!% c #15405E", +"~% c #226087", +"{% c #276488", +"]% c #225D84", +"^% c #1B5072", +"/% c #1E4F71", +"(% c #1A4B6C", +"_% c #194968", +":% c #184766", +"<% c #1F4E6C", +"[% c #246288", +"}% c #29668B", +"|% c #1D5274", +"1% c #1E5174", +"2% c #194A6A", +"3% c #215271", +"4% c #1B4B6A", +"5% c #184665", +"6% c #1C4867", +"7% c #18425F", +"8% c #266489", +"9% c #246187", +"0% c #1F5478", +"a% c #1D4D6C", +"b% c #1C4B6A", +"c% c #27658A", +"d% c #225F86", +"e% c #1E4E6D", +"f% c #1D4A68", +"g% c #1D4765", +"h% c #194362", +"i% c #28658B", +"j% c #226088", +"k% c #28668C", +"l% c #1D5578", +"m% c #1E5579", +"n% c #215578", +"o% c #1C5074", +"p% c #1D4866", +"q% c #246289", +"r% c #29668C", +"s% c #246188", +"t% c #1D5579", +"u% c #22597C", +"v% c #1D5477", +"w% c #1E5276", +"x% c #1F5375", +"y% c #25648A", +"z% c #29678C", +"A% c #28668B", +"B% c #236087", +"C% c #226086", +"D% c #235A7E", +"E% c #23587C", +"F% c #1D4D6E", +"G% c #194564", +"H% c #15415F", +"I% c #2A678C", +"J% c #1C5376", +"K% c #1C5073", +"L% c #20597C", +"M% c #1F567A", +"N% c #25638A", +"O% c #1D567A", +"P% c #1F5579", +"Q% c #194A6B", +"R% c #194464", +"S% c #236188", +"T% c #29668D", +"U% c #2A678D", +"V% c #246189", +"W% c #23597C", +"X% c #1E5476", +"Y% c #225678", +"Z% c #184768", +"`% c #1B4866", +" & c #236288", +".& c #245B7C", +"+& c #1C5274", +"@& c #236289", +"#& c #29688D", +"$& c #22597D", +"%& c #1E5377", +"&& c #225879", +"*& c #205374", +"=& c #194A6C", +"-& c #1E4F6F", +";& c #1A4A6A", +">& c #1A4666", +",& c #143D59", +"'& c #19405D", +")& c #24628A", +"!& c #2A688D", +"~& c #1D4F6F", +"{& c #19405C", +"]& c #24638A", +"^& c #236187", +"/& c #225779", +"(& c #215475", +"_& c #205373", +":& c #1B4560", +"<& c #143B57", +"[& c #2A688E", +"}& c #21577B", +"|& c #1B5173", +"1& c #184464", +"2& c #29688E", +"3& c #245C81", +"4& c #245C80", +"5& c #21597D", +"6& c #245A7E", +"7& c #1D5174", +"8& c #1E5073", +"9& c #164262", +"0& c #1C4967", +"a& c #23628A", +"b& c #245E81", +"c& c #1C4969", +"d& c #23638A", +"e& c #226188", +"f& c #1B4F70", +"g& c #1D4B69", +"h& c #174363", +"i& c #29698E", +"j& c #1F597E", +"k& c #1F577B", +"l& c #1D5376", +"m& c #1C4460", +"n& c #2A698E", +"o& c #29678E", +"p& c #245B7F", +"q& c #29688F", +"r& c #265D82", +"s& c #20587D", +"t& c #1C5174", +"u& c #194665", +"v& c #28688E", +"w& c #2A698F", +"x& c #236189", +"y& c #1E5072", +"z& c #17405D", +"A& c #28678E", +"B& c #27678D", +"C& c #245D81", +"D& c #1C5375", +"E& c #173F5D", +"F& c #28668D", +"G& c #27658C", +"H& c #2B698F", +"I& c #25638B", +"J& c #235A7B", +"K& c #1E5375", +"L& c #19496A", +"M& c #194869", +"N& c #26658C", +"O& c #26658B", +"P& c #245D7F", +"Q& c #205172", +"R& c #1B425E", +"S& c #25648C", +"T& c #27668C", +"U& c #255D82", +"V& c #1F5273", +"W& c #17415D", +"X& c #24638B", +"Y& c #2A6990", +"Z& c #1F587D", +"`& c #19496B", +" * c #23638B", +".* c #29698F", +"+* c #1D4C6C", +"@* c #2B6A90", +"#* c #1C4C6B", +"$* c #1B4969", +"%* c #27678E", +"&* c #1D4B6B", +"** c #153D5A", +"=* c #1A415E", +"-* c #26658D", +";* c #24648B", +">* c #21597C", +",* c #1D4F71", +"'* c #1A4A6C", +")* c #1C4A6A", +"!* c #24648C", +"~* c #2A6A90", +"{* c #1E5679", +"]* c #225778", +"^* c #184969", +"/* c #28678D", +"(* c #194868", +"_* c #133B57", +":* c #1F577D", +"<* c #205577", +"[* c #163C58", +"}* c #1B415D", +"|* c #27668D", +"1* c #2A688F", +"2* c #133956", +"3* c #25658C", +"4* c #23638C", +"5* c #27668E", +"6* c #25658D", +"7* c #2B6A91", +"8* c #2A6991", +"9* c #26668D", +"0* c #296990", +"a* c #26678E", +"b* c #256085", +"c* c #1F577A", +"d* c #1E5577", +"e* c #24658C", +"f* c #2A6B91", +"g* c #215C82", +"h* c #245A7D", +"i* c #2B6B91", +"j* c #1F5678", +"k* c #296A90", +"l* c #1B4B6B", +"m* c #27688E", +"n* c #266184", +"o* c #205A80", +"p* c #1C4D6F", +"q* c #1F506F", +"r* c #24658D", +"s* c #205A7F", +"t* c #174564", +"u* c #1B4A69", +"v* c #215C83", +"w* c #1E5070", +"x* c #1A4B6B", +"y* c #1B4765", +"z* c #164260", +"A* c #25668D", +"B* c #23648B", +"C* c #2A6A91", +"D* c #225577", +"E* c #1B4D70", +"F* c #184361", +"G* c #27688F", +"H* c #245F85", +"I* c #1C4F70", +"J* c #215E83", +"K* c #245E83", +"L* c #16405E", +"M* c #296890", +"N* c #16405F", +"O* c #1C4864", +"P* c #17415E", +"Q* c #1D4764", +"R* c #1B4662", +"S* c #17425F", +"T* c #28688F", +"U* c #1C4C6D", +"V* c #163F5D", +"W* c #20597F", +"X* c #276184", +"Y* c #1D5276", +"Z* c #225679", +"`* c #153F5C", +" = c #215C80", +".= c #205272", +"+= c #113754", +"@= c #225D81", +"#= c #1E5478", +"$= c #1C4966", +"%= c #143D5A", +"&= c #245F83", +"*= c #24587B", +"== c #194563", +"-= c #17405C", +";= c #205679", +">= c #1E4B68", +",= c #26668E", +"'= c #164362", +")= c #1B4766", +"!= c #1A425F", +"~= c #205478", +"{= c #194562", +"]= c #1A435E", +"^= c #225C82", +"/= c #173F5C", +"(= c #1F5578", +"_= c #184260", +":= c #266288", +"<= c #1B4E71", +"[= c #163D5A", +"}= c #1A415D", +"|= c #183F5C", +"1= c #1A405C", +"2= c #21587A", +"3= c #1C4B6C", +"4= c #113753", +"5= c #1E4D6B", +"6= c #163E5C", +"7= c #19415C", +"8= c #173E59", +"9= c #1C4562", +"0= c #1A425D", +"a= c #183F5A", +"b= c #256187", +"c= c #143D5B", +"d= c #163F5B", +"e= c #153C58", +"f= c #1A415C", +"g= c #193F5B", +"h= c #123854", +"i= c #1E4968", +"j= c #133A56", +"k= c #19405B", +"l= c #184461", +"m= c #255A7E", +"n= c #24648D", +"o= c #1A4563", +"p= c #153D59", +"q= c #193F5A", +"r= c #215B81", +"s= c #255B7F", +"t= c #194565", +"u= c #1A435F", +"v= c #173F5B", +"w= c #1C4E6E", +"x= c #1D4D6D", +"y= c #163C59", +"z= c #205474", +"A= c #1A4A69", +"B= c #246086", +"C= c #1A4C6C", +"D= c #113653", +"E= c #163E59", +"F= c #215375", +"G= c #235B7D", +"H= c #153B58", +"I= c #194768", +"J= c #143B56", +"K= c #113652", +"L= c #133A55", +"M= c #173D5A", +"N= c #1A405B", +"O= c #133955", +"P= c #183E5A", +"Q= c #123754", +"R= c #20567B", +"S= c #1B445F", +"T= c #123753", +"U= c #153E5A", +"V= c #183E59", +"W= c #225F84", +"X= c #163C57", +"Y= c #226289", +"Z= c #1A415B", +"`= c #1C4C6E", +" - c #173E5B", +".- c #143A56", +"+- c #25648B", +"@- c #163B57", +"#- c #1F557A", +"$- c #183D58", +"%- c #29678D", +"&- c #17405E", +"*- c #163D58", +"=- c #1F5A7F", +"-- c #153B57", +";- c #133854", +">- c #163B56", +",- c #26668C", +"'- c #1F567B", +")- c #184360", +"!- c #235E85", +"~- c #1F4D6B", +"{- c #153C57", +"]- c #103652", +"^- c #266187", +"/- c #27668B", +"(- c #103551", +"_- c #235E82", +":- c #205C81", +"<- c #174360", +"[- c #27658B", +"}- c #22597B", +"|- c #173D58", +"1- c #25628A", +"2- c #143955", +"3- c #246389", +"4- c #1E4F6E", +"5- c #21577C", +"6- c #205170", +"7- c #28658C", +"8- c #235F84", +"9- c #26648A", +"0- c #215A80", +"a- c #1B4665", +"b- c #133954", +"c- c #103651", +"d- c #28678C", +"e- c #174160", +"f- c #1D5476", +"g- c #256289", +"h- c #173C58", +"i- c #19405A", +"j- c #1A3F5A", +"k- c #153A55", +"l- c #103550", +"m- c #256389", +"n- c #143A57", +"o- c #27648A", +"p- c #1B425D", +"q- c #143A55", +"r- c #215F85", +"s- c #18405E", +"t- c #153B56", +"u- c #113651", +"v- c #143A54", +"w- c #103450", +"x- c #163D57", +"y- c #193F59", +"z- c #183E58", +"A- c #205578", +"B- c #1C4A67", +"C- c #133853", +"D- c #123652", +"E- c #266389", +"F- c #1A4564", +"G- c #193E59", +"H- c #163A55", +"I- c #29658B", +"J- c #153C59", +"K- c #193D58", +"L- c #235F86", +"M- c #225E85", +"N- c #1A4460", +"O- c #215A7F", +"P- c #1D5175", +"Q- c #173C56", +"R- c #215778", +"S- c #143954", +"T- c #193E58", +"U- c #225C81", +"V- c #143B59", +"W- c #123752", +"X- c #286589", +"Y- c #255E83", +"Z- c #225A80", +"`- c #143854", +" ; c #21597E", +".; c #173C57", +"+; c #184868", +"@; c #153A54", +"#; c #143852", +"$; c #236085", +"%; c #16425F", +"&; c #10344F", +"*; c #123651", +"=; c #1F4F6E", +"-; c #19435F", +";; c #235C81", +">; c #1A4C6F", +",; c #163D5B", +"'; c #193D57", +"); c #1B4A6B", +"!; c #183C56", +"~; c #22597E", +"{; c #173A55", +"]; c #266286", +"^; c #21587D", +"/; c #1B4D6D", +"(; c #10334F", +"_; c #163955", +":; c #1F5377", +"<; c #153D58", +"[; c #163954", +"}; c #20577C", +"|; c #245E84", +"1; c #183D59", +"2; c #153A56", +"3; c #173B55", +"4; c #1F5376", +"5; c #123550", +"6; c #163953", +"7; c #163A56", +"8; c #133753", +"9; c #113551", +"0; c #12354F", +"a; c #1D4B68", +"b; c #143C58", +"c; c #153954", +"d; c #133751", +"e; c #113550", +"f; c #173D59", +"g; c #183B56", +"h; c #193E5A", +"i; c #193C57", +"j; c #1B4B6D", +"k; c #193C56", +"l; c #235D83", +"m; c #235678", +"n; c #204F6D", +"o; c #143953", +"p; c #235C7F", +"q; c #174665", +"r; c #123551", +"s; c #183E5B", +"t; c #10334E", +"u; c #133752", +"v; c #0F334E", +"w; c #123751", +"x; c #0F324E", +"y; c #153853", +"z; c #1F5272", +"A; c #0F324D", +"B; c #153953", +"C; c #193C55", +"D; c #215575", +"E; c #1D5073", +"F; c #183C55", +"G; c #11344F", +"H; c #143752", +"I; c #193B55", +"J; c #173A54", +"K; c #10344E", +"L; c #123853", +"M; c #163A54", +"N; c #133852", +"O; c #143751", +"P; c #1C4B69", +"Q; c #164462", +"R; c #183D57", +"S; c #113450", +"T; c #11344E", +"U; c #183C57", +"V; c #183B55", +"W; c #133651", +"X; c #22577B", +"Y; c #183B54", +"Z; c #225A7F", +"`; c #163952", +" > c #163C56", +".> c #193B54", +"+> c #13354F", +"@> c #1C5071", +"#> c #143853", +"$> c #10324C", +"%> c #153751", +"&> c #0F314B", +"*> c #173F5A", +"=> c #133652", +"-> c #11334D", +";> c #215372", +">> c #18405B", +",> c #11354F", +"'> c #173952", +")> c #0F304B", +"!> c #13354E", +"~> c #183A53", +"{> c #13364F", +"]> c #0E304B", +"^> c #1A445F", +"/> c #143650", +"(> c #1E4E6C", +"_> c #173A53", +":> c #0E304A", +"<> c #163A53", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , ' ' ' ' ' ' ' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) ' ' ' ' ' ' ' , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e f g g g g g h i j j j j k k l l l l m m m m n o o p p p p q q q q q r r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P P Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ' ' ' ' ' ' , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & & % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L M M M M M N N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.", +" . + + + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n n o o p p p h.q q q q q r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.9.", +" . + + + + + + + g.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ g.+ + + + + + + . ~ ~ { { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n o o o p p p q q q q q r r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.9.", +" . + + + + + + + n.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s s t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P P Q Q Q R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.", +" . + + + + + + + n.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e e g g g g g h h j j j j k k l l l l m m m n n o o p p p p q q q q q r r s s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I I J J J c.K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.", +". + + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n o o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.", +". + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e f g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s s t t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.", +"+ + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; > , , , , , , , ' ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' ' , , , , , , , > ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r r s s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.", +"+ + + + + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L M M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.", +"+ + + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a b b b c d d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.", +"+ + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; > , , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , , > ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.", +"+ + + + g.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S S T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.", +"+ + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.", +"+ + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.", +"+ + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m n o o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.", +"+ n.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ n.+ + + + + + + ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G H H H H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.", +"+ @ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.", +"g.@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o o p p p q q q q q r r s s s s t t u u u v v v v v w x x x x x x x y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.", +"@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.s.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.", +"@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & * * = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = * * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G H H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.7.7.8.", +"@ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | | 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.", +"@ @ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x x y y y z z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L M M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.", +"@ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : < < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.", +"@ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.", +"@ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.", +"@ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x a.y y y z z z A B B B B C C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.", +"@ @ # # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w x x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L M M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.", +"@ # # $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.", +"# # # $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ # # # @ @ @ @ @ @ + + + + + + + ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.", +"# # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.", +"# $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x x y y y z z z A B B B B C C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.", +"# $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , , ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' , , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.", +"$ $ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.x.x.x.y.y.y.y.y.y.y.x.x.x.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.", +"$ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.", +"$ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ) ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ) ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v v w x x x x x x y y y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.", +"$ $ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x x y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.", +"$ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.", +"$ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.", +"$ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N P P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.", +"$ $ % % % & & & * = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.", +"$ $ % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x a.y y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.", +"$ % % % % & & * = = = = = - ; ; ; ; ; ; ; > , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , > ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w x x x x x x x y y y z z z A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.", +"$ % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.B.B.B.B.B.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J c.K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.", +"% % % % & & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.", +"% % % & & & = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.5.", +"% % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% % % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 8 8 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% % & & * = = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C C D D D E E E E E F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% & & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J c.K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.", +"% & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"% & & * = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x a.y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V E.F.G.G.G.H.I.I.J.J.J.K.L.L.M.M.N.O.O.O.P.N.N.N.Q.R.S.S.S.S.T.U.V.V.W.X.X.Y.Z.Z.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m n o o p p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N P P Q Q R R R S `.`. +.+++@+#+$+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.P.N.N.N.N.N.,+,+,+,+'+'+)+!+!+~+~+~+{+{+]+]+^+/+(+_+:+<+[+,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & = = = = = - - ; ; ; ; ; ; > , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , > ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y z z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N }+.+|+1+2+2+3+4+4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.O.N.N.N.N.N.,+,+,+,+'+'+'+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+8+9+0+a+_+b+c+d+).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& * = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C C D D D E E E E E F G G G G G H H H H H I I J J J J K K K L L M M M M e+|+1+f+g+h+i+j+k+k+k+3+3+4+4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+-+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+8+9+9+0+0+0+m+m+m+n+o+p+q+r+~.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.", +"& * = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.s+s+s+s+t+t+t+t+t+t+t+s+s+s+s+D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L J u+v+w+x+x+h+h+h+h+h+i+j+k+k+k+3+3+4+4+4+5+5+5+6+6+%+%+%+&+y+*+*+*+*+z+A+K.K.K.L.B+B+C+C+D+E+F+F+F+G+G+G+G+H+I+I+I+S.T.T.!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+L+M+r+~.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& * = = = = - - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J N+u+O+P+Q+R+S+S+S+S+x+h+h+h+h+h+i+j+k+k+k+3+3+4+4+4+5+5+&+T+@+U+I.H.V+W+X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.Y.Y.Y.X+Y+Z+`+/+8+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@+@@@#@$@]./.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H %@O+w+&@*@=@=@=@R+R+S+S+S+S+S+h+h+h+h+h+i+j+k+-@f+;@;@.+>@`.F.T T U U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.,@<+'@)@!@(+~@0+m+m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@L+^@/@(@_._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H _@:@<@[@}@}@*@*@*@|@=@=@R+R+S+S+S+S+S+h+h+g+f+1@2@>@Q Q Q R R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.3@4@5@o+n+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@6@7@8@9@0@a@:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G b@c@d@e@e@e@e@}@}@*@*@*@|@=@=@R+R+S+x+f@v+u+g@N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.h@i@j@k@l@.@.@.@{@]@]@]@]@6@6@7@7@7@m@m@n@o@p@[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F E q@d@r@r@r@e@e@e@e@e@}@}@*@*@*@|@=@s@t@u@v@M M M M N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].w@x@p+y@]@]@]@]@6@6@7@7@7@m@m@m@z@A@n@B@0@:.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E b@C@D@r@r@r@r@r@r@e@e@e@e@e@}@}@E@<@O+u+K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e./.F@x@G@7@6@7@7@7@m@m@m@z@A@A@A@A@A@H@I@J@}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h i j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z A A B B B B C C C D D L@M@r@N@N@O@r@r@r@r@r@r@e@e@e@P@Q@R@S@J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.T@U@V@7@m@m@m@z@z@A@A@A@A@W@W@W@X@Y@J@|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - - ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z A B B B B C C Z@`@ #.#N@N@N@N@O@r@r@r@r@r@r@+#@###H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.$#%#&#z@z@A@A@A@A@W@W@W@W@W@*#=#-#;#1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.D.t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@>#>#>#K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+D.D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x x y y y z z z A B B B L@,#'#)#.#.#.#N@N@N@N@O@r@r@!#~#b@E H H H H H I I J J J c.K K K L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:._.0@{#n@A@A@W@W@W@W@W@*#*#]#]#^#/#|.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y z z z A y (#_#:#)#)#)#.#.#.#N@N@N@N@<#[#}#G G G G H H H H H I I J J J c.K K K L L M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.p@%#|#W@W@W@W@*#*#]#]#]#]#X@1#2#1.4.4.4.4.m.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z 3#4#5#:#:#:#)#)#)#.#.#.#N@6#~#}#b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.7#I@H@W@*#*#]#]#]#]#X@X@H@8#9#0#4.4.m.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y a#b#c#d#:#:#:#:#:#)#)#)#.#D@C@}#E E E b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N P P Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.J@e#X@]#]#]#]#X@X@H@H@=#=#f#2#f.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & % % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x (#g#h#h#d#d#:#:#:#:#:#)#_#i#j#D D E E E E E F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.k#l#m#]#X@X@H@H@=#=#=#=#n#o#p#5.5.", +"= = = = - - ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b c c d d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x q#r#d#h#h#h#h#d#d#:#:#:#s#(#y C C C D D E E E E E F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.t#I@=#H@H@=#=#=#=#=#n#n#u#v#", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x 3#g#w#x#x#h#h#h#h#d#d#:#y#a#B B B C C C D D E E E E E F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.2#z#=#=#=#=#=#n#n#n#n#", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w A#c#x#x#x#x#x#h#h#h#h#B#C#D#B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.0#9#n#=#=#n#n#n#n#", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v v v E#F#G#x#x#x#x#x#x#h#h#g#H#z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.f.2#f#n#n#n#n#", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v I#J#K#G#G#x#x#x#x#x#L#b#a#y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.p#o#M#n#", +"= = = = - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u t N#O#K#K#K#G#G#x#x#x#c#C#x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.P#Q#", +"= = = = - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#R#S#R#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t T#U#V#W#K#K#K#K#G#G#x#B#3#x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s X#Y#W#W#W#K#K#K#K#G#Z#`#x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p q q q q q q r s s $.$+$+$W#W#W#K#K#@$X##$w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p q q q q q q r N#W#$$$$+$+$W#W#W#O#%$&$v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p q q q q q *$=$$$$$$$$$+$+$W#V#N#v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#R#S#S#S#S#S#S#S#S#S#S#S#R#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p q q q -$;$>$,$$$$$$$$$+$Y#X#u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p q o '$>$>$>$,$$$$$$$+$)$T#t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#R#S#S#S#S#S#S#S#S#S#S#S#R#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p p p !$~$>$>$>$>$,$$$+${$]$t t t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o o p ^$/$>$>$>$>$>$>$,$($]$s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n o _$:$<$<$>$>$>$>$>$[$}$s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g g h j j j j k k l l l m m m m n '$<$<$<$<$>$>$>$|$*$q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#S#S#S#S#S#S#S#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h h j j j j k k l l l m m m 1$2$3$3$<$<$<$>$,$'$q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W X X X X X Y Y Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#R#S#R#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e g g g g g h h j j j j k k l l l m m 4$5$6$3$3$<$<$<$7$o q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.d.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l l l 1$8$5$5$6$3$3$<$2$9$q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.t+t+t+t+t+t+t+t+K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@t+t+t+t+t+t+t+t+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h h j j j j k k l 0$a$5$5$5$5$6$3$3$b$p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j k k c$d$e$5$5$5$5$6$|$_$p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j j ^$f$e$e$e$5$5$5$8$g$o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G i.H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T U V V V V W X X h$h$h$i$j$j$j$Z ` ` . ...+.+.+.+.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g h i j j j k$l$m$e$e$e$5$5$n$0$n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G H H H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S o$`.>@p$U+#+#+q$&+*+*+*+*+*+*+=+=+=+=+-+L.r$s$t$+.+.+.@.#.#.#.#.#.$.$.W.,+T.T.u$v$v$w$Y.%.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b c c d d e e e e e g g g g g h i j j x$m$m$m$e$e$e$y$c$m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C C D D E E E E E F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q }+z$A$B$C$D$4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+E$F$` +.@.#.#.#.#.#.$.$.$.G$'+'+)+!+!+~+~+~+{+U./+Z+Y+H$,@;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & % % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h i g I$J$m$m$m$e$e$n$^$m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C C D D D E E E E E F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O -@k+k+k+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.G+..#.#.#.#.#.$.$.$.$.U.'+)+!+!+~+~+~+{+{+]+]+]+]+K$,@;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g h L$M$J$J$m$m$m$l$c$l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J c.K K K L L M M M M j.N N N N N P -@k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.M.t$#.#.#.#.$.$.$.$.%.T.!+!+!+~+~+~+{+{+]+]+]+]+7+~@;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.", +"= = = = - - ; ; ; ; ; ; > , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , > ; ; ; ; ; ; - - = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g N$O$P$J$J$J$m$m$Q$i l l m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B C C C D D D E E E E b.F G G G G G H H H H H I I J J J c.K K K c.u+u@M M M j.N N N N O P R$k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.P.l+t$#.#.#.$.$.$.$.%.Y.S.!+!+~+~+~+{+{+]+]+]+]+7+7+S$;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@>#>#>#>#>#>#>#>#>#>#>#K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g T$U$V$P$J$J$J$m$4$k l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J c.S@R@s@R+R+S+c.M M N N N N N O P A$k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.l+..#.#.$.$.$.$.%.%.Y.~+!+~+~+~+{+{+]+]+]+]+7+7+K$,@>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.D.D.D.D.D.D.t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@>#>#>#K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+D.D.D.D.D.D.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c c d e e e e e e g g W$X$Y$V$P$J$J$Z$`$k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x x y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I I N+O+<@|@=@=@=@R+S+S+u+M M N N N N N O P %k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.R.#.d.$.$.$.$.%.%.&.t$!+~+~+~+{+{+]+]+]+]+7+7+7+~@>.,.'.'.'.).).).!.!.~.~.~.~.i@L+p+x@~.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - - ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e f g .%Y$Y$Y$V$P$J$+%i k k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z A B B B B C C C D D D E E E E b.G G G G G G H H H H H I w+*@*@*@|@=@=@R+R+S+S+@%M M N N N N N O P P k+k+3+3+3+4+4+5+5+5+5+6+%+%+%+%+&+*+*+#%*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.$%d.$.$.$.$.%.%.&.&.V.~+~+~+{+]+]+]+]+]+7+7+7+7+%%,.'.'.'.).).).!.!.~.~.r+&%K+ @.@.@.@@@*%]././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"= = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+K@K@K@K@K@K@K@K@K@t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b c c d e e e e e k$Z$Y$Y$Y$Y$V$J$T$j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I :@*@*@*@|@=@=@R+R+S+S+w+M M N N N N N O P Q =%k+3+3+3+4+4+5+5+5+5+6+%+U+-%i$W X X X X X ;%>%,%=+-+-+-+;+;+>+O.O.O.P.N.N.N.Q.d.$.$.$.$.%.%.&.&.$.'%~+~+{+]+]+]+]+]+7+7+7+7+9+)%'.'.'.).).).!.!.w@j@K+K+K+ @.@.@.@{@]@]@L+*%!%(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e ~%U$Y$Y$Y$Y$Y${%]%j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G G H H H H H I %@*@*@*@|@=@=@R+R+S+S+S+^%M N N N N N O P Q f+k+3+3+3+4+4+5+5+5+5+6+%+/%V V W X X X X X Y Z Z (%L.-+-+;+;+>+O.O.O.P.N.N.N.N._%$.$.$.$.%.%.&.&.&.:%<%~+{+]+]+]+]+]+7+7+7+7+8+a+'.'.'.).).).!.c+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@z@o@(@_.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e [%}%Y$Y$Y$Y$Y$I$1$j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G G H H H H H I I Q+*@*@|@=@=@R+R+S+S+S+|%M N N N N N O P Q 1%k+3+3+4+4+4+5+5+5+5+6+%+/%V W W X X X X X Y Z Z Z 2%3%-+;+;+O.O.O.O.P.N.N.N.N.4%$.$.$.$.%.%.&.&.&.*._%~+{+]+]+]+]+]+7+7+7+7+8+9+5%'.'.).).3@6%J+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@6@7@7@V@7%a@:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d e e 8%}%}%Y$Y$Y$Y$9%h h j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E F G G G G G i.H H H H H I I 0%*@*@|@=@=@R+R+S+S+S+v+M N N N N N O P Q g@k+3+3+4+4+4+5+5+5+5+6+%+#+V W W X X X X Y Y Z Z Z ` E+-+;+;+O.O.O.O.P.N.N.N.N.a%$.$.$.$.%.%.&.&.&.*.*.b%{+]+]+]+]+]+7+7+7+7+8+9+0+;.'.).5@m+J+J+K+K+K+K+K+ @.@.@.@{@]@]@]@]@6@6@7@7@7@m@B@:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"* = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c d d b c%}%}%}%Y$Y$U$d%g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I I u+*@*@|@=@=@R+R+S+S+S+w+M N N N N N O P Q }+k+3+3+4+4+4+5+5+5+5+6+%+#+V W W X X X X Y Y Z Z Z ` ` -+;+;+O.O.O.O.N.N.N.N.N.e%$.$.$.$.%.%.&.&.&.*.*.%.u$]+]+]+]+]+7+7+7+7+8+9+0+_+,@f%m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@6@7@g%h%a@:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c d ~%i%}%}%}%}%Y$Z$k$g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D E E E E E F G G G G G i.H H H H H I J J R+*@=@=@=@R+R+S+S+S+S+M N N N N N O P Q Q k+3+3+4+4+4+5+5+5+6+6+%+q$V W W X X X X Y Y Z Z Z ` ` L.;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.Z.]+]+]+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @ @.@.@.@{@]@]@]@]@6@7@U@(@_.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& * = = = = - - ; ; ; ; ; ; ; , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.s.s.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.s+s+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+t+s+s+D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a b b c c j%k%}%}%}%}%}%.%g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B C C l%m%D E E E E E F G G G G G i.H H H H H I J J n%*@=@=@=@R+R+S+S+S+S+o%N N N N N O P Q Q f+3+3+4+4+4+5+5+5+6+6+%+%+V W r.X X X X Y Y Z Z Z ` ` F$;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.Z+]+]+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@{@]@]@]@p%{#!%_._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& * = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.s+s+s+s+t+t+t+t+t+t+t+s+s+s+s+D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 a a a b b c q%r%}%}%}%}%}%s%g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B B B t%u%)#.# #~#v%E E E F G G G G G i.H H H H I I J J u@*@=@=@=@R+R+S+S+S+S+w%N N N N N O P Q Q x%3+3+4+4+4+5+5+5+6+6+%+%+V W r.X X X X Y Y Z Z Z ` ` F$;+;+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.-.^+]+]+]+7+7+7+7+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@{@]@p%#@].(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.", +"& * = = = = = - - ; ; ; ; ; ; , , , , , ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' , , , , , ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 a a a b b y%z%r%}%}%}%A%B%f g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B B B a#y#)#)#.#.#.#N@ #d@b@E F G G G G G H H H H H I I J J J *@=@=@=@R+R+S+S+S+S+g+N N N N N O P Q Q 1%3+3+4+4+4+5+5+5+6+%+%+%+-%W X X X X X Y Z Z Z Z ` ` F$;+>+O.O.O.O.N.N.N.N.N.l+$.$.$.$.%.%.&.&.*.*.*.*.=.-.5%]+]+]+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+ @.@.@.@.@]@#@]././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.5.5.5.5.5.", +"& & = = = = = - - ; ; ; ; ; ; > , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , > ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b c%z%z%r%}%}%i%C%e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y z z z A B a#D%:#)#)#)#.#.#.#N@N@N@N@E%L@G G G G G H H H H H I I J J J w+=@=@=@R+R+S+S+S+S+x+N N N N N O P Q Q z$3+3+4+4+4+5+5+5+6+%+%+%+-%W X X X X X Y Z Z Z Z ` ` F%;+>+O.O.O.O.N.N.N.N.N.e%$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.Z+]+]+7+7+7+8+9+0+0+0+0+m+m+m+J+K+K+K+K+K+ @.@.@.@G%H%/./././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 9 0 0 a a j%i%I%z%z%r%}%c%b e e g g g g g g h i j j j k k l l l m m m m n o o p p p p q q q q q r s s s s t t t u u u v v v v w x x x x x x a.y y y z z z q#D%:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@!#m%J%G G H H H H H I I J J J u@=@=@=@R+R+S+S+S+S+x+K%N N N N P P Q Q O 3+3+4+4+5+5+5+5+6+%+%+%+H.W X X X X X Y Z Z Z Z ` ` r$;+>+O.O.O.P.N.N.N.N.N.S.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.8+7+7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+K+ @.@5@~.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 0 0 0 a j%k%I%I%z%r%r%8%e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u v v v v v w x x x x x x a.y y y z z L%:#:#:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@r@r@6#M%J%H H H H H I I J J J v@=@=@=@R+S+S+S+S+S+h+1+N N N O P P Q Q R 4+3+4+4+5+5+5+5+6+%+%+%+/%W X X X X X Y Z Z Z Z ` ` M.;+>+O.O.O.P.N.N.N.N.N.V.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.H$7+7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+K+p%w@^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.", +"& & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.x.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.D.D.D.D.D.D.D.D.D.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.x.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 j%r%I%I%z%z%r%N%e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x x x x x y y y y z z O%:#:#:#:#:#)#)#)#.#.#.#N@N@N@N@O@r@r@r@r@r@6#P%%@H H H I I J J J c.s@=@R+R+S+S+S+S+S+h+f+N N N O P P Q Q R 2+3+4+4+5+5+5+5+6+%+%+%+/%W X X X X X Y Z Z Z ` ` Q%;+;+>+O.O.O.P.N.N.N.N.N.G$$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-._+7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+K+R%].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.1.4.4.4.4.4.5.5.5.5.5.", +"% & & * = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 S%T%U%I%I%z%z%V%d e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t u u u v v v v w w x x x x x x y y y y z z A y#:#:#:#:#)#)#)#.#.#.#N@N@N@N@ #r@r@r@r@r@r@e@W%P%%@H I I J J J c.X%=@R+R+S+S+S+S+S+h+Y%N N N O P Q Q Q R ;@3+4+4+5+5+5+5+6+%+%+%+#+W X X X X X Y Z Z Z ` ` F$;+;+>+O.O.O.P.N.N.N.N.l+Z%$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.<+7+7+7+8+9+0+0+0+m+m+m+J+J+K+K+`%w@{.].^.^.^.^././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.;#H@u#0#4.4.4.5.5.5.5.5.", +"% & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ g.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 &T%U%U%I%I%z% &d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A O%:#:#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@W%P%+&I J J J c.+&=@R+R+S+S+S+S+S+h+h+g@N N O P Q Q Q R 2@4+4+4+5+5+5+5+6+%+%+%+#+W X X X X X Y Z Z Z ` E+-+;+;+>+O.O.O.P.N.N.N.N.e%$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.b+7+7+7+8+9+0+0+0+m+m+m+J+J++@r+~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.I@H@=#=#1#0#4.5.5.5.5.5.", +"% & & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # @ @ @ @ @ @ n.+ + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 @&#&U%U%U%I%z% &c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x x x x x y y y y z z A A $&:#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@e@e@[@%&J J J c.K &&R+R+S+S+S+S+S+h+h+1+N N O P Q Q Q R }+4+4+4+5+5+5+5+6+%+%+%+*&W X X X X Y Y Z Z =&-&-+-+;+;+O.O.O.O.P.N.N.N.N.;&$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-._+7+7+7+8+9+0+0+0+m+m+m+J+>&w@~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.,&H@H@=#=#=#=#'&p#5.5.5.5.", +"% % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 9 )&#&!&U%U%U%z%S%c c d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z A B y :#:#:#)#)#)#.#.#N@N@N@N@N@ #r@r@r@r@r@.&e@e@e@e@e@}@*@R+u@J K K R@R+R+S+S+S+S+S+h+h+f+N N O P Q Q Q R R 4+4+4+5+5+5+6+6+%+%+%+&+W X X V h$i$~&B+K.=+-+-+-+;+;+O.O.O.O.N.N.N.N.R.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.~@7+7+7+8+9+0+0+0+m+m+m+n+!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.I@H@H@=#=#=#=#8#n#{&p#5.5.", +"% % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.C.C.C.C.C.C.C.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 9 9 9 ]&!&!&!&U%U%T%^&b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u u u v v v v w w q#g#%$x x x y y y y z z A B B $&:#)#)#)#.#.#.#N@N@N@N@d@6#r@r@r@r@r@e@e@e@e@e@}@}@*@*@*@/&+&K S@R+R+S+S+S+S+x+h+h+k+N N O P Q Q Q R R (&4+4+5+5+5+6+6+%+%+%+&+_&*+*+*+*+*+*+=+=+=+-+-+-+;+;+O.O.O.O.N.N.N.N.t$d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.K$7+7+7+8+9+0+0+0+m+m+m+>&!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.|.:&H@H@=#=#=#=#8#n#n#n#{&<&", +"% % & & * = = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 6 6 6 7 7 7 8 9 9 ]&[&!&!&!&U%T%B%b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w r#L#x#x#3#x a.y y y z z z A B B y :#)#)#)#.#.#.#N@N@N@N@}&F m%u%r@r@r@e@e@e@e@e@}@}@*@*@*@|@=@R@|&Q+R+S+S+S+S+x+h+h+h+z$N O P Q Q Q R R C$4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+-+-+-+;+;+O.O.O.O.N.N.N.$%#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.[+7+7+7+7+9+9+0+0+0+m+m+m+1&!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.e#X@H@H@=#=#=#=#8#n#n#n#n#n#", +"% % % & & * = = = = = - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 8 8 9 )&2&[&!&!&U%r%j%b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u v v v v T#3&x#x#x#x#4&x y y y y z z A A B B B 5&)#)#)#.#.#.#N@N@N@N@6&J%G G }#d@r@e@e@e@e@e@}@}@*@*@*@|@=@=@+#/&R+S+S+S+S+x+h+h+h+7&N O P Q Q Q R R 8&4+4+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+-+;+>+O.O.O.O.N.N.$%#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.H$7+7+7+7+9+9+0+0+0+m+m+m+9&!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.|.:&X@H@H@=#=#=#=#8#n#n#n#n#n#", +"% % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 8 )&2&[&[&!&!&k%j%a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t u u u v v v I#G#G#x#x#x#x#x#A#y y y y z z A A B B B t%'#)#)#.#.#.#N@N@N@N@O@q@G G G G }#+#e@e@e@e@}@}@*@*@*@|@=@=@=@R+S+S+S+S+S+h+h+h+h+f+O P P Q Q R R R >@4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.O.l+$%#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.=.=.-.-.-.-.Z+7+7+7+8+9+9+0+0+0+m+m+0&!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.e#X@X@H@H@=#=#=#=#8#n#n#n#n#n#", +"% % % & & & = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 8 a&2&[&[&[&!&z%j%a a b b c c d e e e e e f g g g g g h i j j j k k k l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v T#b&K#G#G#x#x#x#x#x#L#Z@y y y z z A A B B B B i#)#)#.#.#.#N@N@N@N@ #!#G G G G G H ##+#e@e@}@}@*@*@*@|@=@=@R+R+S+S+S+S+S+h+h+h+h+k+O P P Q Q R R R `.4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.r$..#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.c&7+7+7+8+9+0+0+0+0+m+m+j@!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.;#:&X@H@H@H@=#=#=#=#n#n#n#n#n#n#", +"% % % % & & * = = = = = - - ; ; ; ; ; ; > , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.B.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , > ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 7 7 d&2&[&[&[&!&z%e&a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r s s s s s t t u u u I#K#K#K#G#G#x#x#x#x#x#x#b#y y y z z A A B B B B l%'#)#.#.#N@N@N@N@N@ #r@m%G G G i.H H H ##+#}@}@*@*@*@|@=@=@R+R+S+S+S+S+S+h+h+h+h+h+f&P Q Q Q R R R S 4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.-&Q%@.#.#.#.#.#.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.g&7+7+7+8+9+0+0+0+0+m+m+h&!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.-#X@X@H@H@=#=#=#=#=#n#n#n#n#n#n#", +"$ % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.B.B.B.B.B.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 6 7 7 d&i&[&[&[&[&z%e&0 a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s t t t u j&($K#K#K#K#G#G#x#x#x#x#x#x#h#k&y y z z A B B B B B C i#)#.#.#N@N@N@N@N@ #r@d@G G G i.H H H H H l&Q+*@*@*@|@=@=@R+R+S+S+S+S+S+h+h+h+h+h+2@P Q Q Q R R S S C$5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+=+=+=+-+-+-+M.r$s$` +.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.7+7+7+7+8+9+0+0+0+0+m+m+!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.;#m&X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#", +"$ % % % % & & * = = = = = - ; ; ; ; ; ; ; > , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , > ; ; ; ; ; ; ; - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 7 7 d&i&n&[&[&[&o&e&0 a a a b b c c d d e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t ]$V#W#K#K#K#K#G#x#x#x#x#x#x#x#h#p&y z z z A B B B B C C l% #.#.#N@N@N@N@N@ #r@r@v%G G i.H H H H H I J %&R+*@=@=@=@R+R+S+S+S+S+S+h+h+h+h+h+B$P Q Q Q R R S S R$5+5+5+5+6+%+%+%+&+&+*+*+*+#%A+A+B+B+B+F+(%` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.H$7+7+7+7+8+9+0+0+0+m+m+0&!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.|.|.1.1.-#]#X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#", +"$ $ % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 7 5 q&n&[&[&[&o&6 0 0 a a a b b c c d e e e e e e g g g g g h i j j j k k k l l l m m m m n o o p p p q q q q q q r s s s s t t t t ($K#K#K#K#K#G#x#x#x#x#x#x#r&h#h#s&z z z A B B B B C C C C@.#.#N@N@N@N@O@ #r@r@Q@G G H H H H H I I J J t&0%+#=@=@R+R+S+S+S+S+x+h+h+h+h+h+=%P Q Q Q R R S S 8&5+5+5+6+T+#+/%/%F.V+;%X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.w$7+7+7+7+8+9+0+0+0+m+m+u&!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.;#m#]#X@X@H@H@=#=#=#=#=#n#n#n#n#n#M#", +"$ $ % % % & & & * = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.q.q.q.q.q.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.q.q.q.q.q.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 6 5 v&w&n&[&[&o&x&0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q r r s s s s t t u u j&K#K#K#K#G#G#x#x#x#x#x#x#h#h#h#:#O%z A A B B B B C C C }# #.#N@N@N@N@O@r@r@r@r@E G H H H H H I I J J J c.I P+=@R+R+S+S+S+S+x+h+h+h+h+h+i+`.Q Q Q R R S S P y&`.F.U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.w$0+7+7+7+8+9+0+0+0+m+m+3@!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.z&]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#", +"$ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 x&A&w&w&n&[&o&x&9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u I#K#K#K#G#G#x#x#x#x#x#x#h#h#h#h#r#z A A B B B B C C C D C@.#N@N@N@N@O@r@r@r@r@M%G H H H H H I I J J J c.K K %@/&R+S+S+S+S+x+h+h+h+h+h+j+ %Q Q Q R R S S S T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.,@<+_+a+9+0+0+0+m+m+).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.m#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#", +"$ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { ] ] ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 6 6 B&w&w&n&n&2&a&9 9 0 0 0 a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v C&K#K#G#G#x#x#x#x#x#x#h#h#h#h#d#q#A A B B B B C C C D }# #N@N@N@N@ #r@r@r@r@6#D&H H H H H I I J J J c.K K L c.u@S+S+S+S+h+h+h+h+h+=%R$>@Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.,@5%_+f%!@).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.E&]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#", +"$ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 6 F&w&w&w&n&[&]&9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v &$O#K#G#G#x#x#x#x#x#x#h#h#h#h#d#g#A B B B B B C C C D D M%N@N@N@N@ #r@r@r@r@r@b@H H H H H I I J J J c.K K L L M v@w+S+S+h+k+f+|+}+O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.H@]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#", +"$ $ $ $ % % % & & & = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 5 G&H&w&w&w&n&I&9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u v v v E#K#G#x#x#x#x#x#x#x#h#h#h#d#d#d#3#B B B B C C C D D D E r@N@N@N@ #r@r@r@r@r@J&H H H H H I J J J J K K K L L M M M K&K&g@N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X i$j$j$j$(%(%` . ...+.+.+.@.#.;&I+L&L&M&M&$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.k#m#]#]#X@X@X@H@H@=#=#=#=#8#n#n#n#n#n#M#", +"$ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ) ) ! ! ! ! ! ! ! o.o.o.p.p.p.p.p.p.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.y.y.y.y.y.y.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.z.y.y.y.y.y.y.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.p.p.p.p.p.p.o.o.o.! ! ! ! ! ! ! ) ) ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 5 5 5 5 N&H&H&w&w&w&O&9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v B#G#x#x#x#x#x#x#h#h#h#h#d#d#d#P&t%B B B C C C D D D E M%N@N@O@ #r@r@r@r@r@e@##H H H I I J J J J K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X E.*+=+Q&K.3%-+L. . ...+.+.+.@.#.R.l+N.l+,+,+,+M&%.%.&.&.&.*.%.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.-#m#X@X@H@H@=#=#=#=#=#n#n#n#n#n#n#R&", +"$ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.y.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 5 S&H&H&w&w&w&T&8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v `#U&x#x#x#x#x#x#h#h#h#h#d#d#:#:#5&B B B C C C D D D E E r@N@O@r@r@r@r@r@r@e@+#H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T o$V&T+T+I.V V W W X X X X i$*+j$Z Z ` F$-+$% .+.+.+.+.@.#.R.R.#.d.$.$.M&Z%%.%.&.&.&.*.w$~+H$-.-.[+H$-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.0#W&:&H@H@=#=#=#=#=#n#n#n#n#n#M#R&", +"$ $ $ $ $ % % % % & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.x.x.x.x.x.x.y.y.y.y.y.y.y.x.x.x.x.x.x.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ / / / ( ( ( ( ( _ : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 X&Y&H&H&w&w&B&8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w Z&x#x#x#x#x#x#h#h#h#h#d#d#:#:#:#Z@B B C C C D D D E E M%N@ #r@r@r@r@r@r@e@e@_@H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S y&5+*&p$U+%+#%V W X X X X X i$*+j$Z Z ` G+-+`& .+.+.+.+.#.#.R.R.#.d.$.$.$.$.%.%.&.&.&.*.:%~+Z.-.-.`+]+Z+;.;.;.,@>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.|.-#H@=#=#=#=#=#n#n#n#n#n#M#R&", +"# $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; , , , , , , ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' , , , , , , ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 4 4 5 5 5 *.*H&H&H&w&A&6 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p p p q q q q q q r s s s s t t t u u u v v v v w w r#x#x#x#x#x#h#h#h#h#d#d#:#:#:#D%B B C C C D D E E E E r@ #r@r@r@r@r@.&e@e@<@H H I I J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S `.5+ +T U V H./%V W X X X X X ;%=+,%K.K.O.-+F$ . .+.+.+.+.#.#.R.N.R.e%S.+*G$$.%.%.&.&.*.*.*.~+%%-.[+]+8+/+;.;.,@7+Y+,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.;#u#=#=#=#=#n#n#n#n#n#M#R&", +"# $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 5 v&@*H&H&H&2&5 7 8 9 9 9 9 0 0 0 a a a b b c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u v v v v v w x x 4&x#x#x#x#h#h#h#d#d#d#:#:#:#:#H#C C C C D D E E E E M% #r@r@r@r@r@.&e@e@W%%@H I I J J J c.K K L L M M M M M N N N N N O P Q Q Q R R R S S y&5+T T U V V V V W X X X X X Y =+K.B+,%-+F$ . ...+.+.+.+.#.#.R.R.$%#*V.V.V.$.%.%.&.&.*.*.*.<%$*-.`+8+Z+/+;.;.~@K$)%,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.0#1#=#8#n#n#n#n#n#M#R&", +"# # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 5 5 %*@*@*H&H&.*d&7 8 8 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t u u u v v v v w w x x `#L#x#x#h#h#h#h#d#d#d#:#:#:#:#s#l%C C D D D E E E E E 6#r@r@r@r@r@.&e@e@e@:@H I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R S S S y&5+T T U V V V V W X X X X X Y =+B+Z ` G+-+F% ...+.+.+.@.#.#.R.I+#.$.$.$.$.%.%.k.&.&.*.*.*.&*$*[+]+w$Y+]+;.<+7+:%,.,.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.**=*n#n#n#n#n#M#R&", +"# # # $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ' ) ) ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ) ) ' ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ # # # @ @ @ @ @ @ + + + + + + + ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 3 3 3 3 3 4 5 5 -*@*@*H&H&H&;*7 7 8 9 9 9 9 9 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x A#x#x#h#h#h#h#d#d#:#:#:#:#:#:#>*C C D D D E E E E b.m%r@r@r@r@r@e@e@<@_@H I I J J J J K K K L L M M |%^%j.N N N N N O P Q Q Q R R S S S +5+F.T U V V #%,*W X X X X X Y Q&B+Z ` ` -&;+'*..+.+.+.@.#.#.Q.I+#.$.$.$.$.%.%.&.&.&.*.*.*.)*)*`+/+-.H$7+;.~@g&>.,.'.'.'.'.).).).!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.v#f#n#n#n#M#R&", +"@ # # $ $ $ $ $ % % % % & & * = = = = = = - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 4 5 !*~*@*@*H&H&G&7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e f g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x B#x#h#h#h#h#d#d#:#:#:#:#:#:#)#{*C D D D E E E E b.G d@r@r@r@6#P%%@H H H I I J J J c.K K K L t&v+]*S+|%j.N N N N N O P Q Q Q R R S S S V+5+V&U U V -%%+o$W X X X X Y Y K.,%Z ` ` 2%P.>+^*+.+.+.@.#.#.N.N.l+,+,+!+S.%.%.&.&.&.*.*.*.$*u$]+H$-.H$7+<+7+<+>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.Q#M#n#M#R&", +"@ @ # # $ $ $ $ $ % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 *w&@*@*@*H&/*7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w x x x x x q#d#h#h#h#h#d#d#:#:#:#:#:#:#)#6#C D D E E E E E F G }#r@6#M%J%H H H H H I I J J J c.K |&X%/&S+/&K&c.M j.N N N N N O P Q Q Q R R S S S T y&5+@+G./%$+#+W r.X X X X Y Y (%(%Z ` ` ` . .+.+.+.+.#.#.#.#.#.t$M&M&W.G$%.%.&.&.&.*.*.*.(*]+/+-.-.[+7+~@~@;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.4.4.4.4.4.5.5.5.5._*{&R&R&", +"@ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 3 3 4 5 q&@*@*@*H&2&x&7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g h h j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x :*h#h#h#d#d#d#:#:#:#:#:#)#)#)#k&D D E E E E E F G G m%J%G i.H H H H H I I J J J c.K +#Q+<*f@]*M M M N N N N N O P P Q Q R R R S S T T T >@T+#+#+H.V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.k./+H$-.-.-.7+7+,@;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.[*}*", +"@ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; > , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , > ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 5 |*@*@*@*@*1*a&6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n o o o p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y 5&h#h#d#d#:#:#:#:#:#:#)#)#)# #}#D E E E E b.F G G G G G i.H H H H H I J J J J K K +&L L u+S+|%M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.c&_+;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.2*", +"@ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.v.v.v.v.v.v.v.v.v.v.v.v.v.v.v.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 3*@*@*@*@*~*X&6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p h.q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y 4&h#d#d#:#:#:#:#:#:#)#)#)#.#,#D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M /&@%M M N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.", +"@ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + + ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : < < < [ [ [ [ [ [ } | | | | 1 1 1 2 2 2 3 3 3 3 4 4*Y&@*@*@*@*N&6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k l l l m m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y k&:#d#d#:#:#:#:#:#:#)#)#)#.#.#m%E E E E F G G G G G G H H H H _@w++&J J J c.K K K L L M @%/&M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.", +"@ @ @ @ @ # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 4 q&@*@*@*@*v&6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o o p p p q q q q q r r s s s s t t t u u v v v v v w x x x x x x x y y y C#d#d#:#:#:#:#:#:#)#)#)#.#.#W%E E E E F G G G G G i.H H H H ##}@R@J J J c.K K K L M M v@S+u+j.N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.", +"@ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ ~ { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } | | | | | 1 1 1 2 2 3 3 3 3 3 5*@*@*@*@*w& *6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n o o p p p p q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z %$d#:#:#:#:#:#)#)#)#.#.#.#~#E E E E F G G G G G i.H H H H H &&=@J J J c.K K L L M M M ]*<*N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.", +"@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & * * = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = * * & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 6*7*@*@*@*~*!*6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m n n o o p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z O%P&:#:#:#:#:#)#)#)#.#D@j#E E E E b.F G G G G G H H H H H H X%*@%&J J K K K L L M M M K&w+N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.7.7.8.", +"@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.s.s.s.s.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ n.+ + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 1 8*@*@*@*@*9*5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m m n n o !$p p p q q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z q#:#:#:#:#:#)#)#'#C@D D E E E E b.G G G G G G H H H H H I I R+R+J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.", +"g.@ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ) ) ) ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.s.s.s.t.t.t.t.t.t.t.t.t.t.t.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.t.t.t.t.t.t.t.t.t.t.t.s.s.s.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ) ) ) ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 3 0*7*@*@*@*v&5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a b b b c d d e e e e e f g g g g g h i j j j k k k l l l m m m m n o [$3$g$p p q q q q q r r s s s s t t u u u v v v v v w x x x x x x x y y y z z z A 5&:#:#:#:#)#y#l%D D E E E E E F G G v%m%v%G H H H H H I I %&*@%&c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.", +"+ @ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 a*7*@*@*@*w& *5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m m n b*3$<$<$b$h.q q q q q r s s s s s t t u u u v v v v w w x x x x x x y y y y z z z A t%g#:#:#'#c*C D D D E E E E E F }#d@r@r@r@d*H H H H H I I J R+/&c.K K L L M M M M M N N N N N N O P Q Q Q R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.", +"+ n.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ n.+ + + + + + + ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 3 3 3 e*f*7*@*@*~*!*5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h h j j j j k k l l l m m m m n b*3$3$<$<$<$!$q q q q q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B Z@:#D%t%C C D D D E E E E ##}&r@d@}#J%!#<@H H H H H I J J %&*@u+K K L L M M M M M N N N N N O P P Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.", +"+ + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * * = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = * * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 3 0*7*7*@*@*|*5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m m m b*3$3$3$<$<$<$>$g*q q q r r s s s s t t t u u v v v v v w x x x x x x x y y y y z z A A B B Z@B C C C D D D E E E E h*r@b@G G G u%+#H H H H I I J J J O+|&K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.", +"+ + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.s.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | 1 1 1 1 2 2 2 3 3 %*i*7*@*@*.*5 5 5 6 6 6 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l m m m b*6$3$3$<$<$<$<$>$,$*$q q r s s s s s t t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C D D E E E E E M%r@q@G G Q@r@P%H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.", +"+ + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 e*f*7*7*@*~*;*5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g h i j j j j k k l l l m m m b*6$6$3$3$<$<$<$>$>$>$,$*$q r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F u%6#b@u%6#j*H H H H H I I J J J c.K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.", +"+ + + + g.@ @ @ @ @ @ # # # $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ # # # @ @ @ @ @ @ g.+ + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 3 k*i*7*@*@*9*5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l l l m m 4$5$6$6$3$3$<$<$<$>$>$>$>$,$_$r s s s s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F v%r@r@M@E i.H H H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q R R R S S S T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` .`&Q%Q%Q%Q%`&l*l*l*l*4%v$v$v$G$(*(*Z%&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.", +"+ + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; > , , , , , , , ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' , , , , , , , > ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 m*i*i*7*@*q&5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l l m 7$5$5$6$3$3$<$<$<$<$>$>$>$>$>$n*o*s s s s t t u u u v v v v v w x x x x x x x y y y y z z A B B B B B C C C D D D E E E E b.G G M%r@}#G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X i$i$D+p*J.q*L.3%;+;+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+U.U.)*X+%%H$-.-.;.;.;.;.>.,.'.'.'.'.).).).!.~.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.", +"+ + + + + + @ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.q.q.q.q.q.q.q.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ + + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 r*i*i*7*@*~* *5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a b b b c d d e e e e e e g g g g g h i j j j j k k l l l m g$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$.$s*s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G J%!#!#G H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q Q R R S S S T T T T U V V V V F.G.I.#%#+*+*+=+=+=+=+-+-+-+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+]+]+]+]+8+~@(+_+:+t*,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.", +"+ + + + + + n.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 3 k*i*i*7*@*-*5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k k k l l l b$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$.$s*s t t t u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G }#r@j*H H H H H I I J J J c.K K K L M M M M M j.N N N N N O P Q Q Q R R S S S T T T T -%I.#%*+&+&+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+7+8+9+(+:+c+).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.", +"+ + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; > , , , , , , , ' ' ' ' ' ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ' ' ' ' ' , , , , , , , > ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } | | | | | 1 1 1 2 2 2 3 %*i*i*7*7*q&5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l l N$5$5$5$5$6$3$3$<$<$<$<$>$>$>$>$>$,$,$$$$$.$]$t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.G G G G G q@%@H H H H H I J J J J c.K K L L M M M M M N N N N N O P P Q Q Q R R S S S `.++*&6+6+%+%+%+&+&+*+*+*+*+*+*+=+=+=+-+-+-+>+M.Q.D+D+D+D+G+l*l*l*l*v$v$v$v$u*b%#*V.V.U.U.T.~+{+]+]+]+]+]+7+7+7+7+9+9+0+0+0+m+f%:+1&9&~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.", +". + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + + + + . ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 1 f*i*i*7*~* *5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e f g g g g g h i j j j j k k l l v*y$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$W#j&t u u u v v v v w w x x x x x x a.y y y z z z A B B B B B C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N N O P Q Q Q R R O >@R$(&5+5+5+6+%+%+%+%+&+*+*+*+*+*+A+w*J.D+D+(%x*` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.k.H$Y+X+`+/+9+7+7+8+9+9+0+0+0+m+m+m+J+J++@y*i@z*].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.", +". + + + + + + + + @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.p.p.p.p.p.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 0*i*i*7*7*9*5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k l i d$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$+$K#j&u u u v v v v w w x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L L M M M M j.N N N N N O P Q Q }+|+2+4+4+4+5+5+5+5+6+%+%+%+%+*&U+I.-%i$X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.,@<+b+Y+(+0+0+0+m+m+m+J+K+K+K+K+K+L+r+~.^.^.e././././.(.(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.", +" . + + + + + + + n.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 A*i*i*i*7*.*5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 a a a b b c c d d e e e e e e g g g g g h h j j j j k k l l Q$e$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$,$,$$$$$$$$$+$($u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I I J J J c.K K L L M M M M M j.N N N N N O .+R$g+k+3+3+4+4+4+5+5+5+5+6+#+U+G.h$W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.,@u&`%f%J+J+K+K+K+K+K+ @.@+@y*x@H%/./././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.", +" . + + + + + + + n.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ n.+ + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ } } | | | | | 1 1 1 2 2 2 B*C*i*i*7*7*!*5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k l n$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$$$b&t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H H H H H I I J J J J K K K L L M M M M M N N N N K%B$D*j+k+k+k+3+3+4+4+4+5+_&.+E*U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).9&1&y*+@K+K+K+ @.@.@.@.@]@G@F*].(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.", +" . + + + + + + + g.@ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ g.+ + + + + + + . ~ ~ { { { ] ] ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 G*i*i*i*7*A&5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k k H*e$e$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$,$,$$$$$$$$$b&t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J c.K K K L L M M M M M N z$1@k+h+i+j+k+k+k+3+3+3+(&A$I*T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.w@p+6% @.@.@.@{@]@]@]@]@&#/@_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.9.", +" . + + + + + + + + g.@ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & * * = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ' ' ' ' ' ' , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = * * & & & % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ g.+ + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 e*i*i*i*7*8*d&5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j k k c$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$$$($t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K L L M M M M o%f+h+h+h+h+i+j+k+k+k+2+|+`.S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].F@q+y*.@{@]@]@]@]@6@6@z@U@(@:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.9.", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % & & & & * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , ' ' ' ' ' ' ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ' ' ' ' ' ' , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * & & & & % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 0*i*i*i*7*3*5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j k k J*l$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$$$K*t t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.G G G G G G H H H H H H I J J J J c.K K L L M M ^%K&w+h+h+h+h+h+h+i+j+f+|+N R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.~.].^.^.^.~.x@G@]@]@]@6@6@7@7@7@9@L*:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , ' ' ' ' ' ' ' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) ' ' ' ' ' ' ' , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 9*i*i*i*7*M*5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e f g g g g g h i j j j j k h d$e$e$e$5$5$5$5$6$3$3$<$<$<$<$>$>$>$>$>$,$,$$$$$K*s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J J K K K L L c.v+x+S+S+h+h+h+h+h+k+R$>@Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././.].*%L+6@6@7@7@m@m@m@|#N*[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * * = = = = = = = = - - ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , ' ' ' ' ' ' ' ' ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) ' ' ' ' ' ' ' ' , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; - - = = = = = = = = * * & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 ~*i*i*i*C*X&5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d e e e e e e g g g g g g h i j j j j k Q$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$$$$$K*s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G i.H H H H H I I J J J c.K K K J O+x+S+S+S+S+h+h+h+f+|+O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.T@9@7@7@m@m@m@O*z@|#P*[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.9.", +" . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & * * = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , , , ' ' ' ' ' ' ' ' ' ) ) ) ) ) ) ) ) ) ) ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ) ) ) ) ) ) ) ) ) ) ' ' ' ' ' ' ' ' ' , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * * & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 %*i*i*i*7*%*5 5 5 5 5 5 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g h h i j j j k 4$m$e$e$e$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$,$$$V#s s t t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I I J J J c.K I O+&@S+S+S+S+S+x+Y%x%z$N N O P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._.(@U@Q*m@m@O*z@A@A@R*P*[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.9.9.", +"~ . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ % % % % % & & & & * = = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , , , , , ' ' ' ' ' ' ' ' ' ' ' ' ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ' ' ' ' ' ' ' ' ' ' ' ' , , , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = * & & & & % % % % % $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 1 f*i*i*7*8*d&5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j `$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$,$,$W#]$s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I J J J J I R@+#R+R+S+S+S+S+Y%7&N N N N N O P Q Q Q R R S S S T T T T U V V V W W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#...L&t$M&M&M&M&(*(*$.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.S*&#z@z@A@A@A@A@|#z&}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.6.7.8.8.8.9.9.", +"~ ~ . . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % & & & * * = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , , , , , ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' , , , , , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = * * & & & % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 3 T*i*i*i*7*9*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j j h l$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$,$W#]$s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G G H H H H H I I J J t&<*+#=@=@R+R+S+S+Y%u+M N N N N N O P P Q Q R R R S S S T T T U U V V V W r.X X X X Y Y Z Z Z ` ` Q%'*U*I+D+F$-&M.N.N.N.N.N.l+,+,+,+,+'+'+!+!+~+~+~+U.&*/+`+Z+Y+b+<+;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.(@%#n@A@A@A@W@W@H@V*}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.9.", +"~ ~ ~ . . + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % & & & & * = = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , , , , , , , , ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' , , , , , , , , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = * & & & & % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + . . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 e*i*i*i*7*0*5 5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j j Q$m$e$e$e$e$5$5$5$5$6$3$3$<$<$<$<$>$>$>$>$>$,$n*W*s s s t t t u u u v v v v w w x x x x x x a.y y y y z z A B B B B B C C C D D D E E E E b.F G G G G G i.H H H H H I I J O+E@|@=@=@=@R+R+Y%u+M M j.N N N N N O P Q Q Q R R R S S T T T T U V V V V W X X X X X Y Y j$D+,%L.-+-+;+;+O.O.O.O.P.N.N.N.N.N.,+,+,+,+'+'+)+!+!+~+~+~+~+{+]+]+]+]+]+7+7+0+~@Y+)@3@'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.(.(.(._._.:.:.:.:.<.[.:.%#A@A@W@W@W@W@B@J@|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.9.9.9.", +"{ ~ ~ ~ . + + + + + + + + + @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & * * = = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = * * & & & % % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ + + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 0*i*i*i*7*3*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j j 4$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$X*o*s s s s t t t u u v v v v v w w x x x x x x y y y y z z A A B B B B C C C C D D E E E E E F G G G G G G H H H H H I I Y*Q+*@*@|@=@=@R+Z*u+M M M M N N N N N N O P Q Q Q R R S S S T T T T U V V V V W X V h$G.U+z+=+=+=+=+-+-+-+;+;+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+9+9+0+0+(+)@3@).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.`*{#W@W@W@W@W@*#l#;#1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.", +"{ { ~ ~ ~ . + + + + + + + + + @ @ @ @ @ @ @ @ @ # # $ $ $ $ $ $ $ % % % % % % & & & & * = = = = = = = = = = - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - = = = = = = = = = = * & & & & % % % % % % $ $ $ $ $ $ $ # # @ @ @ @ @ @ @ @ @ + + + + + + + + + . ~ ~ ~ { { { { ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < [ [ [ [ [ [ [ } } | | | | 1 1 1 2 2 2 2 A*i*i*i*7*M*5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j k$m$m$e$e$e$e$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$>$ =s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G G H H H H H %@w+*@*@*@*@=@=@/&Y*L M M M M M N N N N N O P P Q Q Q R R S S S T T T U U V V V F. +.=*+*+*+*+*+=+=+=+=+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+{+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+0&j@i@w@~.~.].].^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.7#B@W@W@W@*#]#m#z&1.2.2.3.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8.8.9.9.+=", +"{ { ~ ~ ~ . + + + + + + + + + @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & * * = = = = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & % % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ + + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 k*i*i*i*7*;*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j g l$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$>$>$@=r s s s s t t t u u u v v v v w w x x x x x x y y y y z z z A B B B B B C C C D D D E E E E b.F G G G G G i.H H H H #=[@}@*@*@*@|@/&l&K L L M M M M j.N N N N N O P Q Q Q R R R S S T T T T U h$H.#%$+&+y+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+L.-&F$D+l*l*;&L&L&M&M&(*$.$.%.%.$.Z%Y.Y.Y.X.X+Y+`+/+8+]+7+7+7+7+8+9+0+0+0+0+m+m+J+J+K+K+K+$=G%F@^.^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.}.}.}./#H@*#*#]#]#]#n#%=2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.8.8.9.9.+=", +"{ { { ~ ~ ~ . + + + + + + + + + g.@ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & & * * = = = = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > , , , , , , , , , , , , , , , , , , , , , , , , , , , , , > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & & % % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ g.+ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 9*i*i*i*7*A&5 5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h i j j j +%m$m$m$e$e$e$5$5$5$5$6$3$3$3$<$<$<$>$>$>$>$>$&=r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C C D D E E E E E F G G G G G G H H H _@+#e@}@*@*@*@*=v+K K L L M M M M M j.N N N N N O P Q Q Q R R S S S T T `.++T+%+%+%+%+&+*+*+*+*+*+*+*+=+=+Q&q*F$G+'*`&+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.,@<+:+_+Z+a+0+0+0+m+m+m+J+J+K+K+K+K+K+ @&%==z*^./././././.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.-=:&]#]#]#]#X@e#1.3.4.4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.9.+=", +"] { { { ~ ~ ~ . + + + + + + + + + g.@ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % & & & & * * = = = = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; > > , , , , , , , , , , , > > ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = * * & & & & % % % % % % $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ g.+ + + + + + + + + . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 ~*i*i*7*C* *5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j T$J$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$[$r r s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G G G G H H ;=P@e@}@}@*@*@<@N+K K K L L M M M M M N N N N N O P P Q Q Q R R S S P ++(&5+5+6+%+%+%+%+&+*+*+*+*+#+w*p*j$Z Z ` ` ` . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.,@'@!@>=m+J+J+K+K+K+K+K+ @.@.@.@&%#@F@/./.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.E&m#]#X@X@X@8#k#4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.+=", +"^ ] { { { ~ ~ ~ . . + + + + + + + + n.@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ % % % % % % & & & & & * = = = = = = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = = = * & & & & & % % % % % % $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ n.+ + + + + + + + . . ~ ~ ~ { { { ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 ,=i*i*i*7*%*5 5 5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e g g g g g g h h j j g M$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$~$o r s s s s s t t t u u v v v v v w w x x x x x x y y y y z z z A B B B B B C C C D D D E E E E b.F G G G G G i.##[@e@e@e@}@}@Q+Y*J c.K K K L L M M M M j.N N N N N O P Q Q Q R R O .+C$5+5+5+5+6+%+%+%+%+&+&+#+U+-%V X Y Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).'=G%)=+@K+K+K+ @ @.@.@.@{@]@]@L+*%!%(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.;#!=X@X@X@H@H@9#4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.+=", +"^ ] ] { { { ~ ~ ~ . . + + + + + + + + n.@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % & & & & & * = = = = = = = = = = = = = - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - = = = = = = = = = = = = = * & & & & & % % % % % % $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ n.+ + + + + + + + . . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 ~*i*i*7*C* *5 5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j j .%J$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$,$_$q r s s s s t t t u u u v v v v v w x x x x x x x y y y y z z A A B B B B C C C C D D E E E E E F G G G G G G @#e@e@e@e@e@*@~=J J J c.K K L L M M M M M j.N N N N N O P Q Q Q }+V&4+4+4+5+5+5+5+6+%+%+%+#%I*V X X X X Y Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.w@==k@l@.@.@.@.@{@]@]@]@]@7@{=L*_._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.|.|.1.1.1.f./#=#H@H@=#=#]=0#4.m.5.5.5.5.5.6.7.8.8.8.9.9.+=+=", +"^ ^ ] ] { { { ~ ~ ~ . . + + + + + + + + + @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ % % % % % % & & & & & * * = = = = = = = = = = = = - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - = = = = = = = = = = = = * * & & & & & % % % % % % $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ + + + + + + + + + . . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 a*i*i*7*7*%*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 a a a a b b c c d d e e e e e g g g g g g h h j j W$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$>$^=q r s s s s s t t t u u u v v v v w w x x x x x x a.y y y y z z A A B B B B C C C D D D E E E E E F G G G G E +#e@e@e@e@e@+#S@J J J J K K K L L M M M M M N N N N N O P P Q K%f+3+3+4+4+4+5+5+5+5+6+#%H.i$V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.F@x@y*+@.@]@]@]@]@]@6@6@7@V@0@:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.|.l#H@=#=#=#=#/=5.5.5.5.5.5.7.7.8.8.8.9.9.+=+=", +"^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + g.@ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ % % % % % % % & & & & & * * = = = = = = = = = = = = = = - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - = = = = = = = = = = = = = = * * & & & & & % % % % % % % $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 ~*i*i*7*~* *5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j g M$J$m$m$e$e$e$5$5$5$5$5$6$3$3$<$<$<$<$>$>$>$K*q q r s s s s s t t u u u v v v v v w x x x x x x x y y y y z z z A B B B B B C C C D D D E E E E b.G G G G }#!#.&e@e@e@P@(=I I J J J c.K K K L L M M M M M N N N N N O P |+g+k+3+3+3+4+4+4+5+5+V&H.U V V V W W X X X X X Y Z Z Z Z ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.H%r+p+p%]@]@6@6@7@7@7@m@n@_=(.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.2#n#=#=#=#=#u#P#5.5.5.6.7.7.8.8.8.9.9.+=+=", +"^ ^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + n.@ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ % % % % % % % & & & & & * * = = = = = = = = = = = = = = = - - - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - - - = = = = = = = = = = = = = = = * * & & & & & % % % % % % % $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 ,=i*i*7*7*|*5 5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 0 0 0 a a a b b b c c d e e e e e e g g g g g g h h j j :=J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$b*q q r r s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E F G G G [#r@r@e@e@e@E@l&I I J J J J c.K K L L M M M M M j.N N N N N ;@4+k+k+k+3+3+4+4+4+*& +F.T U V V V V W X X X X X Y Y Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././.F*L+6@6@7@7@7@m@m@m@z@_=:.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.e#=#=#8#n#R&p#5.5.6.7.7.8.8.9.9.9.+=+=", +"^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ . + + + + + + + + + + @ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ % % % % % % % & & & & & & * * = = = = = = = = = = = = = = = = = - - - - - - - - - ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; - - - - - - - - - = = = = = = = = = = = = = = = = = * * & & & & & & % % % % % % % $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ + + + + + + + + + + . ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 ~*i*7*7*~* *5 5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j T$J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$>$~$-$q q r s s s s s t t u u u v v v v v w w x x x x x x y y y y z z z A B B B B B C C C D D D E E E E E F G E d@r@r@.&e@e@Q@H H I I J J J J K K K L L M M M M M N N N N 1+3+i+j+k+k+k+3+3+4+R$<=T T T T U V V V V W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.%.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.{.].^.^.^.^.e././././.T@^@7@7@7@m@m@m@z@z@n@0@[.[.[.}.}.}.}.}.|.1.1.1.f.2.2.3.3.3.4.4.**n#8#n#n#M#[=5.6.7.8.8.8.9.9.+=+=+=", +"/ ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . + + + + + + + + + + g.@ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ % % % % % % % % & & & & & * * * = = = = = = = = = = = = = = = = = = = - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - = = = = = = = = = = = = = = = = = = = * * * & & & & & % % % % % % % % $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 9*i*i*7*@*|*5 5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b b c c d e e e e e e g g g g g g h i j h M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$>$^=q q r r s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C C D D E E E E E b.G }#!#r@r@r@e@P@0%H H H I I J J J c.K K K L L M M M M M N N 1+k+h+i+j+k+k+k+3+=%|+P S S T T T U U V V V W W X X X X X Y Z Z Z Z ` ` . . .+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(.(@U@g%m@m@m@z@A@A@A@R*P*}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.f.}=n#n#n#n#|=7.7.8.8.8.9.9.+=+=+=", +"/ / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . . + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ % % % % % % % % & & & & & & * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = - - - - - - - - - = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * & & & & & & % % % % % % % % $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + . . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < 0.[ [ [ [ [ [ [ } } | | | | 1 1 1 1 2 2 2 3 3 3 k*i*7*7*Y& *5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j 9%J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$>$K*q q q r s s s s s t t u u u u v v v v w w x x x x x x a.y y y y z z A A B B B B C C C D D D E E E E E F [#r@r@r@r@r@E@l&H H H I I J J J J c.K K L L M M M M M j.7&k+h+h+h+i+j+k+k+g+2@R R S S T T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.7%n@O*z@A@A@A@A@A@H@V*}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.4.5.9#n#n#n#M#1=2*8.8.8.9.9.+=+=+=", +"/ / / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . + + + + + + + + + + @ @ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ % % % % % % % % % & & & & & & * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * & & & & & & % % % % % % % % % $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ @ + + + + + + + + + + . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 3 3 3 A*i*7*7*@*|*5 5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j `$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>$|$q q q r r s s s s t t t u u u v v v v v w x x x x x x x y y y y z z z A B B B B B C C C D D D E E E E b.}&r@r@r@r@r@2=H H H H H I I J J J c.K K K L L M M M M t&g+h+h+h+h+i+i+j+g+|+Q R R S S S T T T T U V V V V W X X X X X Y Y Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.$#V@A@A@A@A@A@W@W@B@7#}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.[=M#n#M#R&}*p#8.9.9.9.+=+=+=", +"( / / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % & & & & & & & * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * & & & & & & & % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + . ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 3 0*i*7*@*~* *5 5 5 5 6 6 6 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e f g g g g g g h i j I$J$J$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$,$-$q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C D D D E E E E E ,#r@r@r@r@h*P%H H H H H H I I J J J c.K K K L L M M c.f+x+h+h+h+h+h+i+D*|+Q Q R R R S S S T T T U U V V V W W X X X X X Y Z Z Z Z ` ` ` . .+.+.+.+.#.#.#...L&;&v$v$v$3=#*#*V.V.V.b%u*X.X.X+w$H$H$-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.<.L*|#A@A@A@W@W@W@W@z#%=1.1.1.1.2.2.3.3.3.4.4.4.4.m.5.5.5.p#R&M#R&f#f#v#9.9.9.+=+=4=", +"( ( / / / ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ . . + + + + + + + + + + g.@ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % & & & & & & & & * * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * * & & & & & & & & % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + . . ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( _ : : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 e*i*7*@*@*A&5 5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h i j W$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$>${$q q q r r s s s s t t t u u u v v v v v w x x x x x x x y y y y z z z A B B B B B C C C D D D E E E }#!# #r@r@r@!#v%G H H H H H I I J J J J K K K L L M M u+/&S+x+h+h+h+h+k+;@P Q Q Q R R R S S T T T T U V V V V W r.X X X X Y Y Z Z Z ` ` ` . ...2%(%G+F$R.Q.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+{+]+]+]+]+5=~@(+_+:+t*)%'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.:.:.<.[.[.[.P*n@W@W@W@W@W@*#]#6=1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.5.5.P#7=R&f#f#f#8=9.+=+=+=4=", +"( ( ( / / / / ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ . + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % & & & & & & & & & & * * * * * = = = = = = = = = = = = = = = = = = = = = = = = = = = * * * * * & & & & & & & & & & % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + . ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < [ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 3 T*7*7*@*@*;*5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b b c c d e e e e e e g g g g g g h h i g M$J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$[$q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B C C C C D D E E E }#h*O@r@r@r@u%E G i.H H H H H I I J J J c.K K K L L c.g+S+S+S+h+h+h+h+1+O P P Q Q Q R R S S S T T T T U V V V V W X X X X X Y Y Z Z Z ` x*G+r$N.>+O.O.O.P.N.N.N.N.N.,+,+,+,+,+'+'+!+!+!+~+~+~+{+]+]+]+]+]+7+7+7+7+8+9+0+~@_+u&d+).).!.!.~.~.~.~.{.].^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.I@9=W@W@W@*#*#]#z#;#2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.6.7.Q#f#f#f#0=a=+=+=+=4=", +"( ( ( ( / / / / ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % & & & & & & & & & & & & & * * * * * * * * * * * * * * * * * * * * * * * & & & & & & & & & & & & & % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + . ~ ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ } } } | | | | 1 1 1 1 2 2 2 3 3 3 3 1 C*7*@*@*q&5 5 5 5 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e g g g g g g h i j b=J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$,$o*q q q r r s s s s t t t u u u v v v v v w w x x x x x x y y y y z z z A B B B B B C C C D D D E E b@r@N@ #r@r@d@G G G H H H H H H I J J J J c.K K L L u@x+S+S+S+x+h+h+f+z$N O P Q Q Q R R R S S S T T T U U V V V W W X X X X X Y Z =&D+,%3%-+-+;+;+O.O.O.O.P.N.N.N.N.l+,+,+,+,+'+'+)+!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+6%j@d+~.~.~.~.].^.^.^.^.e././././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.c=7%W@W@*#]#]#]#m#d=2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.e=0=0=0=0=o#7.+=4=", +"( ( ( ( ( / / / / ^ ^ ^ ^ ^ ] ] { { { ~ ~ ~ ~ . . + + + + + + + + + + + g.@ @ @ @ @ @ @ @ @ @ @ @ # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % % % & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # @ @ @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + + . . ~ ~ ~ ~ { { { ] ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 3 3 G*7*@*@*@*S&5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g h h i `$J$J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$!$q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z A A B B B B B C C C D D D E m% #N@O@ #r@M@G G G i.H H H H H I I J J J J K K K J n%S+S+S+S+S+h+k+|+N N N O P Q Q Q R R S S S T T T T U V V V V W r.X X X X i$~&O.=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+0+0+0+m+m+m+J+J+K+K+)=i@{.].^.^.^.^.e././././.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.J@B@*#]#]#]#]#X@1#1.3.3.4.4.4.4.m.5.5.5.5.5.6.7.8.8._*f=0=0=}=g=h=4=", +"( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % % % % % % % & & & & & & & & & & & & & & & & & & & & & & & & & % % % % % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + . ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 2 2 2 2 3 3 3 3 1 C*@*@*@*.*5 5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g h i j :=J$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$[$q q q q r r s s s s t t t u u u v v v v v w w x x x x x x y y y y y z z A A B B B B C C C D D D E M%O@N@N@ #r@~#G G G G H H H H H H I I J J J c.K K %@/&R+S+S+S+S+S+x%N N N N O P P Q Q Q R R S S S T T T T U V V V V W X X -%U+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+{+{+]+]+]+]+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+i=y*r+~.^.^././././.(.(.(._._.:.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.-=m#]#]#]#X@X@8#6=4.4.4.4.4.5.5.5.5.5.5.6.7.8.8.8.j=f=}=}=}=k=h=", +"_ ( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + . . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : : < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 ,=7*@*@*@*3*5 5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e g g g g g g h i j T$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$,$-$q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C C D D D ~#N@N@N@N@ #q@G G G G G H H H H H I I J J J J c.K :@R+R+S+S+S+S+w+K%N N N N N O P Q Q Q R R R S S S T T T U U V V V W V+I.*+*+*+*+*+=+=+=+=+-+-+-+;+;+O.O.O.O.P.N.N.N.N.l+,+,+,+,+'+'+'+!+!+T.b%W.X.X+Y+Y+`+`+/+~@9+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+ @ @.@+@y*l=/./././.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.;#z#]#X@X@X@H@H@-#4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.6.k=}=}=f=k=", +": _ ( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + . ~ ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : : < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 4 0*@*@*@*w&5 5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c d d e e e e e e g g g g g g g h i j I$J$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$'$q q q q r r s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A B B B B C C C C D D M%N@N@N@N@m=m%G G G G G i.H H H H H I I J J J c.I w+=@R+R+S+S+x+K&M N N N N N N O P Q Q Q R R S S S T T T T U V V i$I.V&y+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.l+,+,+,+,+'+'+!+V.Z%*.*.*.=.=.-.-.-.-.-.;.;.;.<+b+Y+(+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@p%#@]./.(.(._._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.6==#X@X@H@H@=#f#f.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.7.g=}=f=f=", +": : _ ( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ . + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ % % % % % % % % % % % % % % % % % % % % % % % % % % % $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + . ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 n=@*@*@*@*|*5 5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g h i j W$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$|$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C C D D M%O@N@N@N@ #j#G G G G G G H H H H H H I J J J J u+&&=@=@R+R+S+w+c.M M N N N N N O P P Q Q Q R R S S S T T T T U -%#%%+%+&+*+*+*+*+*+*+*+=+=+=+-+-+-+;+;+>+O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+(*&.&.*.*.*.=.=.-.-.-.-.;.;.;.;.;.,.,.'.'.,@u&!@>=J+J+K+K+K+K+K+ @.@.@.@.@]@]@]@p%o=T@(._._._.:.:.:.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.|.1#H@H@H@=#=#n#p=m.5.5.5.5.5.6.7.8.8.8.9.9.9.+=7.q=f=f=", +": : : _ ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + . . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 4 A&@*@*@*~* *5 6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e g g g g g g h h i i M$J$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$>$r=q q q q r r s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A B B B B C C C C D L@s=N@N@N@O@#=F G G G G G i.H H H H H I I J J J ~==@=@=@R+R+S+v+M M M j.N N N N N O P Q Q Q R R R S S T T T T E*T+%+%+%+&+&+*+*+*+*+*+*+=+=+=+=+-+-+-+P.r$F+F+E+-&P.N.N.N.N.N.,+,+,+,+'+'+b%&.&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).r+t=&%K+K+K+K+ @.@.@.@{@]@]@]@]@6@6@8@_=_.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.6==#=#=#=#=#=#Q#5.5.5.5.5.6.7.8.8.8.9.9.+=+=+=h=a=f=", +": : : : _ ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + + g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.+ + + + + + + + + + + + + . . ~ ~ ~ ~ { { { { ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 4 4*~*@*@*@*v&5 6 6 6 7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b c c c d e e e e e e f g g g g g g h i j W$J$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$&=q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C C D m%s=N@N@N@O@m%F G G G G G G H H H H H H I I J J <@|@=@=@=@R+Q+t&M M M M N N N N N O O P Q Q Q R R S S S T T `.*&6+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+O.-&G+`& ...+.+.+.+.L&Q.N.N.N.l+,+,+,+,+'+'+v$&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.w@i@y*l@.@.@.@.@{@]@]@]@]@6@6@7@7@z@7%:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.;#u==#=#=#=#n#{&P#5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=a=", +": : : : : _ ( ( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 4 4 9*@*@*@*@*S&5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$~$o q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B C C C C m% #.#N@N@O@q@b.F G G G G G i.H H H H H I I J N+Q+*@|@=@=@R+<@c.M M M M j.N N N N N O P Q Q Q R R R S S S `.*&5+5+6+%+%+%+%+&+*+*+*+*+*+*+*+Q&p*j$` ` ` . ...+.+.+.@.#...Q.N.N.l+,+,+,+,+'+'++*&.&.*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].h@p++@.@{@]@]@]@]@6@6@7@7@7@m@n@0@<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.u#=#=#8#n#n#0=p#5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=", +"< : : : : : _ ( ( ( ( ( ( ( ( / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ ~ . + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # # # $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # # # # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + . ~ ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / ( ( ( ( ( ( ( ( _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 4 4 5 w&@*@*@*w&5 6 6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e g g g g g g g h i j x$J$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$!$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C C j# #.#N@N@N@M%E F G G G G G G H H H H H H I I N+&@*@|@=@=@=@O+L M M M M M N N N N N N O P Q Q Q R R R S <=*&5+5+5+6+%+%+%+%+&+&+*+*+*+*+*+*+D+Z Z Z ` ` ` . .+.+.+.+.@.#.#.l*N.N.,+,+,+,+'+'+'+!+&.*.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.H%x@G@]@]@]@6@6@6@7@7@m@m@m@m@^@a@[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.v==#8#n#n#n#R&e=6.7.8.8.8.9.9.9.+=+=4=4=4=4=", +"< < : : : : : _ _ ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # # # # # # # # # # $ $ $ $ $ $ $ $ $ # # # # # # # # # # # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + + . . ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( _ _ : : : : : < < < 0.[ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 4 4 5 !*@*@*@*@*9*6 6 6 7 7 7 7 8 9 9 9 9 9 0 0 0 0 a a a b b c c d d e e e e e e f g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$|$q q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B B C C t% #.#.#N@N@~#E E F G G G G G G H H H H H I I u+R+*@*@|@=@=@l&L L M M M M M N N N N N O P P Q Q Q R R F.A$4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+w=Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.l+l+,+,+,+,+'+'+!+!+(**.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.~.~.~.~.~.].].^.^.^.e./.T@o=p%6@6@7@7@7@m@m@m@O*z@A@{#`*[.[.}.}.}.}.}.|.|.1.1.1.2.2.2.3.3.4.4.4.4.**R&n#n#n#n#M#e=7.8.8.8.9.9.+=+=+=4=4=4=4=", +"< < < : : : : : : _ ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + + + . . ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( _ : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 5 5 v&@*@*@*~*a&6 6 7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g h h i j x$J$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$*$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C t%D%.#.#N@N@~#E E b.G G G G G G i.H H H H H I l&*=*@*@*@=@+#%@L L M M M M M j.N N N N N O P Q Q Q R R g@-@4+4+5+5+5+6+6+%+%+%+%+&+*+*+*+*+*+A+Z Z Z Z ` ` ` . ...+.+.+.@.#.#.#.#.x=l+,+,+,+,+'+'+!+!+v$*.*.*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e./././.].^@7@7@7@7@m@m@m@z@z@A@A@A@_=[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.p#f#n#n#n#M#R&y=8.8.8.9.9.+=+=+=4=4=4=4=", +"[ < < < : : : : : : _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { ~ ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + n.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ n.+ + + + + + + + + + + + + + + + . . ~ ~ ~ ~ ~ { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 5 5 *~*@*@*@*/*6 6 7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g g h i j g M$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$;$q q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B B C C 6#.#.#.#N@M@E E E F G G G G G G H H H H H H X%*@*@*@*@|@&&I K L L M M M M M N N N N N N O P Q Q Q O z=4+4+4+5+5+5+5+6+%+%+%+%+&+y+*+*+*+*+*+E.Z Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.A=,+,+,+,+'+'+'+!+!+U.*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.!%V@7@7@m@m@m@z@A@A@A@A@A@X@V*}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.m.5.P#}=n#n#M#R&R&[=8.9.9.9.+=+=+=4=4=4=4=", +"[ [ < < < : : : : : : _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + . . ~ ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ : : : : : : < < < [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 5 5 5 9*@*@*@*@*I&6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e f g g g g g g h i j j B=J$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$_$q q q q q r s s s s s t t t u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C C >*.#.#.#N@d@E E E b.G G G G G G i.H H H H H R@}@*@*@*@*@s@I K L L M M M M M j.N N N N N O P Q Q Q A$3+3+4+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+C=Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.'+,+,+,+'+'+!+!+!+~+*.*.*.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._=Q*m@m@O*z@A@A@A@A@A@W@W@%#c=}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.5.5.5.P#u#n#M#R&f#f#t#9.9.+=+=+=4=4=4=4=D=", +"[ [ [ 0.< < < : : : : : _ _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + + + . . ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ _ : : : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 5 5 5 5 q&@*@*@*[&6 6 7 7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b c c c d e e e e e e e g g g g g g h h i j j M$m$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$K*q q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B B C i#)#.#.#.#u%E E E E F G G G G G G H H H H H (=}@}@*@*@*@Y%K K K L L M M M M M N N N N N N O P Q }+-@3+3+3+4+4+5+5+5+5+6+6+%+%+%+&+&+*+*+*+*+*+*+(%Z Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.d.+*,+,+,+'+'+!+!+!+~+w$*.=.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.L*z@m@O*z@A@A@A@A@A@W@W@W@9=V*|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.k=R&R&f#f#f#E=9.+=+=+=4=4=4=4=D=", +"[ [ [ [ 0.< < < : : : : : : _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + + + + + n.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.n.+ + + + + + + + + + + + + + + + + + + + . . ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5 5 *@*@*@*H&G&6 7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g g h i j j W$J$m$m$m$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$,$o q q q q r r s s s s s t t u u u u v v v v w w x x x x x x x y y y y z z z A B B B B B C Z@.#.#.#.#6#v%E E E b.G G G G G G i.H H H H #=}@}@*@*@*@<@c.K K L L L M M M M j.N N N N N O P P |+k+k+3+3+4+4+4+5+5+5+5+6+%+%+%+%+&+*+*+*+*+*+*+*+E+Z Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.G$,+,+'+'+'+!+!+~+~+u**.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.a@z@z@z@A@A@A@A@A@W@W@W@W@W@1#1.1.1.1.2.2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.o#R&f#f#0=0=8=+=+=+=4=4=4=4=D=", +"[ [ [ [ [ 0.< < < : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ] ] ] { { { { ~ ~ ~ ~ ~ . . . + + + + + + + + + + + + + + + + + + + + + + n.n.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.n.n.+ + + + + + + + + + + + + + + + + + + + + + . . . ~ ~ ~ ~ ~ { { { { ] ] ] ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 9*@*@*H&H&)&7 7 7 7 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e g g g g g g g h i j j j Z$m$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$'$q q q q q r s s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A B B B B B t%'#)#.#.# #}#E E E E F G G G G G G H H H H :@e@}@}@*@*@w+c.K K K L L M M M M M N N N N N N O `.x%k+k+3+3+3+4+4+4+5+5+5+F=T+#+%+%+%+&+*+*+*+*+*+*+*+B+Z Z ` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.Z%,+,+'+'+!+!+!+~+~+5==.=.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.!%z@A@A@A@A@A@W@W@W@W@W@*#*#8#;#1.f.2.2.3.3.4.4.4.4.4.m.5.5.5.5.5.6.7.Q#f#f#0=0=0=y=+=4=4=4=4=D=D=", +"[ [ [ [ [ [ [ < < < : : : : : : _ _ ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { ~ ~ ~ ~ ~ ~ . . + + + + + + + + + + + + + + + + + + + + + + + + + + + n.n.n.g.g.@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ g.g.n.n.n.+ + + + + + + + + + + + + + + + + + + + + + + + + + + . . ~ ~ ~ ~ ~ ~ { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( _ _ : : : : : : < < < [ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 .*@*H&H&2&7 7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e e g g g g g g h h i j j T$J$m$m$e$e$e$e$5$5$5$5$6$6$3$3$<$<$<$<$,$o q q q q r r s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B B B B C G=)#.#.#s=m%E E E E b.F G G G G G i.H H H ##P@}@}@*@*@<@J c.K K K L L M M M M M N N N N N O >@4+k+k+k+3+3+4+4+4+5+*&I*F.U V V h$#+&+&+*+*+*+*+*+*+=+,%Z Z ` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.a%,+'+'+!+!+!+~+~+~+k.=.-.-.-.-.-.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.l.#@A@A@A@A@A@W@W@W@W@W@*#*#]#m#6=2.2.2.3.3.4.4.4.4.4.5.5.5.5.5.5.7.7.8.t#f#0=0=}=}=H=4=4=4=4=D=D=", +"[ [ [ [ [ [ [ [ 0.< < < : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ~ ~ ~ ~ . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . ~ ~ ~ ~ ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 ;*@*H&H&H&3*7 7 7 8 8 9 9 9 9 9 0 0 0 a a a a b b c c c d e e e e e e e g g g g g g h h i j j j I$m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$'$q q q q q r s s s s s t t t u u u v v v v v w w x x x x x x a.y y y y z z A A B B B B B >*)#)#.#.#C@E E E E E F G G G G G G H H H ##P@e@}@}@*@<@J c.K K K L L M M M M M j.N N N N N ;@j+k+k+k+3+3+3+4+4+C$H.T T T U V V V E.&+*+*+*+*+*+*+*+=+Q&Z ` ` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.v$'+'+)+!+!+~+~+~+~+I=-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.:.:.<.T@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#-=2.2.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.v#0=0=}=}=}=J=4=4=4=D=K=", +"} [ [ [ [ [ [ [ [ 0.< < < : : : : : : _ _ ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ~ ~ ~ ~ . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . ~ ~ ~ ~ ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( _ _ : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } } | | | | | 1 1 1 1 1 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 |*H&H&H&w&5 7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e g g g g g g g h i j j j k$m$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$~$o q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B B B B Z@)#)#.#.#$&E E E E E b.F G G G G G G H H l&W%e@}@}@*@Z*J J c.K K K L L M M M M M N N N N N f+i+j+k+k+k+3+3+3+(&<=T T T T U U V V V W *&*+*+*+*+*+*+=+=+=+=&` ` ` . ...+.+.+.@.#.#.#.#.#.d.$.$.$.Z%'+'+!+!+!+~+~+~+{+)*-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.:.<.L*A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@1#1.3.3.4.4.4.4.m.5.5.5.5.5.6.7.7.8.8.9.v#0=}=}=}=f=L=4=4=D=K=", +"} } [ [ [ [ [ [ [ [ 0.< < < : : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ~ ~ ~ ~ ~ . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . ~ ~ ~ ~ ~ ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 [&H&H&w&A&7 7 8 8 9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e f g g g g g g h i j j j j +%m$m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$'$q q q q r r s s s s s t t t u u u v v v v v w x x x x x x x y y y y y z z A A B B B B y '#)#)#.#D%D E E E E E F G G G G G G H H _@[@e@e@}@}@Q+J J J K K K L L M M M M M j.N N N z$g+i+j+j+k+k+k+3+3+A$S S T T T T U V V V V W U+*+*+*+*+*+*+=+=+=+E+` ` ` . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.$.T.'+!+!+!+~+~+~+{+]+-.-.-.-.;.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.l.<.B@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@u=;#4.4.4.4.4.5.5.5.5.5.5.6.7.8.8.8.9.9.M=}=}=f=f=N=O=D=D=K=", +"| } } [ [ [ [ [ [ [ [ [ < < < < : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { ~ ~ ~ ~ ~ ~ . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . . ~ ~ ~ ~ ~ ~ { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : < < < < [ [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 X&H&H&w&w&S&7 8 8 9 9 9 9 9 9 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g h h i j j j g m$m$m$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$>$~$o q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B B B B 6#)#)#.#s=m%E E E E E b.F G G G G G G H _@[@e@e@}@}@Q+N+J J c.K K K L L M M M M M N N N g@k+h+i+j+k+k+k+3+=%}+S S T T T T U U V V V V W -%*+*+*+*+*+*+=+=+=+D+` ` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.u*)+!+!+~+~+~+~+{+]+5%-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.:.:.<.9@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#X@X@X@H@8#,&4.4.4.4.5.5.5.5.5.6.7.7.8.8.8.9.9.+=P=}=f=f=f=k=Q=K=K=", +"| | } } } [ [ [ [ [ [ [ [ 0.< < < : : : : : : : _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] { { { { { { ~ ~ ~ ~ ~ ~ . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . . . . . ~ ~ ~ ~ ~ ~ { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ : : : : : : : < < < 0.[ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 |*H&H&w&w&5 7 8 9 9 9 9 9 9 0 0 0 a a a a b b c c d d e e e e e e e g g g g g g g h i j j j j 4$m$m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$'$q q q q r r s s s s t t t u u u u v v v v w w x x x x x x x y y y y y z z A A B B B B (#)#)#)#.#R=D E E E E E F G G G G G G i.H +#e@e@e@}@R+N+J J J K K K L L M M M M M j.N N K%Y%h+i+j+j+k+k+k+2+O S S S T T T T U V V V V W W V *+*+*+*+*+=+=+=+=+-&` ` . .+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.Y.!+!+!+~+~+~+{+{+]+X+-.-.-.;.;.;.;.>.,.'.'.'.'.).).!.!.!.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.:.:.:.9@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@H@S=**4.4.m.5.5.5.5.5.6.7.7.8.8.9.9.9.+=7.o#f=f=f=f=g=T=K=", +"| | | } } } [ [ [ [ [ [ [ [ [ < < < < : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] { { { { { { ~ ~ ~ ~ ~ ~ ~ . . . . . . . + + + + + + + + + + + + + + + + + + + + + + + + + . . . . . . . ~ ~ ~ ~ ~ ~ ~ { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : < < < < [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 6 1*H&w&w&A&7 8 9 9 9 9 9 9 0 0 0 a a a a b b c c c d d e e e e e e g g g g g g g h i j j j j k l$m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$,$o q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A B B B B y .#)#)#.#$&D E E E E E b.F G G G G G G H <@e@e@e@e@=@%@J J J c.K K K L L M M M M M N N K%h+h+h+i+j+k+k+k+V&R R S S S T T T T U V V V V W X X .=*+*+*+*+=+=+=+-+O.` . ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.T.!+~+~+~+~+{+]+]+/+-.-.;.;.;.;.;.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.:.:.:.^@z@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@=#=#S=U=4.m.5.5.5.5.5.6.7.8.8.8.9.9.9.+=+=h=k=f=f=1=1=V=K=", +"| | | | | } } [ [ [ [ [ [ [ [ [ 0.< < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ . . . . . . . . . . . . . . . . . . . . . ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < 0.[ [ [ [ [ [ [ [ [ } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 6 ]&H&w&w&w&S&8 9 9 9 9 9 9 0 0 0 a a a a b b c c c d d e e e e e e g g g g g g g h i j j j j j W=m$e$e$e$e$5$5$5$5$5$6$3$3$3$<$<$<$<$>$K*q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B G=)#)#)# #j#D E E E E E F G G G G G G i.(=e@e@e@e@*@S@J J J J K K K L L M M M M M j.N |+h+h+h+i+j+j+k+k+|+R R S S S T T T T U V V V V W W X X I.*+*+*+=+=+=+=+-+-+Q% . ...+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.G$!+~+~+~+~+{+]+]+]+-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._._.:.:.|#z@z@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#X@X@X@H@H@=#=#=#=#2#5.5.5.5.5.5.7.7.8.8.8.9.9.+=+=+=4=h=N=f=1=1=N=X=", +"| | | | | | } } } [ [ [ [ [ [ [ [ [ < < < < : : : : : : : _ ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( ( _ : : : : : : : < < < < [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 6 6 6 G&w&w&w&w&Y=9 9 9 9 9 9 0 0 0 a a a a b b b c c d d e e e e e e f g g g g g g h h i j j j j k +%m$e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$-$q q q r r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z A A B B B B (#)#)#)#.#M%D E E E E E b.F G G G G G G ##e@e@e@e@e@X%I J J J c.K K K L L M M M M M N |+h+h+h+h+i+j+k+k+g@R R R S S S T T T T U V V V V W X X X -%*+*+*+=+=+=+=+-+-+(% . .+.+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.Y.!+~+~+~+{+{+]+]+]+b+-.;.;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.e././././.(.(._._._.:.a@&#m@z@A@A@A@A@A@W@W@W@W@W@*#=#l#l#^#]#X@X@X@H@H@=#=#=#=#8#Q#5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=O=Z=1=N=N=N=", +"1 | | | | | | } } } [ [ [ [ [ [ [ [ [ 0.< < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < 0.[ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 5 5 5 5 5 5 5 5 6 6 6 6 2&w&w&w&/*9 9 9 9 9 9 0 0 0 a a a a b b b c c d d e e e e e e e g g g g g g g h i j j j j k h m$e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$;$q q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A A B B B y .#)#)#)#`@D D E E E E E F G G G G G G J%[@e@e@e@e@P+I J J J J K K K L L L M M M M M |+h+h+h+h+i+i+j+D*z$Q R R S S S T T T T U U V V V W W X X X V *+*+*+=+=+=+-+-+-+`= ...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.k.&.&.T.~+~+~+{+]+]+]+]+Z+;.;.;.;.>.,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._._._.a@n@m@O*z@A@A@A@A@A@W@W@W@W@H@J@|.1.1.1.0#1#X@H@H@=#=#=#=#=#8#n# -5.5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=.-1=N=N=N=", +"1 1 | | | | | | | } } [ [ [ [ [ [ [ [ [ [ < < < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < < [ [ [ [ [ [ [ [ [ [ } } | | | | | | | 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 5 5 5 5 5 5 5 5 6 6 6 6 a&w&w&w&n&+-9 9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h i j j j j k k B=e$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$>$*$q q q r s s s s s t t t u u u u v v v v w w x x x x x x x a.y y y y z z A A B B B B D%)#)#)# #}#D E E E E E b.G G G G G G G Q+e@e@e@e@+#I I J J J c.K K K L L M M M M M 7&h+h+h+h+h+i+j+D*z$Q R R R S S S T T T T U V V V V W r.X X X X .=*+=+=+=+=+-+-+-+r$ .+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.u*~+~+{+{+]+]+]+]+8+;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(._._.(@n@m@m@z@z@A@A@A@A@A@W@W@W@^#}.|.1.1.1.1.2.2.-=H@H@=#=#=#=#=#n#n#n#Q#5.5.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=@-N=N=N=", +"1 1 1 1 | | | | | | } } } [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 3*w&w&n&[&x&9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h i j j j j j k k {%e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$|$q q q r r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A A B B B k&)#)#)#)##-D D E E E E E F G G G G G G (=e@e@e@e@[@%@I J J J J c.K K L L L M M M M t&h+h+h+h+h+h+i+D*}+Q Q R R R S S T T T T U U V V V V W X X X X X w**+=+=+=+-+-+-+;+P...+.+.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.&.Y.~+~+{+{+]+]+]+]+]+,@;.;.;.>.,.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.e././././.(.(._._.(@A@m@m@m@z@A@A@A@A@A@W@W@W@7%}.|.|.1.1.1.1.2.2.2./=H@=#=#=#=#8#n#n#n#n#[=6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=K=$-N=N=", +"1 1 1 1 1 | | | | | | } } } [ [ [ [ [ [ [ [ [ [ 0.< < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { { ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 7 A&w&n&[&%-9 9 9 9 0 0 0 0 a a a b b b c c d d e e e e e e e g g g g g g g h h i j j j j k k W=e$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$!$q q q r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y y z z A A B B B t%s#)#)#)#$&D D E E E E E b.G G G G G G v%e@e@e@e@P@##I I J J J c.K K K L L M M M M v@Z*h+h+h+h+h+i+D*N Q Q Q R R S S S T T T T U V V V V W W X X X X X E.=+=+=+=+-+-+-+;+;+t$+.+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.T.~+{+]+]+]+]+]+7+S$;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(._.L*z@m@m@m@O*z@A@A@A@A@A@W@W@&-}.}.|.|.1.1.1.f.2.2.3.3.-==#=#=#=#8#n#n#n#n#n#*-7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=K=T=q=N=", +"2 2 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] { { { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { { { { ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 Y=w&n&[&[&N&9 9 9 0 0 0 0 a a a a b b c c c d d e e e e e e f g g g g g g h h i j j j j k k k a$e$e$e$5$5$5$5$6$6$3$3$3$<$<$<$>$>$>$~$=-q q r s s s s s t t t u u u u v v v v v w x x x x x x x x y y y y z z z A B B B B 5&)#)#)#s#j#D D E E E E E F G G G G G G [@e@e@e@e@(=I I J J J J c.K K L L L M M M c./&h+h+h+h+h+h+k+>@Q Q Q R R R S S S T T T U U V V V V W X X X X X Y ;%=+=+=+-+-+-+;+;+;+Q%+.+.+.@.#.#.#.#.#.$.$.$.$.$.%.%.&.&.&.*.*.v${+{+]+]+]+]+]+7+~@;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^./././././.(.(.N*g%7@m@m@m@z@z@A@A@A@A@A@9=/#}.}.}.|.1.1.1.1.2.2.2.3.3.4.9#=#=#=#n#n#n#n#n#n#M#--8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=;-N=", +"2 2 2 1 1 1 1 1 | | | | | | } } } [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] { { { { { { { { { { { { { { { { ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ { { { { { { { { { { { { { { { { ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 ;*n&[&[&[&)&9 9 0 0 0 0 a a a a b b c c c d d e e e e e e f g g g g g g g h i j j j j k k k 0$l$e$e$5$5$5$5$5$6$3$3$3$<$<$<$<$>$>$>$K*q q r r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y z z z A A B B B Z@5#)#)#)#C@D D E E E E E b.G G G G G G c@e@e@e@e@Q@I I I J J J c.K K K L L M M M ^%]*x+h+h+h+h+h+k+>@P Q Q Q R R S S S T T T T U V V V V W W X X X X X Y Z Q&=+=+-+-+-+;+;+>+F++.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.*.*.*.Y.{+]+]+]+]+]+7+7+7+,@;.,.,.'.'.'.).).).!.!.~.~.~.~.~.].^.^.^.^.e././././.(.(.N*g%7@m@m@m@m@z@A@A@A@A@A@X@J@}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.-#=#8#n#n#n#n#n#M#R&}*P#8.8.9.9.+=+=+=4=4=4=4=D=K=K=K=K=>-", +"2 2 2 2 1 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7 ,-[&[&[&2&9 9 0 0 0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h i j j j j k k k l c$e$e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$_$q q r s s s s s t t t u u u v v v v v w w x x x x x x x a.y y y y z z A A B B B B D%)#)#)#D%D D E E E E E E F G G G G G b@e@e@e@e@[@H I I J J J J K K K L L L M M M <*S+h+h+h+h+h+h+g@P Q Q Q R R R S S S T T T T U V V V V W X X X X X Y Y Z ,%=+=+-+-+-+;+;+O.F$+.+.@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.=.]+]+]+]+]+]+7+7+7+b+>.,.'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.S*g%7@7@m@m@m@O*z@A@A@A@A@^@c=}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.e#8#n#n#n#n#n#M#R&R&u#5.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=", +"3 2 2 2 2 2 1 1 1 1 1 | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] ] ] { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { ] ] ] ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ } } } } | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7 8 %-[&[&[&T&9 0 0 0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h i j j j j j k k k l b=e$5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$[$q q r s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y z z z A A B B B '-)#)#)#)#L@D D E E E E E F G G G G G E J&e@e@e@e@##I I J J J J c.K K K L L M M M K&S+x+h+h+h+h+h+g@P P Q Q Q R R S S S T T T T U V V V V W W X X X X X Y Y Z >%=+-+-+-+;+;+>+O.L.+.+.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.&.*.*.*.=.)*]+]+]+]+]+7+7+7+`+>.,.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.e././././.(./@7@7@7@7@m@m@m@z@z@A@A@A@U@[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.u#n#n#n#n#n#M#R&f#f#9#9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=", +"3 3 3 2 2 2 2 1 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 8 8 x&[&[&[&[&+-0 0 0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h i i j j j j k k k l i e$e$5$5$5$5$6$6$3$3$3$<$<$<$<$>$>$>$>$!$q r r s s s s s t t t u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B p&)#)#)#$&D D E E E E E b.F G G G G G ~#e@e@e@e@;=H I I J J J J K K K L L L M M t&S+S+h+h+h+h+h+7&O P Q Q Q R R R S S S T T T T U V V V V W X X X X X X Y Z Z =&=+-+-+-+;+;+O.O.O.^*@.#.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.*.=.b+]+]+]+]+7+7+7+7+8+,.,.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.)-7@7@7@7@m@m@m@O*z@A@A@A@7%[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.4.4.m.p#n#n#n#n#M#R&R&f#f#f#8=9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=", +"3 3 3 3 2 2 2 2 1 1 1 1 1 1 | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 N%[&[&[&[& &0 0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h h i j j j j k k k l l !-e$5$5$5$5$6$6$3$3$3$<$<$<$<$>$>$>$>$~$=-r r s s s s s t t t u u u v v v v v w w x x x x x x x a.y y y y z z z A B B B B (#)#)#)#.#j#D D E E E E E F G G G G G }#.&e@e@e@d@H I I J J J J c.K K K L L M M c./&S+x+h+h+h+h+1+O P P Q Q Q R R S S S T T T T U V V V V W W X X X X X Y Y Z Z Z O.-+-+;+;+>+O.O.O.Q%#.#.#.#.#.#.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.~-]+]+]+7+7+7+7+8+5%'.'.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^././././.x@6@6@7@7@7@m@m@m@O*z@A@n@P*[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.m.5.}=n#n#n#M#R&R&f#f#0=0={-+=+=+=4=4=4=4=D=K=K=K=K=K=]-", +"3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ 0.< < < < : : : : : : : : : : _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ : : : : : : : : : : < < < < 0.[ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 T&[&[&[&#&0 0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h h i j j j j k k k l l l ^-5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$&=q r s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y z z z A A B B B B s#)#)#)#,#D D E E E E E b.F G G G G G u%e@e@e@e@##H I I J J J J K K K L L L M M w+S+S+h+h+h+h+x%N O P Q Q Q R R R S S S T T T T U V V V V W X X X X X X Y Z Z Z Z -&-+-+;+;+>+O.O.O.s$#.#.#.#.#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.`+]+]+7+7+7+7+7+9+!@'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.e./././.==6@6@6@7@7@m@m@m@m@z@A@n@a@[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.9#n#n#n#M#R&f#f#f#0=0=f=2*+=4=4=4=4=D=D=K=K=K=K=K=]-", +"3 3 3 3 3 3 3 2 2 2 2 1 1 1 1 1 1 | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | 1 1 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 o&[&[&!&/-0 0 a a a a b b c c c d d e e e e e e e g g g g g g g h h i j j j j k k k l l l i 5$5$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$>$*$r s s s s s s t t t u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B (#)#)#)#'#}#D D E E E E E F G G G G G q@.&e@e@e@;=H I I J J J J c.K K K L L M M f+S+S+x+h+h+h+=%N O P P Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z ` G+-+;+;+>+O.O.O.O.F%#.#.#.#.#.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.%%]+]+7+7+7+7+8+9+0+;.'.'.).).).!.!.~.~.~.~.{.].^.^.^.^.e././.p+]@6@6@7@7@7@m@m@m@O*z@|#:.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.4.4.4.m.5.5.9#n#n#M#R&R&f#f#f#0=0=}=g=+=4=4=4=4=D=D=K=K=K=K=K=(-", +"4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ / / / / / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7 8 8 9 9 9 x&[&[&!&!&y%0 a a a a b b c c c d d e e e e e e e g g g g g g g h h i j j j j k k k l l l l g$5$5$5$5$6$6$3$3$3$<$<$<$>$>$>$>$>$>$~$r r s s s s s t t t u u u v v v v v w w x x x x x x x x y y y y z z z A A B B B B '#)#)#)#C@D D E E E E E b.G G G G G E h*e@e@e@[@_@H I I J J J J K K K L L M M |%S+S+S+h+h+h+Y%z$N O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` (%-+;+;+>+O.O.O.O.Q.#.#.#.#.d.$.$.$.$.%.%.%.&.&.*.*.*.*.=.=.-.-.-.]+]+7+7+7+7+8+9+0+u&'.'.).).!.!.!.~.~.~.~.{.].^.^.^.^./.].G@]@]@6@6@7@7@7@m@m@m@z@9@:.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.4.m.5.5.}=n#n#M#R&R&f#f#0=0=0=}=}=v#4=4=4=4=D=K=K=K=K=K=]-(-", +"5 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 I&[&!&!&!& &a a a a b b c c c d d e e e e e e e g g g g g g g g h i j j j j j k k k l l l m n$5$5$5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$_-r s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y z z z A A B B B B (#)#)#)# #D D E E E E E E F G G G G G M@e@e@e@e@:@H I I J J J J c.K K K L L M c./&S+S+x+h+h+h+|+N O P P Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z ` ` ` >+;+;+O.O.O.O.P.N.#.#.#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.b%7+7+7+7+8+9+9+0+(+'.).).).!.!.~.~.~.~.{.].^.^.^.^.e.].L+]@]@6@6@7@7@7@m@m@m@m@U@[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.5.p#n#n#n#M#R&f#f#f#0=0=}=}=}=f=.-4=4=D=D=K=K=K=K=K=]-(-", +"5 5 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < : : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( / / / / / / / / / / / / / / / / / / / / / / / / / / / ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : : : : < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 9 9 9 G&!&!&!&T%a a a a b b c c c d d e e e e e e e g g g g g g g g h i j j j j j k k k l l l l :-5$5$5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$-$s s s s s t t t u u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B B '#)#)#)#C@D D E E E E E F G G G G G v%.&e@e@e@+#H H I I J J J c.K K K L L M M <*S+S+S+h+h+h+x%N N O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` M.;+>+O.O.O.O.N.N.L&#.#.d.$.$.$.$.%.%.%.&.&.&.*.*.*.=.=.-.-.-.-.%%7+7+7+7+8+9+0+0+0+;.).).).!.!.~.~.~.~.{.].^.^.^.^.z*p%]@]@]@6@6@7@7@7@m@m@m@<-<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.m.5.P#}=n#n#M#R&R&f#f#f#0=0=}=}=}=f=k=h=4=D=D=K=K=K=K=K=(-(-", +"5 5 5 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ : : : : : : : : : : : : < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 9 9 9 9 %-!&!&U%[-a a b b b c c c d d e e e e e e e g g g g g g g g h i j j j j j k k k l l l l m b$5$5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$>$>$.$s s s s s s t t t u u u v v v v v w w x x x x x x x x y y y y z z z A A B B B B (#)#)#)# #D D E E E E E b.F G G G G G }-e@e@e@e@##H I I J J J J c.K K K L L M 7&S+S+S+h+h+h+D*N N O P P Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z ` ` ` ` r$;+O.O.O.O.P.N.N.l*#.#.d.$.$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.-.,@7+7+7+8+9+9+0+0+0+5@).).!.!.~.~.~.~.~.].].^.^.^.l=p%]@]@]@6@6@6@7@7@7@m@Q*0@<.<.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.m.5.{&n#n#n#M#R&R&f#f#0=0=0=}=}=}=f=f=|-4=D=K=K=K=K=K=]-(-(-", +"5 5 5 5 5 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 | | | | | | | | | } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : : _ _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ : : : : : : : : : : : : : < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } | | | | | | | | | 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 0 e&!&!&U%U%1-a b b b c c c d d e e e e e e e g g g g g g g g h i j j j j j k k k l l l l m m b*5$5$6$6$3$3$<$<$<$<$>$>$>$>$>$>$>$,$_-s s s s s t t t u u u v v v v v w w w x x x x x x x y y y y y z z A A B B B B B #)#)#.#C@D D E E E E E F G G G G G b@.&e@e@e@Q@H I I I J J J c.K K K L L M M ]*S+S+x+h+h+h+g@N N O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` .U*>+O.O.O.O.N.N.N.x=#.d.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.g&7+7+8+9+0+0+0+0+m+).).!.!.~.~.~.~.{.].^.^.^.p+{@]@]@]@]@6@6@7@7@7@m@z@L*l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4.5.9#n#n#n#n#R&R&f#f#f#0=0=}=}=}=f=f=f=f=2-D=K=K=K=K=K=]-(-(-", +"5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < < : : : : : : : : : : : : : : : _ _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ : : : : : : : : : : : : : : : < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 0 0 3-!&U%U%U%S%b b b c c c d d e e e e e e e g g g g g g g g h i j j j j j k k k l l l l m m m 8$5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$,$,$o*s s s s t t t u u u u v v v v v w w x x x x x x x y y y y y z z A A B B B B B c*)#)#)# #D D E E E E E b.G G G G G G u%e@e@e@e@_@H I I J J J J c.K K K L L M v+S+S+S+h+h+h+x%N N O P P Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z Z ` ` ` .'*O.O.O.O.P.N.N.N.4-#.d.$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.;.;.~@7+7+8+9+0+0+0+m+m+M+!.!.!.~.~.~.~.{.].^.~.y*.@]@]@]@]@]@6@6@7@7@7@n@a@:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.4.4.m.o#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=f=f=f=f=g=D=K=K=K=K=K=(-(-(-", +"5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < : : : : : : : : : : : : : : : : _ _ _ _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ _ _ : : : : : : : : : : : : : : : : < < < < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 O&U%U%U%%-b b b c c c d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m g*5$6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$.$s s s s t t t t u u u v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B _#)#)#.#5-D E E E E E b.F G G G G G b@.&e@e@e@;=H I I J J J J c.K K K L L M t&S+S+S+x+h+h+Y%N N O O P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...6-O.O.O.N.N.N.N.N.#.$.$.$.$.$.%.%.&.&.&.*.*.*.=.=.-.-.-.-.-.;.3@7+7+8+9+0+0+0+0+m+m+f%!.!.~.~.~.~.{.].^.h@$=.@{@]@]@]@]@6@6@7@7@7@|#:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.4.4. -n#n#n#n#n#M#R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=@-K=K=K=K=]-(-(-(-", +"5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < < < : : : : : : : : : : : : : : : : : : : : _ _ _ _ _ _ _ _ _ ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( _ _ _ _ _ _ _ _ _ : : : : : : : : : : : : : : : : : : : : < < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 7-U%U%U%i%b b c c c d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m 8-6$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$$${$s s s s t t t u u u v v v v v w w x x x x x x x x y y y y z z z A A B B B B B Z@)#)#)#s=j#D E E E E E F G G G G G G }-e@e@e@P@_@H I I J J J J K K K L L L M ]*S+S+S+h+h+h+1+N N O P Q Q Q Q R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .+.-&O.O.P.N.N.N.N.N.t$$.$.$.$.%.%.k.&.&.*.*.*.*.=.-.-.-.-.-.-.;.Y+7+7+8+9+0+0+0+0+m+m+m+t=!.~.~.~.~.{.].p+.@.@.@{@]@]@]@]@6@6@7@7@#@:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.4.4.d=n#n#n#n#n#n#R&R&f#f#f#0=0=}=}=}=f=f=f=f=1=1=N=T=K=K=K=]-(-(-(-", +"6 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 | | | | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < : : : : : : : : : : : : : : : : : : : : : : : : : : : _ _ _ _ _ _ _ _ _ _ _ _ _ : : : : : : : : : : : : : : : : : : : : : : : : : : : < < < < < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | | 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a U%U%U%I%9-b c c d d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m m a$6$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$0-s s s t t t u u u v v v v v v w w x x x x x x x y y y y y z z A A B B B B B C y#)#)#.#,#D E E E E E b.F G G G G G b@e@e@e@e@;=H I I J J J J c.K K K L L M K&S+S+S+x+h+h+h+z$N O P P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.F$O.O.N.N.N.N.N.N.v$$.$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.-.;.<+7+7+8+9+9+0+0+0+m+m+m+J+K+w@~.~.~.~.$@p%.@.@.@{@]@]@]@]@6@6@6@7@o@_.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.4.4.4.4.2#8#n#n#n#n#n#M#R&R&f#f#f#0=f=}=}=}=f=f=f=f=1=N=N=$-K=K=K=(-(-(-(-", +"6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.< < < < < < < < < : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : < < < < < < < < < 0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } | | | | | | | | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 8 9 9 9 9 9 9 0 0 0 0 a a S%U%U%I%I%[%c c d d d e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m m n 8$3$3$3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$.$s s s t t t u u u u v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B t%.#)#.#.#j#E E E E E E F G G G G G G }-e@e@e@W%_@H I I J J J c.K K K L L M c./&S+S+S+h+h+h+f+N N O P Q Q Q R R R S S S T T T T U U V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.F+O.P.N.N.N.N.N.l++*$.$.$.%.%.%.&.&.*.*.*.*.=.=.-.-.-.-.-.<+K$7+7+8+9+0+0+0+0+m+m+m+J+J+&%w@~.w@a- @.@.@.@.@{@]@]@]@]@6@6@g%0@_.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.4.**S=n#n#n#n#n#n#M#R&f#f#f#f=j=9.6.[*}=f=f=f=f=1=N=N=N=b-K=]-(-(-(-(-", +"6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < < < : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : < < < < < < < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } | | | | | | | | | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a a a 1-I%I%I%z%0 c d d e e e e e e e e g g g g g g g g h i i j j j j k k k l l l l m m m m m m g*6$3$3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$$$K*s s t t t u u u u v v v v v w w x x x x x x x x y y y y z z z A A B B B B B C i#)#)#.#D@D E E E E E F G G G G G G }#e@e@e@e@(=H I I J J J J c.K K K L L M v+S+S+S+x+h+h+h+7&N O P P Q Q Q R R R S S S T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.+.Q%O.N.N.N.N.N.N.,+!+$.$.$.%.%.&.&.&.*.*.*.*.=.-.-.-.-.-.S$7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K++@K+K+ @.@.@.@.@]@]@]@]@]@6@7@L*_._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.4.4.p=S=8#n#n#n#n#n#M#R&R&f#f#}=j=9.+=+=+=O=N=f=f=1=1=N=N=N=V=K=c-(-(-(-(-", +"7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.< < < < < < < < < < < < < < : : : : : : : : : : : : : : : : : : : : : : : : : : : : : < < < < < < < < < < < < < < 0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } | | | | | | | | | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a a a a 9-I%I%z%d-c d d e e e e e e e e g g g g g g g g h i i j j j j k k k k l l l l m m m m m n g$3$3$<$<$<$<$>$>$>$>$>$>$>$>$,$$$$$$$ $s t t t t u u u v v v v v w w x x x x x x x x y y y y y z z A A B B B B B C C '#)#.#.#M%E E E E E b.F G G G G G G Q@e@e@e@W%H I I J J J J c.K K K L L M c.S+S+S+S+h+h+h+h+K%N O P Q Q Q R R R S S S T T T T U V V V V V W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.+.` P.N.N.N.N.N.l+,+,+I+$.%.%.%.&.&.&.*.*.*.=.=.-.-.5%Z+5=7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@e-_._.:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.4.4.0#n#=#8#n#n#n#n#n#M#R&R&f#}=j=9.9.+=+=+=4=O=Z=f=1=N=N=N=N=N=2-(-(-(-(-(-", +"7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < 0.0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a b A%I%z%z%O&d d e e e e e e e e g g g g g g g g h i i j j j j k k k k l l l l m m m m m n n b*3$3$<$<$<$<$>$>$>$>$>$>$>$,$$$$$$$$$]$s t t t u u u v v v v v v w w x x x x x x x y y y y y z z z A A B B B B B C c*)#.#.# #E E E E E E F G G G G G G f-e@e@e@e@;=H I I J J J J K K K L L L M v+S+S+S+h+h+h+h+h+g@O P P Q Q Q R R R S S T T T T T U V V V V W r.X X X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.L.N.N.N.N.N.,+,+,+,+S.Y.%.&.&.&.*.*.*.*.k.Y+`+~-]+]+7+7+7+7+7+9+9+0+0+0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@]@]@]@]@]@6@7@_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.0#n#=#=#n#n#n#n#n#n#M#R&f#}=P#9.9.9.+=+=+=4=4=.-f=1=N=N=N=N=N=V=(-(-(-(-(-", +"7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.0.< < < < < < < < < < < < < < < < < < < < < < < < < < < < < 0.0.0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b I%z%z%z%g-d e e e e e e e e g g g g g g g g h i i j j j j k k k k l l l l m m m m m n n o <$3$<$<$<$<$>$>$>$>$>$>$>$,$$$$$$$$$W#s t t t u u u u v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B C C y#)#.#.#,#E E E E E F G G G G G G G s@e@e@e@P@_@I I J J J J c.K K K L L M c.S+S+S+x+h+h+h+h+h+|+O P Q Q Q R R R S S S T T T T U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.+.+.@.R.N.N.N.N.l+,+,+,+,+'+'+T.G$M&Z%w$X.b%u${+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@z@_._._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.3.3.f.n#=#=#8#n#n#n#n#n#M#R&R&}=j=8.9.9.+=+=+=4=4=4=4=h-1=N=N=N=N=N=i-b-(-(-(-(-", +"8 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ 0.0.0.0.0.0.0.[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a b b b ^&z%z%r%r%S%e e e e e e e e g g g g g g g g h i j j j j j k k k k l l l l m m m m m n n o g*3$<$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$${$t t t u u u u v v v v v w w x x x x x x x x y y y y y z z A A B B B B B C C t%.#.#.#.#}#E E E E b.F G G G G G G J%P@e@e@e@s@I I I J J J c.K K K L L L M f+S+S+S+h+h+h+h+h+h+=%P P Q Q Q R R S S S T T T T U U V V V V W X X X X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.@.x=N.N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@8@_._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.f.u==#=#=#n#n#n#n#n#n#M#R&}*j=8.9.9.9.+=+=+=4=4=4=4=Q=j-N=N=N=N=N=q=$-(-(-(-(-", +"8 8 7 7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a b b b c &z%r%r%r%b e e e e e e f g g g g g g g g h i j j j j j k k k k l l l l m m m m m n n o o '$<$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$I#t t u u u u v v v v v w w x x x x x x x x y y y y y z z z A B B B B B C C C i#.#.#.#!#E E E E E F G G G G G G i.(=e@e@e@e@l&I I J J J J c.K K K L L M c.S+S+S+x+h+h+h+h+h+i+i+D*x%K%N R R R S S S T T T T U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . .+.+.+.+.@.#.I+N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@{@]@]@]@]@]@6@6@g%_._.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.1.f#=#=#=#8#n#n#n#n#n#n#R&}*P#8.8.9.9.+=+=+=4=4=4=4=4=D=k-N=N=N=N=k=q=q=;-(-(-l-", +"9 9 8 8 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | } } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b c c m-r%r%}%A%e e e e e e f g g g g g g g h h i j j j j j k k k k l l l l m m m m m n n o o o :$<$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$+$j&t t u u u v v v v v w w w x x x x x x x y y y y y z z z A A B B B B B C C C 6#.#.#.#~#E E E E F G G G G G G G H E@e@e@e@d@I I J J J J c.K K K L L M M v+S+S+S+h+h+h+h+h+h+i+j+k+k+k+k+(&;@<=S S T T T T U U V V V V W X X X X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.l*N.N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@N*:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.2.3.1.1#=#=#=#=#8#n#n#n#n#n#M#}*n-8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=q=N=N=N=q=q=q=|-(-(-l-", +"9 9 9 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | } } } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } } | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a b b b b c c c 9-r%}%}%o-e e e e e g g g g g g g g h h i j j j j j k k k l l l l l m m m m m n n o o o p 2$<$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$+$K#t t u u u v v v v v v w w x x x x x x x a.y y y y z z z A A B B B B B C C C t%.#.#.#O@}#E E E b.F G G G G G G i.##e@e@e@e@#=I I J J J J K K K L L L M M /&S+S+h+h+h+h+h+h+i+j+j+k+k+k+3+3+3+4+4+R$ +F.T T U V V V V W W X X X X X Y Y Z Z Z ` ` ` ` . .+.+.+.+.@.#.#.;&N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@]@6@6@7@7@#@:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.l#=#=#=#=#=#n#n#n#n#n#n#p-n-8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=q-N=N=N=q=q=q=q=T=(-l-", +"9 9 9 9 9 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | } } } } } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } } } } | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b c c c d d 7-}%}%}%m-e e e e g g g g g g g g h h i j j j j j k k k l l l l l m m m m m n n o o o p m <$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$$$+$)$t u u u v v v v v v w w x x x x x x x x y y y y y z z A A B B B B B C C C C C@.#.#N@u%E E E E F G G G G G G i.H s@e@e@e@W%I I J J J J c.K K K L L M M K&S+S+x+h+h+h+h+h+h+i+j+k+k+k+3+3+3+4+4+4+5+5+5+(&y&H.V V V V W X X X X X X Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.;&N.N.l+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@Q*a@:.:.<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.3.l#H@=#=#=#=#8#n#n#n#n#n#p-n-7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=q=N=k=q=q=q=q=@-l-l-", +"9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | } } } } } } } } } } } } } } } } } } [ [ [ [ [ [ [ [ [ [ [ [ [ } } } } } } } } } } } } } } } } } } | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d d r-}%}%}%}%[%e e e g g g g g g g g h h i j j j j j k k k l l l l l m m m m m n n o o o p p ^$<$>$>$>$>$>$>$>$,$,$$$$$$$$$$$$$+$+$N#u u u u v v v v v w w x x x x x x x x y y y y y z z z A B B B B B B C C C D 6#.#.#N@M%E E E F G G G G G G G H H P@e@e@e@P+I J J J J c.K K K L L M M M w+S+S+h+h+h+h+h+h+i+j+j+k+k+k+3+3+3+4+4+4+5+5+5+5+6+%+%+#+,*F.W X X X X X Y Y Z Z Z ` ` ` ` . .+.+.+.+.@.#.#.#.L&N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@]@]@]@]@]@6@6@7@7@7@m@|#:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.2.2.2.s-H@=#=#=#=#=#n#n#n#n#n#R&<&7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=t-N=q=q=q=q=q=q=u-l-", +"9 9 9 9 9 9 9 9 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } } | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b b c c c d d e C%}%}%}%}%~%e f g g g g g g g g h h i j j j j j k k k l l l l l m m m m m n n o o o p p p b$>$>$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$s u u u v v v v v w w x x x x x x x x y y y y y z z z A A B B B B B C C C C j#s=.#N@O@E E E b.F G G G G G G i.H :@e@e@e@e@l&I J J J J K K K K L L M M t&S+S+x+h+h+h+h+h+i+i+j+k+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+*+#%I.E.V X Y Z Z Z Z ` ` ` . ...+.+.+.+.#.#.#.#.L&N.l+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@U@<.<.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.f.2.2.W&H@H@=#=#=#=#8#n#n#n#n#R&p#7.7.8.8.8.9.9.9.+=+=4=4=4=4=4=D=K=K=K=K=T=k=q=q=q=q=q=q=v-w-", +"0 0 9 9 9 9 9 9 9 9 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d d d e e ^&}%}%}%}%b g g g g g g g g g h i i j j j j j k k k l l l l m m m m m m n n o o o p p p p 7$>$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$V#u u u v v v v v w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D C@.#N@N@u%E E E F G G G G G G i.H H Q@e@e@e@=@I J J J J c.K K K L L M M M <*S+S+h+h+h+h+h+h+i+j+k+k+k+k+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+&+y+*+*+*+*+*+z+w*D+=&Z ` ` ` . . .+.+.+.+.@.#.#.#.#.$%l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@m@m@m@O*_=[.[.[.[.[.[.}.}.}.}.}.|.1.1.1.1.2.2.-=H@H@=#=#=#=#=#8#n#n#n#R&p#6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=x-q=q=q=q=q=y-z-w-", +"0 0 0 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 0 0 0 0 a a a a a b b b b c c c d d e e e e [%}%}%}%A%g g g g g g g g g h i i j j j j j k k k l l l l m m m m m m n n o o o p p p p q |$>$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$W#{$u u v v v v v w w w x x x x x x x a.y y y y z z z A A B B B B B C C C C D D $&N@N@N@~#E E F G G G G G G G H H _@P@e@e@}@A-J J J J c.K K K L L L M M c.x+S+h+h+h+h+h+h+i+j+j+k+k+k+3+3+3+4+4+4+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+Q&-&D+F+`& ...+.+.+.+.#.#.#.#.#.I+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+8+9+0+0+g&a+B-0&_+0&0&+@K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@z@z@S*[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2./#X@H@H@=#=#=#=#=#n#n#n#R&p#5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=C-q=q=q=q=q=y-y-D-", +"0 0 0 0 0 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c d d d e e e e e E-}%}%}%M$g g g g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q o >$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$W#W# $u v v v v v v w w x x x x x x x x y y y y y z z A A B B B B B B C C C D D D s=N@N@N@}#E b.G G G G G G G i.H H :@e@e@e@}@%@J J J J K K K K L L M M M u+S+x+h+h+h+h+h+h+i+j+f+x%f+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+y+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+-&D+F+F+'*L&L&L&L&G+l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+7+7+7+7+/+!@)@,@'.'.).).).!.!.!.~.~.w@x@F-L+.@.@.@.@{@]@]@]@]@6@6@7@7@7@m@m@m@m@z@A@A@0@[.[.[.}.}.}.}.}.|.|.1.1.1.f.J@X@H@H@H@=#=#=#=#8#n#n#M#p#5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-G-q=q=q=y-y-G-H-", +"a a 0 0 0 0 0 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | | | | | | | | | | | | | | | | | | | | | | | | | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b b c c c d d d e e e e e e o-}%I-Y$I$g g g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q q _$>$>$>$>$>$,$$$$$$$$$$$$$+$+$+$+$W#W#s*v v v v v v w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D #-N@N@N@6#E b.F G G G G G G i.H H H Q@e@e@}@+#J J J J c.K K K L L M M M M g+S+h+h+h+h+h+h+i+R$<=Q Q Q R f&2@R$(&4+5+5+5+5+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.N.,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+0+Y+,@>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].^.~.x@y*{@]@]@]@]@]@6@6@7@7@7@m@m@m@O*z@A@A@A@P*[.}.}.}.}.}.|.|.1.1.1.1.J@:&X@H@H@=#=#=#=#=#n#n#M#J-5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=(-(-t-q=q=q=y-y-G-K-", +"a a a 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b c c c c d d e e e e e e e e A%Y$Y$Y$b=g g g g g h h i j j j j j k k k k l l l l m m m m m m n n o o o p p p p q q q ^=>$>$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#t v v v v v w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D M@N@N@N@M@E F G G G G G G G H H H H P@e@}@}@A-J J J c.K K K L L L M M M c.S+x+h+h+h+h+h+i+|+P Q Q Q R R R S S S F.I*++*&6+6+%+%+%+%+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+]+]+]+(+<+;.;.>.,.,.'.'.'.).).).!.!.!.~.~.~.~.{.].^.^.^.^.H%o=p%]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@L*}.}.}.}.}.|.|.1.1.1.U==#X@H@H@H@=#=#=#=#8#n#n#J-5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=]-(-(-T=q=q=q=y-y-G-G-", +"a a a a a 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a b b b b c c c d d d e e e e e e e e r-Y$Y$Y$Y$x$g g g g h i i j j j j j k k k l l l l l m m m m m m n n o o o p p p p q q q q '$>$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#O#v v v v v w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D E 6#N@N@N@m%F G G G G G G G H H H H :@e@}@}@}@Y*J J J K K K K L L M M M M K&x+h+h+h+h+h+h+B$P Q Q Q Q R R S S S T T T T T U -%,*#%q$&+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.N.N.N.N.N.N.,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+]+]+`+5%;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.e././.].o=p%6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@W@P*}.}.}.}.|.1.1.1.k#=#X@X@H@H@=#=#=#=#=#8#n#[=5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-G-q=y-y-G-G-G-", +"b a a a a a a 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a a b b b c c c d d d e e e e e e e e e g T$Y$Y$Y$Y$L-g g h h i j j j j j j k k k l l l l l m m m m m n n n o o o p p p p q q q q q 7$>$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#C&v v v v w w x x x x x x x x a.y y y y z z z A A B B B B B C C C C D D D D }#O@N@N@O@b.F G G G G G G i.H H H H ;=e@}@}@R+J J J c.K K K L L M M M M M w+h+h+h+h+h+h+i+<=P Q Q Q R R R S S S T T T T U V V V V V T -%U+.=*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+]+]+8+b+-.;.;.;.;.>.,.,.'.'.'.).).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.!%^@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@W@H@V*}.|.|.1.1.6=m&X@X@X@H@H@=#=#=#=#=#n#Q#5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-X=q=y-y-G-G-G-", +"b b b a a a a a a 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 a a a a a a b b b b c c c d d d e e e e e e e e e g g W$Y$Y$Y$Y$M-g h h i j j j j j k k k k l l l l l m m m m m n n n o o o p p p p q q q q q q |$>$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#X#v v v w w x x x x x x x x a.y y y y y z z A A B B B B B B C C C C D D D E q@N@N@N@`@F G G G G G G i.H H H H H [@}@}@*@A-J J c.K K K L L L M M M M c.x+h+h+h+h+h+i+=%P Q Q Q R R R S S S T T T T U U V V V V W r.X X X V E.~&A+-+=+=+-+-+-+-+;+;+O.O.O.O.O.N.N.N.N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+]+]+`+t*-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.^./././././.(.(.e-z@7@m@m@m@O*z@A@A@A@A@A@W@W@W@W@W@9=N-l#l#8#]#X@X@X@H@H@=#=#=#=#=#8#o#5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-b-y-y-G-G-G-G-", +"c b b b b a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a b b b b c c c d d d e e e e e e e e e f g g g 9%Y$Y$Y$Y$g h i i j j j j j k k k k l l l l m m m m m m n n o o o o p p p p q q q q q q q >$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#O-v v w w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D E E M@N@N@N@M%G G G G G G G H H H H H _@}@}@}@*@u@J J K K K K L L M M M M M P-h+h+h+h+h+h+i+R$Q Q Q Q R R S S S S T T T T U V V V V W W X X X X X Y Y Z Z j$D+-&O.-+;+;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+{+5=b+-.-.-.;.;.;.;.>.,.,.'.'.'.).).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._.(@V@m@m@m@z@z@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#=#{&5.5.5.5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=(-(-(-(-(-K=y-y-G-G-G-G-", +"c c c b b b b a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a b b b b c c c c d d d e e e e e e e e e g g g g g b=Y$Y$Y$J$h i j j j j j j k k k k l l l l m m m m m m n n o o o p p p p p q q q q q q q -$,$,$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#j&v w w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D E E E 6#N@N@ #}#G G G G G G H H H H H H #=}@}@*@*@N+J c.K K K L L M M M M M M f+h+h+h+h+h+i+j+ %Q Q Q R R R S S S T T T T U U V V V V W X X X X X X Y Z Z Z Z ` ` ` `&s$F%-&O.O.O.P.N.N.N.N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+/+[+-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.{#m@O*z@A@A@A@A@A@A@W@W@W@W@W@*#]#]#]#]#]#X@X@H@H@H@=#=#=#=#=*5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=]-(-(-(-(-(-(-Q-y-G-G-G-G-", +"d c c c b b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b c c c d d d e e e e e e e e e f g g g g g g :=Y$Y$V${%i j j j j j j k k k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q =$$$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#t w w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D E E E E O@N@O@h*G G G G G G i.H H H H H H R-}@*@*@Q+J c.K K K L L L M M M M M j.Y%h+h+h+h+i+i+D*Q Q Q R R R S S S T T T T T U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.x*-&N.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+{+&*-.-.-.-.-.;.;.;.;.>.,.,.'.'.'.).).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.U@z@z@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#u=P#5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-l-S-G-G-G-G-T-", +"d d d c c c b b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b c c c d d d e e e e e e e e e e g g g g g g g g Z$V$V$P$:=j j j j j k k k k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q r U-$$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#O#w w w x x x x x x x x y y y y y z z z A A B B B B B C C C C D D D D E E E m%N@O@ #d@G G G G G i.H H H H H H I =@*@*@*@A-c.K K K L L L M M M M M j.g@h+h+h+h+h+i+j+x%Q Q Q R R R S S S T T T T U V V V V V W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.@.R.N.N.N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+&*-.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.:.:.l.U@A@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@H@=#=#=#n#V-5.5.5.5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-W-G-G-G-G-T-", +"e d d d c c c c b b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b c c c c d d d e e e e e e e e e f g g g g g g g g g {%V$P$J$9%j j j j k k k k l l l l l m m m m m n n n o o o p p p p p q q q q q q q q r s {$$$$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#b&w w x x x x x x x x y y y y y z z z A A B B B B B B C C C D D D D E E E E ~#N@ #r@~#G G G G G H H H H H H I %@}@*@*@*@u@c.K K K L L M M M M M M N 1+h+h+h+h+i+j+j+K%Q Q R R R S S S T T T T U U V V V V W r.X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.#...l+N.N.N.l+,+,+,+,+'+'+)+!+!+~+~+~+~+&*=.-.-.-.-.-.;.;.;.;.>.,.,.'.'.'.).).).).!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.:.<.<.%#A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#S=**m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-l-l-w-K-G-G-G-T-", +"e e e d d d c c c c b b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b c c c c d d d e e e e e e e e e e g g g g g g g g g g h X-P$J$J$W$j j j k k k k l l l l m m m m m m n n n o o o p p p p h.q q q q q q q r r s s Y-$$$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#B#w x x x x x x x x y y y y y z z z A A B B B B B B C C C C D D D E E E E E }- #r@r@b@G G G G H H H H H H H I X%*@*@*@*@v@K K K L L M M M M M M N N f+h+h+h+i+i+j+k+N Q R R R S S S T T T T T U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.x=N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+T.[+-.-.-.-.-.-.;.;.;.;.>.,.'.'.'.'.).).).!.!.~.~.~.~.~.].].^.^.^.^./././././.(.(._._._.:.:.:.:.<.[.[.%#A@A@A@W@W@W@W@W@*#*#]#]#]#]#X@X@H@H@H@=#=#=#v=4.5.5.5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w->-G-G-T-T-", +"e e e e e d d d c c c c b b b b a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a b b b b c c c c d d d e e e e e e e e e e g g g g g g g g g g h h i P$J$J$J$L-j k k k k l l l l l m m m m m m n n o o o o p p p p q q q q q q q q r r s s s W#$$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#Z-x x x x x x x x y y y y y z z z A A B B B B B B C C C C D D D E E E E E E D@r@r@r@E G G G i.H H H H H H I I A-*@*@*@x+K K K L L L M M M M M j.N N Y%h+h+h+i+j+k+f+Q Q R R R S S S T T T T U V V V V V W X X X X X X Y Z Z Z Z ` ` ` ` . .+.+.+.+.+.#.#.#.;&N.N.,+,+,+,+,+'+'+!+!+!+~+~+~+~+H$=.-.-.-.-.-.;.;.;.;.>.,.,.'.'.'.).).).!.!.!.~.~.~.~.{.].^.^.^.^.e././././.(.(.(._._._.:.:.:.l.<.[.[.[.|#A@W@W@W@W@W@W@*#]#]#]#]#X@X@X@H@H@=#=#=#1#4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-`-G-G-T-T-", +"e e e e e e e d d d c c c c b b b b a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a b b b b c c c c d d d e e e e e e e e e e e g g g g g g g g g g h h i g J$J$J$J$T$k k k k l l l l l m m m m m m n n o o o p p p p p q q q q q q q q r r s s s s +$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#G# ;x x x x x x x y y y y y y z z A A A B B B B B C C C C D D D E E E E E E b. #r@r@6#G G G i.H H H H H H I I J Z**@*@*@P+K K K L L M M M M M M N N z$h+h+h+i+j+j+k+1+Q R R R S S S T T T T U U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#...N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+_%=.-.-.-.-.-.;.;.;.;.;.>.,.'.'.'.'.).).).!.!.w@r+r+r+r+].].^.^.^.^./././././.(.(._._._.:.:.:.:.<.[.[.[.[.a@A@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#n#f.4.5.5.5.5.5.5.6.7.7.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=c-(-(-(-(-(-l-l-w-w-D-G-T-T-T-", +"e e e e e e e e d d d d c c c c b b b b a a a a a a a 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 a a a a a a a b b b b c c c c d d d d e e e e e e e e e e g g g g g g g g g g h h i i j `$J$J$J$m$`$k k l l l l l m m m m m m n n n o o o p p p p p q q q q q q q q r r s s s s ]$$$+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#Z&x x x x x x a.y y y y y z z z A A B B B B B C C C C D D D D E E E E E b.j#r@r@r@d@G G G H H H H H H I I I J E@*@*@|@X%K K L L M M M M M M N N N |+h+h+i+i+j+k+k+1+R R R S S S T T T T T U V V V V W W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.@.#.#.#.#.#.l+,+,+,+,+,+'+'+!+!+!+~+~+~+U.=.=.-.-.-.-.-.;.;.;.;.>.,.,.'.'.'.).).).1&)=n+K+K+K+K+K+ @+@5@x@~.e././././.(.(.(._._._.:.:.:.l.<.[.[.[.[.[.I@W@W@W@W@W@*#]#]#]#]#X@X@X@H@H@=#=#=#**4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-G-T-T-T-", +"e e e e e e e e e e d d d d c c c c b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b c c c c d d d d e e e e e e e e e e g g g g g g g g g g g h h i j j j k$J$J$m$m$1$k l l l l l m m m m m m n n n o o o p p p p p q q q q q q q q r r s s s s s s*+$+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#`#x x x x x a.y y y y y z z z A A B B B B B C C C C D D D D E E E E E b.F P%r@r@r@~#G G H H H H H H H I I J N+*@*@|@=@%@K L L L M M M M M j.N N N x%h+h+i+j+k+k+k+f+R R R S S S T T T T U U V V V V W X X X X X X Y Y Z Z Z ` ` ` ` . ...+.+.+.+.#.#.#.#.#.#.l+,+,+,+,+'+'+'+!+!+!+~+~+<%k.=.-.-.-.-.-.;.;.;.;.;.>.,.'.'.'.'.'=5@m+J+J+K+K+K+K+K+ @ @.@.@.@+@o=]./././.(.(._._._.:.:.:.:.<.[.[.[.[.[.[.}.B@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#-#4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-l-l-w-w-w-w-.;T-T-T-", +"g f e e e e e e e e e e d d d d c c c c b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b c c c c d d d d e e e e e e e e e e f g g g g g g g g g g h h i i j j j j T$J$m$m$m$h l l l l l m m m m m m n n o o o o p p p p p q q q q q q q q r r s s s s s s O-+$+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#x#q#x x x x a.y y y y y z z z A A B B B B B C C C C D D D D E E E E E E F G ~#r@r@r@q@G i.H H H H H H I I J J u@*@|@=@=@K L L L M M M M M j.N N N N f+h+i+j+j+k+k+k+3+}+R S S S T T T T U U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.+;,+,+,+,+,+'+'+!+!+!+~+~+~+W.=.-.-.-.-.-.-.;.;.;.;.>.,.,.'.'.'.5@m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@o=!%(.(.(._._._.:.:.:.l.<.[.[.[.[.[.}.}.J@W@W@W@*#]#]#]#]#X@X@X@H@H@=#=#S=f.4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-@;T-T-T-", +"g g g e e e e e e e e e e e d d d d c c c c b b b b b a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a b b b b b c c c c d d d d e e e e e e e e e e e g g g g g g g g g g h h i i j j j j j j T$m$m$m$e$l l l l m m m m m m m n n o o o p p p p p q q q q q q q q q r r s s s s s s t I#+$+$W#W#W#W#K#K#K#K#K#K#G#G#x#x#x#L#x x x x a.y y y y y z z z A A B B B B B C C C C C D D D E E E E E E F G G M@r@r@r@}#i.H H H H H H I I J J J O+|@=@=@&&K L L M M M M M M N N N N N k+i+j+j+k+k+k+3+3+2+S S S T T T T T U V V V V W W X X X X X X Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.G$,+,+,+,+'+'+)+!+!+~+~+~+T.=.=.-.-.-.-.-.;.;.;.;.;.>.,.'.'.;.a+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@L+T@(._._._.:.:.:.:.<.[.[.[.[.[.[.}.}.}.z#W@*#*#]#]#]#]#X@X@X@H@H@=#=#9#4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-#;T-T-K-", +"g g g g g e e e e e e e e e e e d d d d c c c c b b b b b a a a a a a a a 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 a a a a a a a a b b b b b c c c c d d d d e e e e e e e e e e e g g g g g g g g g g g h h i j j j j j j k k $;m$m$e$l$l l l m m m m m m n n n o o o p p p p p q q q q q q q q q r s s s s s s s t t X#W#W#W#W#W#K#K#K#K#K#K#G#G#x#x#x#x#C&x x x a.y y y y y z z z A A B B B B B B C C C C D D D E E E E E E F G G G W%r@r@r@J%H H H H H H I I I J J J w+|@=@=@P+L L M M M M M M N N N N N N h+i+j+k+k+k+k+3+3+3+C$P S T T T T U U V V V V W X X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.+*,+,+,+'+'+'+!+!+!+~+~+~+X.=.-.-.-.-.-.-.;.;.;.;.>.,.'.'.3@g&m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@7@%;_._._.:.:.:.l.<.[.[.[.[.[.}.}.}.}.c=W@*#]#]#]#]#X@X@X@H@H@H@=#=#**4.m.5.5.5.5.5.5.7.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;*;T-T-K-", +"g g g g g g g e e e e e e e e e e e d d d d c c c c c b b b b b a a a a a a a 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 a a a a a a a b b b b b c c c c c d d d d e e e e e e e e e e e g g g g g g g g g g g h h i i j j j j j j k k k 4$e$e$e$d$l m m m m m m m n n o o o o p p p p p q q q q q q q q r r s s s s s s s t t t U-W#W#W#K#K#K#K#K#K#G#G#G#x#x#x#x#x#4&x x a.y y y y y z z z A A B B B B B B C C C C D D D E E E E E E b.F G G G h*r@r@h*H H H H H H I I I J J J J /&=@=@=@O+L L M M M M M j.N N N N N g@i+j+j+k+k+k+3+3+3+4+4+4+++`.T T U U V V V V W W X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.=;,+,+,+'+'+!+!+!+~+~+~+<%=.=.-.-.-.-.-.;.;.;.;.;.,.,.'.,@g&0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@]@6@6@g%N*_.:.:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.-;*#]#]#]#]#X@X@X@H@H@=#=#=#v=4.m.5.5.5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;T-T-K-", +"g g g g g g g g g e e e e e e e e e e e e d d d c c c c c b b b b b a a a a a a a a 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 a a a a a a a a b b b b b c c c c c d d d e e e e e e e e e e e e g g g g g g g g g g g h h i i j j j j j j k k k k k B=e$e$e$Q$m m m m m m n n n o o o p p p p p h.q q q q q q q q r r s s s s s s t t t t u ;;W#W#K#K#K#K#K#K#G#G#G#x#x#x#x#x#x#B#x a.y y y y y z z z A A B B B B B B C C C C D D D E E E E E E b.F G G G E r@r@r@d@H H H H H H I I J J J J c.+#=@=@R+u@L M M M M M j.N N N N N N 2@j+j+k+k+k+3+3+3+4+4+4+5+5+5+&+++>;V V V V W W X X X X X X Y Z Z Z Z ` ` ` ` . .+.+.+.+.+.#.#.#.#.#.#.d.M&,+,+,+'+'+'+!+!+!+~+~+~+v$=.-.-.-.-.-.-.;.;.;.;.>.,.'.;.g&0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@g%L*:.:.:.l.<.[.[.[.[.[.}.}.}.}.}.}.d=]#]#]#]#]#X@X@H@H@H@=#=#=#S=,;5.5.5.5.5.5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=c-(-(-(-(-(-l-l-w-w-w-w-&;&;&;';K-K-", +"g g g g g g g g g g g e e e e e e e e e e e e d d d d c c c c b b b b b b a a a a a a a a 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 a a a a a a a a b b b b b b c c c c d d d d e e e e e e e e e e e e g g g g g g g g g g g h h h i j j j j j j j k k k k l l a$e$e$5$2$m m m m m n n n o o o p p p p p q q q q q q q q q r r s s s s s s t t t t u u {$W#K#K#K#K#K#K#G#G#G#x#x#x#x#x#x#x#%$a.y y y y y z z z A A B B B B B B C C C C D D D E E E E E E b.F G G G G v%r@r@e@Q@H H H H H I I J J J J c.|&=@=@=@R+N+M M M M M M N N N N N N O ;@j+k+k+k+k+3+3+4+4+4+4+5+5+5+5+6+%+#+I.h$V W X X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.);,+,+,+'+'+!+!+!+~+~+~+~+H$=.-.-.-.-.-.;.;.;.;.>.,.,.'.(+0+0+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@n@:.:.:.<.[.[.[.[.[.[.}.}.}.}.}.|.|.=#]#]#]#X@X@X@H@H@=#=#=#=#=#8#u#p#5.5.5.6.7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;!;K-K-", +"h g g g g g g g g g g g g e e e e e e e e e e e e d d d d c c c c c b b b b b b a a a a a a a a 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 8 8 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 a a a a a a a a b b b b b b c c c c c d d d d e e e e e e e e e e e e g g g g g g g g g g g g h h i i j j j j j j k k k k k l l l b=e$5$5$n$m m m m n n o o o o p p p p p q q q q q q q q q r r s s s s s s t t t t u u u F#K#K#K#K#K#K#G#G#G#x#x#x#x#x#x#x#x#~;y y y y y z z z A A B B B B B B C C C C D D D D E E E E E b.F G G G G G b@r@.&e@;=H H H H I I J J J J J K +&=@=@R+R+c.M M M M M N N N N N N N O R$k+k+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+V&H.;%X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.@.#.#.#.#.#.#.$.$.S.,+,+'+'+'+!+!+!+~+~+~+u$=.-.-.-.-.-.;.;.;.;.;.>.,.'.u&0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@%#:.l.<.[.[.[.[.[.}.}.}.}.}.}.|.1.-#]#]#]#X@X@H@H@H@=#=#=#=#8#n#n#n#u#e=5.6.7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;{;K-K-", +"i h h g g g g g g g g g g g g e e e e e e e e e e e e e d d d d c c c c c b b b b b b a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a b b b b b b c c c c c d d d d e e e e e e e e e e e e e g g g g g g g g g g g g h h i i j j j j j j k k k k k l l l l l 2$5$5$5$/$m m n n n o o o p p p p p p q q q q q q q q r r s s s s s s s t t t t u u u u b&K#K#K#K#K#G#G#G#x#x#x#x#x#x#x#x#r&5&y y y y z z z A A B B B B B B C C C C D D D D E E E E E E F G G G G G G q@.&e@e@(=H H H I I J J J J J c.K u+=@R+R+S+M M M M M j.N N N N N N O P C$k+k+k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+&+&+*+*+U+E.X X Y Z Z Z Z ` ` ` ` . ...+.+.+.+.#.#.#.#.#.#.d.$.Z%,+,+'+'+'+!+!+!+~+~+~+~+$*-.-.-.-.-.-.;.;.;.;.>.,.,.'.~@0+m+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@Q*a@<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.k#]#]#X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#7=e=7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;H-K-';", +"j i i h h g g g g g g g g g g g g e e e e e e e e e e e e e d d d d c c c c c b b b b b b b a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a b b b b b b b c c c c c d d d d e e e e e e e e e e e e e g g g g g g g g g g g g h h i i j j j j j j j k k k k l l l l l l m ];5$5$5$b*m n n o o o o p p p p p q q q q q q q q q r r s s s s s s s t t t t u u u u v J#K#K#K#K#G#G#G#x#x#x#x#x#x#x#x#r&h#^;y y y z z z A A B B B B B B C C C C D D D D E E E E E E F G G G G G G G c@e@e@e@#=H H I I I J J J J c.K K u@R+R+R+/&M M M M j.N N N N N N O P P f+k+k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+w*/;Z Z Z Z ` ` ` . ...+.+.+.+.@.#.#.#.#.#.d.$.$.G$,+,+'+'+!+!+!+~+~+~+~+{+Y.-.-.-.-.-.;.;.;.;.;.>.,.'.u&0+0+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@)-<.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.m#]#X@X@H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&}*t#P#9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-w-w-w-w-w-&;&;&;(;_;K-';", +"j j j i i h h g g g g g g g g g g g g e e e e e e e e e e e e e e d d d d c c c c c b b b b b b b a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a b b b b b b b c c c c c d d d d e e e e e e e e e e e e e e g g g g g g g g g g g g h h i i j j j j j j j k k k k k l l l l l m m m ];5$5$5$H*n n o o o o p p p p p q q q q q q q q q r r s s s s s s t t t t u u u u u v v O#K#K#K#G#G#x#x#x#x#x#x#x#x#x#r&h#h#(#y y z z z A A B B B B B B C C C C D D D D E E E E E E F G G G G G G G i.s@e@e@e@##H I I I J J J J c.K K K :;R+R+S+w+M M M M N N N N N N O O P Q g+k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+Q&B+j$` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.d.$.$.S.,+'+'+'+!+!+!+~+~+~+~+{+t*-.-.-.-.-.;.;.;.;.>.,.'.'.~@0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@8@[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.!=X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#M#R&R&f#f#f#k=<;6.+=+=+=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;[;';';", +"j j j j j i h h h g g g g g g g g g g g g f e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a b b b b b b c c c c c c d d d d d e e e e e e e e e e e e e f g g g g g g g g g g g g h h h i j j j j j j j k k k k k l l l l l l m m m m 8$5$5$6$H*o o o o p p p p p h.q q q q q q q q r r s s s s s s s t t t t u u u u v v v v O#K#K#G#G#x#x#x#x#x#x#x#x#x#h#h#h#h#};y z z z A A B B B B B B C C C C D D D D E E E E E E F G G G G G G G G H Q@e@e@e@l&H I I J J J J c.K K K K O+R+S+S+<*M M M N N N N N N N O P Q Q k+k+3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=+=+-+O.F$Q% . ...+.+.+.+.@.#.#.#.#.#.d.$.$.M&,+,+'+'+!+!+!+~+~+~+~+{+8+-.-.-.-.-.;.;.;.;.;.,.,.'.;.0+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@7@m@m@m@z@z@p@[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.-#X@X@H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#0=0=0=}=P=--h=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;[;';';", +"j j j j j j j i h h h g g g g g g g g g g g g g e e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b b a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a a b b b b b b b c c c c c c d d d d d e e e e e e e e e e e e e e g g g g g g g g g g g g g h h h i j j j j j j j k k k k k l l l l l l m m m m m m 8$6$6$6$|;o o o p p p p p q q q q q q q q q r r s s s s s s s t t t t u u u u v v v v v K#K#G#G#x#x#x#x#x#x#x#x#x#h#h#h#h#h#H#z z z A A B B B B B B C C C C D D D D E E E E E E F G G G G G G G G H H d@e@e@e@_@I I J J J J c.K K K K L A-S+S+S+<*M M j.N N N N N N O P P Q Q k+3+3+3+4+4+4+5+5+5+5+5+6+%+%+%+%+%+&+*+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+M.I+` +.+.+.@.#.#.#.#.#.d.$.$.Z%!+,+'+'+)+!+!+!+~+~+~+{+{+`+-.-.-.-.;.;.;.;.;.>.,.'.'.u&0+m+m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@O*z@A@%#[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.J@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=V=t-h=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;H-';';", +"k j j j j j j j j i h h h g g g g g g g g g g g g g e e e e e e e e e e e e e e e d d d d d c c c c c c b b b b b b b b a a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a a a b b b b b b b b c c c c c c d d d d d e e e e e e e e e e e e e e e g g g g g g g g g g g g g h h h i j j j j j j j j k k k k l l l l l l m m m m m m m n 8$6$3$3$K*o p p p p p h.q q q q q q q q q r r s s s s s s s t t t t u u u u v v v v v v G#G#G#x#x#x#x#x#x#x#x#x#h#h#h#h#h#h#`#z z A A B B B B B B C C C C D D D D E E E E E E b.F G G G G G G G H H H E@e@e@e@+&I J J J J J c.K K K L L g+S+S+S+v+M j.N N N N N N O P P Q Q N 3+3+3+4+4+4+4+5+5+5+5+6+6+%+%+%+%+&+y+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+;+O.O.-&(%^*#.#.#.#.#.#.d.$.$.S.,+'+'+'+!+!+!+~+~+~+~+{+]+Z+-.-.-.-.;.;.;.;.>.,.,.'.'._+0+m+m+m+J+J+K+K+K+K+K+ @ @.@.@.@.@]@]@]@]@]@6@6@7@7@7@7@m@m@m@z@z@A@^@[.[.[.}.}.}.}.}.}.|.1.1.1.1.f.%=X@H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#0=0=0=}=}=}=f=f=f=f=1=1;2;T=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;3;';';", +"k k k j j j j j j j j i i h h g g g g g g g g g g g g g f e e e e e e e e e e e e e e e d d d d d c c c c c c c b b b b b b b b b a a a a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a a a a a b b b b b b b b b c c c c c c c d d d d d e e e e e e e e e e e e e e e f g g g g g g g g g g g g g h h i i j j j j j j j j k k k k k l l l l l l m m m m m m m n n 8$3$3$3$K*p p p p p q q q q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v `#G#G#x#x#x#x#x#x#x#x#x#h#h#h#h#h#h#d#`#z A A B B B B B B C C C C D D D D E E E E E E b.F G G G G G G G H H H H [@e@e@}@I J J J J J c.K K K L L L w+S+S+S+4;j.N N N N N N O O P Q Q Q N 3+3+3+4+4+4+5+5+5+5+6+6+%+%+%+%+&+&+*+*+*+*+*+*+*+=+=+=+=+-+-+-+-+;+;+>+O.O.O.O.N.x=;&#.#.#.d.$.M&!+,+,+'+'+!+!+!+~+~+~+~+{+{+]+Y+-.-.-.;.;.;.;.;.>.,.'.'.'.~@m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@n@[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.|.X@H@H@=#=#=#=#=#8#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=N=N=N=q=@-S-c-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;';';';", +"k k k k k j j j j j j j j i i h h g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e d d d d d d c c c c c c c b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 a a a a a a a a a a a a a a a a a a a a a b b b b b b b b b b c c c c c c c d d d d d d e e e e e e e e e e e e e e e e g g g g g g g g g g g g g g h h i i j j j j j j j j k k k k k l l l l l l m m m m m m m n n n o <$3$3$<$'$p p p p q q q q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v w `#G#x#x#x#x#x#x#x#x#x#h#h#h#h#h#h#d#d#q#A A B B B B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H e@e@}@[@I J J J J c.K K K L L L M ]*S+S+S+K&N N N N N N N O P Q Q Q Q f&3+3+4+4+4+5+5+5+5+5+@+#%$+%+%+&+&+*+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.l+x=l*v$+*,+,+,+'+'+'+!+!+!+~+~+~+~+{+]+]+b+-.-.-.;.;.;.;.>.,.,.'.'.'.m+m+m+m+J+J+K+K+K+K+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@:.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=k=G-X=b-K=(-(-l-l-w-w-w-w-&;&;&;&;(;(;5;';';';", +"l l k k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e e d d d d d d c c c c c c c c b b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a b b b b b b b b b b b c c c c c c c c d d d d d d e e e e e e e e e e e e e e e e e g g g g g g g g g g g g g g h h h i i j j j j j j j j k k k k k l l l l l l m m m m m m m n n n o o o <$<$<$<$b$p p q q q q q q q q q q r r s s s s s s s t t t t u u u u v v v v v v v w w `#x#x#x#x#x#x#x#x#x#h#h#h#h#h#h#d#d#d#Z@A B B B B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H e@}@}@=@J J J J c.K K K L L L M M ]*S+S+S+1+N N N N N N O P P Q Q Q R f&3+4+4+4+5+5+5+5+(&-%V V V -%U+V&*+*+*+*+*+*+*+*+=+=+=+=+-+-+-+;+;+;+O.O.O.O.O.N.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+~+{+{+]+]+:%-.-.;.;.;.;.;.>.,.'.'.'.'.m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@:.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.=#H@=#=#=#=#=#8#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=N=N=N=N=N=N=i-q=q=q=q=|-k-W-w-w-w-w-w-&;&;&;(;(;(;6;';';';", +"l l l l k k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e e e d d d d d d c c c c c c c c c b b b b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a b b b b b b b b b b b b b c c c c c c c c c d d d d d d e e e e e e e e e e e e e e e e e e g g g g g g g g g g g g g g g h h h i i j j j j j j j j k k k k k l l l l l l m m m m m m m m n n n o o o o <$<$<$<$b$h.q q q q q q q q q r r s s s s s s s t t t t t u u u u v v v v v v v w w x `#x#x#x#x#x#x#x#x#h#h#h#h#h#d#d#d#d#:#O%B B B B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H %@}@}@}@Q+J J J c.K K K K L L M M M /&S+S+x+7&N N N N N O P P Q Q Q R R }+4+4+4+4+5+5+5+5+H.U V V V V W W V -%U+#+*+*+*+=+=+=+=+-+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+{+]+]+]+H$-.;.;.;.;.;.>.,.'.'.'.'.).m+m+J+J+J+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@:.}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.8#H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=N=q=q=q=q=q=q=y-y-K-7;8;9;&;&;&;&;(;(;0;';';';';", +"l l l l l l k k k k k j j j j j j j j i i i h h g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e d d d d d d d c c c c c c c c c c b b b b b b b b b b b b b b b b b b a a a a a a a a a a a a a a a a a a a a a a a a a b b b b b b b b b b b b b b b b b b c c c c c c c c c c d d d d d d d e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g h h i i i j j j j j j j j k k k k k l l l l l l m m m m m m m m n n n o o o o p p <$<$<$<$!$q q q q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v w w w x x `#x#x#x#x#x#x#r&h#h#h#h#h#d#d#d#d#:#:#y B B B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H +&}@}@*@Q+J J c.K K K K L L M M M M /&S+x+h+7&N N N N O P P Q Q Q R R R <=4+4+4+5+5+5+5+q$U V V V V W W X X X X X h$~&K.=+=+=+=+-+-+-+;+;+>+O.O.O.O.P.N.N.N.N.N.N.,+,+,+,+,+'+'+'+!+!+!+~+~+~+~+{+]+]+]+]+b+-.;.;.;.;.>.,.,.'.'.'.'.).a;m+J+J+K+K+K+K+K+ @ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.8#=#=#=#=#=#8#n#n#n#n#n#M#R&R&o#v#b;*-7=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=i-q=q=q=q=q=y-y-G-G-G-G-G-!;c;d;(;(;e;!;';';';';", +"m m l l l l l l k k k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e e e e e d d d d d d d d c c c c c c c c c c c c b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b c c c c c c c c c c c c d d d d d d d d e e e e e e e e e e e e e e e e e e e e g g g g g g g g g g g g g g g g h h h i i j j j j j j j j k k k k k k l l l l l l m m m m m m m m n n n o o o o p p p p ];<$>$>$!$q q q q q q q r r s s s s s s s s t t t t u u u u v v v v v v v w w w x x x `#x#x#x#x#x#r&h#h#h#h#h#d#d#d#:#:#:#:#y B B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H %@}@*@*@Q+J c.K K K K L L M M M M M x+x+h+h+|+N N N O O P Q Q Q Q R R R <=4+4+5+5+5+5+6+,*V V V V V W X X X X X X Y Y Z =&>%,%O.-+-+;+;+;+O.O.O.O.O.N.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+~+{+{+]+]+]+]+b+;.;.;.;.;.>.,.'.'.'.'.).).0&J+J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@&#}.}.}.}.}.}.|.1.1.1.1.2.2.2.3.3.H@=#=#=#=#=#n#n#n#n#n#n#M#}=n-8.8.8.9.9.2*f;1=f=f=f=f=f=1=N=N=N=N=N=N=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-g;';';';';';';", +"m m m m l l l l l l k k k k k k j j j j j j j j i i h h h g g g g g g g g g g g g g g g g g e e e e e e e e e e e e e e e e e e e e e e d d d d d d d d d c c c c c c c c c c c c c c c c c c b b b b b b b b b b b b b b b b b b b b b b b c c c c c c c c c c c c c c c c c c d d d d d d d d d e e e e e e e e e e e e e e e e e e e e e e g g g g g g g g g g g g g g g g g h h h i i j j j j j j j j k k k k k k l l l l l l m m m m m m m m n n n n o o o o p p p p p ];>$>$>$!$q q q q q q r r s s s s s s s t t t t u u u u u v v v v v v v w w w x x x x `#x#x#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#y B B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I %@*@*@*@/&J K K K K L L M M M M M M x+h+h+h+|+N N O O P Q Q Q Q R R R S `.4+5+5+5+5+5+6+-%V V V V W X X X X X X Y Y Z Z Z Z ` ` (%F%M.;+>+O.O.O.O.P.N.N.N.N.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+{+]+]+]+]+]+Y+;.;.;.;.>.,.,.'.'.'.).).).`%J+J+K+K+K+K+K+ @ @.@.@.@.@{@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@B@}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.1.=#=#=#=#=#8#n#n#n#n#n#M#}=2*8.8.8.9.9.9.+=+=+=.-V=f=f=1=1=N=N=N=N=N=k=q=q=q=q=q=y-y-G-G-G-G-G-T-T-T-K-K-K-';';';';';';", +"m m m m m m l l l l l l k k k k k k j j j j j j j j j i i h h h g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e d d d d d d d d d d d c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c d d d d d d d d d d d e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g h h h i i j j j j j j j j j k k k k k k l l l l l l m m m m m m m m m n n n o o o o p p p p p p q ~$>$>$>${$q q q q r r s s s s s s s s t t t t u u u u u v v v v v v v w w x x x x x x `#x#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#y B B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I N+*@*@*@/&c.K K K L L L M M M M M M h+h+h+h+|+N N O P P Q Q Q R R R S S `.5+5+5+5+5+6+6+h$V V V W W X X X X X X Y Z Z Z Z ` ` ` ` . .L&F+-&O.O.P.N.N.N.N.N.N.,+,+,+,+,+'+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+_+;.;.;.>.,.,.'.'.'.'.).).).1&J+K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@A@A@A@A@A@A@z&}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.0#=#=#=#=#8#n#n#n#n#n#n#p-n-7.8.8.8.9.9.+=+=+=4=4=4=h=--h;N=N=N=N=N=N=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-';';';';';';i;", +"m m m m m m m m l l l l l l l k k k k k j j j j j j j j j i i i h h h g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e d d d d d d d d d d d d d d d d d d d c c c c c c c c c c c c c d d d d d d d d d d d d d d d d d d d e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g h h h i i i j j j j j j j j j k k k k k l l l l l l l m m m m m m m m m n n n o o o o p p p p p p h.q q |$>$>$>${$q q q r r s s s s s s s t t t t t u u u u v v v v v v v w w w x x x x x x x q#x#x#h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#:#L@B C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J N+*@*@*@Y%K K K L L L M M M M M M N h+h+h+h+|+N O P P Q Q Q R R R S S S P 5+5+5+5+6+6+%+V V V W W X X X X X X Y Y Z Z Z Z ` ` ` . ...+.+.+.` j;R.N.N.N.N.N.l+,+,+,+,+'+'+'+!+!+!+~+~+~+~+{+{+]+]+]+]+]+7+(+;.;.;.>.,.'.'.'.'.).).).!.9&J+K+K+K+K+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@7@m@m@m@z@z@A@A@A@A@A@W@J@}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.p==#=#=#=#8#n#n#n#n#n#M#y=7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=;-X=j-N=N=k=q=q=q=q=q=q=y-G-G-G-G-G-T-T-T-T-K-K-';';';';';';k;", +"n m m m m m m m m m l l l l l l l k k k k k k j j j j j j j j j i i i h h h g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g h h h i i i j j j j j j j j j k k k k k k l l l l l l l m m m m m m m m m n n n o o o o p p p p p p p q q q q |$>$>$>$l;q r r s s s s s s s s t t t t u u u u u v v v v v v v w w w x x x x x x x x q#r&h#h#h#h#h#h#d#d#d#:#:#:#:#:#:#:#:#m%C C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J N+*@*@|@R-K K L L L M M M M M M N N m;h+h+h+|+O P P Q Q Q R R R S S S S F.5+5+5+6+6+%+$+V V W W X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.^*I+Q.N.l+,+,+,+,+,+'+'+!+!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+0+;.;.>.,.,.'.'.'.'.).).).!.!.y*K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@A@B@}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.d==#=#=#8#n#n#n#n#n#n#0=7.7.8.8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=2-|-q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-';';';';';i;k;", +"n n n m m m m m m m m m l l l l l l l k k k k k k j j j j j j j j j j i i h h h h g g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g g h h h h i i j j j j j j j j j j k k k k k k l l l l l l l m m m m m m m m m n n n o o o o o p p p p p p q q q q q q |$>$>$>$_-r r s s s s s s s t t t t t u u u u u v v v v v v v w w x x x x x x x x x x y h#h#h#h#h#d#d#d#d#:#:#:#:#:#:#:#:#)#{*C C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J N+*@|@=@R-K L L L M M M M M M j.N N Y%h+h+h+|+P P Q Q Q Q R R R S S S T T 5+5+5+6+%+%+q$V V W X X X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.t$V.!+,+,+,+'+'+'+!+!+!+~+~+~+~+{+]+]+]+]+]+]+7+7+7+;.;.>.,.'.'.'.'.).).).!.!.~.r+K+K+K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@V*}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.e#=#=#=#8#n#n#n#n#n#M#f;7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-K=2-|-q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-';';';';';';k;k;", +"o o n n n m m m m m m m m m l l l l l l l l k k k k k k j j j j j j j j j j i i i h h h g g g g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g g g g h h h i i i j j j j j j j j j j k k k k k k l l l l l l l l m m m m m m m m m n n n o o o o o p p p p p p q q q q q q q q [$>$>$,$_-s s s s s s s s t t t t u u u u u v v v v v v v w w w x x x x x x x x x x y y h#h#h#h#d#d#d#d#:#:#:#:#:#:#:#:#)#)#Z@C C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J J N+|@=@=@s@L L L M M M M M M j.N N N Y%h+h+i+|+P Q Q Q Q R R R S S S T T T 5+5+6+%+%+%+%+V W r.X X X X X X Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.M&+*n;'+'+!+!+!+~+~+~+~+{+{+]+]+]+]+]+7+7+7+7+<+>.,.'.'.'.'.).).).).!.!.~.~.&%K+K+ @ @.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@A@H@}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.n#=#=#8#n#n#n#n#n#n#M#n-7.8.8.9.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-K=k-z-y-G-G-G-G-T-T-T-T-K-K-K-';';';';';i;k;k;", +"o o o o n n n m m m m m m m m m l l l l l l l l k k k k k k k j j j j j j j j j j i i i h h h h g g g g g g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g g g g g g h h h h i i i j j j j j j j j j j k k k k k k k l l l l l l l l m m m m m m m m m n n n o o o o o p p p p p p q q q q q q q q q q [$,$,$$$F#s s s s s s t t t t t u u u u u v v v v v v v w w w x x x x x x x x x x y y y :#h#h#d#d#d#:#:#:#:#:#:#:#:#:#)#)#)#Z@C D D D D E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J J J v@=@=@=@s@L L M M M M M M j.N N N N k+h+i+i+A$Q Q Q Q R R R S S S T T T T (&6+6+%+%+%+%+W W X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.u*S.!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+7+Y+,.,.'.'.'.'.).).).!.!.~.~.~.w@K+K+ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@7#}.}.}.}.|.1.1.1.1.f.2.2.3.3.3.4.0#=#=#=#n#n#n#n#n#n#M#}=7.8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-W-H-T-G-G-T-T-T-T-K-K-';';';';';';k;k;k;", +"p o o o o o n n n n m m m m m m m m m l l l l l l l l k k k k k k j j j j j j j j j j j i i i h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h i i i j j j j j j j j j j j k k k k k k l l l l l l l l m m m m m m m m m n n n n o o o o o p p p p p p h.q q q q q q q q q q r V#,$$$$$($s s s s s t t t t u u u u u v v v v v v v w w w x x x x x x x x x x a.y y y y :#h#d#d#d#:#:#:#:#:#:#:#:#:#)#)#)#)#c*D D D E E E E E E E b.F G G G G G G G i.H H H H H H H I I J J J J J c.K =@=@=@/&L M M M M M M j.N N N N N g+h+i+j+7&Q Q Q R R R S S S S T T T T T+6+%+%+%+%+&+T X X X X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.t$T.~+~+~+~+{+]+]+]+]+]+]+7+7+7+7+7+`+,.'.'.'.'.).).).!.!.!.~.~.~.~.R% @.@.@.@.@.@]@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@I@}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.d==#=#8#n#n#n#n#n#n#M#P=7.8.8.9.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-8;3;T-T-T-K-K-K-';';';';';i;k;k;k;", +"p p p o o o o o n n n n m m m m m m m m m l l l l l l l l k k k k k k k j j j j j j j j j j j j i i i h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g f e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e e f g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h i i i j j j j j j j j j j j j k k k k k k k l l l l l l l l m m m m m m m m m n n n n o o o o o p p p p p p h.q q q q q q q q q q q r r K*$$$$$$K*s s s t t t t t u u u u u v v v v v v v w w w x x x x x x x x x x y y y y y y B#d#d#d#:#:#:#:#:#:#:#:#:#)#)#)#)#.#'-D D E E E E E E E F F G G G G G G G i.H H H H H H H I I J J J J J c.K K R+=@R+/&M M M M M M j.N N N N N N g+i+j+j+1+Q Q R R R S S S S T T T T U #+%+%+%+%+%+&+V X X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.$.$.$.$.$.%.%.%.&.&.&.*.(*T.~+{+{+]+]+]+]+]+7+7+7+7+7+8+9+;.'.'.'.).).).).!.!.~.~.~.~.~.].G@.@.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@A@A@A@A@A@A@^#}.}.}.}.}.|.1.1.1.1.f.2.2.3.3.3.4.4.f#=#=#n#n#n#n#n#n#M#R&H=8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;e;o;!;K-K-';';';';';';i;k;k;k;", +"p p p p p o o o o o n n n n m m m m m m m m m m l l l l l l l l k k k k k k k k j j j j j j j j j j j j i i i h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h i i i j j j j j j j j j j j j k k k k k k k k l l l l l l l l m m m m m m m m m m n n n n o o o o o p p p p p p h.q q q q q q q q q q q r r s s F#$$$$$$Y-s t t t t t u u u u u v v v v v v v v w w w x x x x x x x x x x y y y y y y z p;d#d#:#:#:#:#:#:#:#:#:#)#)#)#)#.#.#C@D E E E E E E E F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K &&R+R+x+M M M M M M N N N N N N N 2+j+j+k+R$Q R R R S S S S T T T T T U #%%+%+%+%+&+y+E.X X X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.*.*.*.q;&*{+]+]+]+]+]+]+7+7+7+7+8+9+9+:+'.'.'.).).).!.!.~.~.~.~.~.{.].^.5@.@.@{@]@]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@^#}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.b;=#=#8#n#n#n#n#n#n#M#}*8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;r;c;k;';';';';i;k;k;k;k;", +"h.p p p p p p p o o o o o n n n m m m m m m m m m m l l l l l l l l l k k k k k k k k j j j j j j j j j j j j j i i i h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h h i i i j j j j j j j j j j j j j k k k k k k k k l l l l l l l l l m m m m m m m m m m n n n o o o o o p p p p p p p h.q q q q q q q q q q q r r r s s s {$$$$$$$V#t t t t t u u u u u v v v v v v v w w w x x x x x x x x x x a.y y y y y y z z g#:#:#:#:#:#:#:#:#:#)#)#)#)#)#.#.#.#C@E E E E E E E F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K Z*R+S+S+M M M M M N N N N N N N O C$j+k+k+x%R R R R S S S T T T T T U V ,*%+%+%+&+&+*+G.X X X X Y Z Z Z Z Z ` ` ` . . .+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.k.]+]+]+]+]+]+7+7+7+7+7+8+9+0+~@'.'.).).).!.!.!.~.~.~.~.{.].^.^.^.p+.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@O*z@A@A@A@A@A@B@}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.4.9#=#=#n#n#n#n#n#n#M#R&s;8.8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;t;t;u;!;';';i;k;k;k;k;", +"q q h.p p p p p p p o o o o o n n n n m m m m m m m m m m l l l l l l l l l k k k k k k k k j j j j j j j j j j j j j j i i i i h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h h i i i i j j j j j j j j j j j j j j k k k k k k k k l l l l l l l l l m m m m m m m m m m n n n n o o o o o p p p p p p p h.q q q q q q q q q q q r r r s s s s s {$$$$$+$K#t t t u u u u u v v v v v v v v w w w x x x x x x x x x x y y y y y y y z z z 4#:#:#:#:#:#:#:#:#)#)#)#)#)#.#.#.#.#M@E E E E E E F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K L <@S+S+S+c.M M M N N N N N N N O P R$k+k+k+f+R R R S S S T T T T T U V V H.%+%+&+&+*+*+I.X X X Y Y Z Z Z Z ` ` ` . . .+.+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.]+]+]+]+]+7+7+7+7+7+8+9+0+0+0+<+).).).).!.!.~.~.~.~.~.{.].^.^.^.^.x@p%]@]@]@]@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@7%}.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.4.f.n#=#8#n#n#n#n#n#n#R&R&b;8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;e;!;i;k;k;k;k;k;", +"q q q q h.p p p p p p p o o o o o n n n n m m m m m m m m m m m l l l l l l l l l k k k k k k k k k j j j j j j j j j j j j j j j i i i i h h h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h h h h i i i i j j j j j j j j j j j j j j j k k k k k k k k k l l l l l l l l l m m m m m m m m m m m n n n n o o o o o p p p p p p p h.q q q q q q q q q q q r r r s s s s s s s U-+$+$+$Y#t u u u u u u v v v v v v v w w w x x x x x x x x x x x y y y y y y z z z z A y#:#:#:#:#:#:#:#)#)#)#)#)#.#.#.#.#N@d@E E E E b.F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K L L <*S+S+S+c.M M N N N N N N N O P P |+k+k+k+=%R R S S S T T T T T U U V V -%%+&+&+*+*+*+#%X X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.Y+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+~@).).).!.!.~.~.~.~.~.{.].^.^.^.^.e./.].p+]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@B@a@[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.4./==#=#n#n#n#n#n#n#M#R&R&5.8.8.9.9.+=+=+=+=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;w;i;k;k;k;k;k;", +"q q q q q q h.p p p p p p p o o o o o o n n n n m m m m m m m m m m m l l l l l l l l l l k k k k k k k k k j j j j j j j j j j j j j j j j i i i i i i h h h h h h h h h g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g h h h h h h h h h i i i i i i j j j j j j j j j j j j j j j j k k k k k k k k k l l l l l l l l l l m m m m m m m m m m m n n n n o o o o o o p p p p p p p h.q q q q q q q q q q q r r r s s s s s s s s s X#+$+$+$+$u u u u u v v v v v v v v w w w x x x x x x x x x x a.y y y y y y z z z z A A 5&:#:#:#:#:#:#)#)#)#)#.#.#.#.#.#N@N@u%E E E b.F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K L L M f+S+S+S+t&M N N N N N N N O P P Q K%k+k+3+3+R S S S T T T T T U U V V V h$&+&+*+*+*+*+*+X Y Y Z Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.&.*.*.*.*.=.-.8+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+c+).!.!.!.~.~.~.~.{.].].^.^.^.^.e./././.].^@7@6@7@7@7@m@m@m@m@z@A@A@%#a@[.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.2.3.3.4.4.f.n#=#8#n#n#n#n#n#M#R&R&f#8.8.9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;3;k;k;k;k;k;", +"q q q q q q q q q p p p p p p p p o o o o o n n n n n m m m m m m m m m m m l l l l l l l l l l k k k k k k k k k k j j j j j j j j j j j j j j j j j j j i i i i i i i h h h h h h h h h h h h h h h h h h g g g g g g g g g h h h h h h h h h h h h h h h h h h i i i i i i i j j j j j j j j j j j j j j j j j j j k k k k k k k k k k l l l l l l l l l l m m m m m m m m m m m n n n n n o o o o o p p p p p p p p q q q q q q q q q q q q r r r s s s s s s s s s t t N#+$+$W#W#t u u v v v v v v v v w w w x x x x x x x x x x x y y y y y y y z z z A A A B (#:#:#:#:#)#)#)#)#)#.#.#.#.#N@N@N@N@6#E E b.F G G G G G G G G i.H H H H H H H I I J J J J J c.K K K K L L L M v+S+S+S+u+N N N N N N N O O P Q Q z$k+3+3+3+P S S S T T T T U U V V V V V *&*+*+*+*+*+*+;%Y Z Z Z Z ` ` ` ` . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.Z+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+a;'=!.!.~.~.~.~.~.{.].^.^.^.^.e./././././.(.(.N*U@9@&#&#&# @_=a@[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.4.9#=#8#n#n#n#n#n#n#M#R&R&f#8.8.9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;y;k;k;k;k;k;", +"q q q q q q q q q q q p p p p p p p p o o o o o o n n n n m m m m m m m m m m m m l l l l l l l l l l l k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j i i i i i i i i i i i i h h h h h h h h h h h h h h h h h i i i i i i i i i i i i j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k l l l l l l l l l l l m m m m m m m m m m m m n n n n o o o o o o p p p p p p p p q q q q q q q q q q q q r r r s s s s s s s s s t t t t I#W#W#W#W#s u v v v v v v v v w w w x x x x x x x x x x a.y y y y y y z z z z A A B B B H#:#:#:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@r@E b.F G G G G G G G G H H H H H H H H I I J J J J J c.K K K K L L L M M K&S+S+S+1+N N N N N N O O P Q Q Q N 3+3+3+4+<=S S T T T T U U V V V V V W z;*+*+*+*+*+*+/;Z Z Z Z Z ` ` ` . . .+.+.+.+.+.@.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.H$]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+0+m+m+m+)=!.~.~.~.~.~.{.].^.^.^.^.^./././././.(.(.(._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.f.2.2.3.3.3.4.4.0#=#=#n#n#n#n#n#n#M#R&R&f#f#j=9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;#;k;k;k;k;k;", +"r q q q q q q q q q q q q h.p p p p p p p p o o o o o o n n n n m m m m m m m m m m m m m l l l l l l l l l l l l k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k l l l l l l l l l l l l m m m m m m m m m m m m m n n n n o o o o o o p p p p p p p p h.q q q q q q q q q q q q r r r s s s s s s s s s t t t t t u j&W#W#W#W#j&v v v v v v v w w w x x x x x x x x x x x y y y y y y y z z z z A A B B B B k&:#:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@N@O@F F G G G G G G G G H H H H H H H H I I J J J J J c.K K K K L L L M M M |%S+S+h+x%N N N N N O O P Q Q Q Q R 3+3+4+4+ %S T T T T T U V V V V V W W I.*+*+*+*+*+*+~&Z Z Z Z ` ` ` . . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.*.*.*.*.=.=.-.8+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+J+J+1&~.~.~.~.{.].].^.^.^.^.e././././.(.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.f.2.2.2.3.3.4.4.4.f#=#8#n#n#n#n#n#n#M#R&R&f#f#b;9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-l-l-l-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;B;k;k;k;k;C;", +"r r r q q q q q q q q q q q q q p p p p p p p p o o o o o o n n n n n m m m m m m m m m m m m m l l l l l l l l l l l l l k k k k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k k k k l l l l l l l l l l l l l m m m m m m m m m m m m m n n n n n o o o o o o p p p p p p p p q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u s W#W#K#K#E#v v v v v w w w x x x x x x x x x x x x y y y y y y z z z z A A A B B B B B Z@:#)#)#)#)#)#.#.#.#.#N@N@N@N@N@N@N@ #E G G G G G G G G H H H H H H H H I I J J J J J c.K K K K L L L M M M M t&S+x+h+f+N N N N O O P Q Q Q Q R R D;4+4+4+E;T T T T T U V V V V V W W X G.*+*+*+*+*+*+K.Z Z Z ` ` ` ` . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.=.-.`+]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+m+J+J+K+w@~.~.~.{.].^.^.^.^.e./././././.(.(._._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.4.4.e#=#=#n#n#n#n#n#n#M#R&R&f#f#f#a=9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;F;k;k;k;k;C;", +"s s r r r q q q q q q q q q q q q q p p p p p p p p p o o o o o o n n n n n m m m m m m m m m m m m m m l l l l l l l l l l l l l l k k k k k k k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k k k k k k k l l l l l l l l l l l l l l m m m m m m m m m m m m m m n n n n n o o o o o o p p p p p p p p p q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u u u u K#K#K#K#I#v v v v w w w x x x x x x x x x x x y y y y y y y z z z z A A B B B B B B B t%)#)#)#)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@v%G G G G G G i.H H H H H H H I I I J J J J J c.K K K K L L M M M M M M c.x+h+h+g+N N N O O P Q Q Q Q R R R R$4+4+4+V&T T T T U V V V V V W W X X V *+*+*+*+*+=+-+=&Z ` ` ` ` . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.%%]+]+]+]+7+7+7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+K+K+&%~.~.{.].^.^.^.^.^./././././.(.(.(._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.2.3.3.4.4.2#=#=#8#n#n#n#n#n#n#M#R&f#f#f#0=0=j=+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;G;k;k;k;k;C;C;", +"s s s s r r r q q q q q q q q q q q q q q p p p p p p p p p o o o o o o o n n n n n m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l k k k k k k k k k k k k k k k k k k k k k k k j j j j j j j j j j j j j j j j j j j j j j j k k k k k k k k k k k k k k k k k k k k k k k l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m n n n n n o o o o o o o p p p p p p p p p q q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u u u u u v O#K#K#K#%$v v w w w x x x x x x x x x x x x y y y y y y z z z z A A A B B B B B B B C C .#)#)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@r@b@G G G G G i.H H H H H H H I I I J J J J J c.K K K K L L M M M M M M M j.m;h+h+Y%N N O O P P Q Q Q R R R R A$4+4+5+*&T T T U U V V V V W W X X X X *+*+*+*+=+=+=+E+` ` ` ` . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.H$]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+J+K+K+K+)={.].].^.^.^.^.e./././././.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.4.**S==#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=7=7.+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;H;k;k;k;k;C;I;", +"s s s s s s r r r q q q q q q q q q q q q q q h.p p p p p p p p p o o o o o o o n n n n n m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m n n n n n o o o o o o o p p p p p p p p p h.q q q q q q q q q q q q q q r r r s s s s s s s s s t t t t t t u u u u u u v v v J#K#K#K#X#w w w w x x x x x x x x x x x y y y y y y y z z z z A A A B B B B B B B C C C #)#.#.#.#.#N@N@N@N@N@N@N@O@ #r@r@r@q@G G G G i.H H H H H H H I I I J J J J J c.K K K K L L M M M M M M M j.N g+h+h+h+N O O P P Q Q Q R R R R S %4+5+5+5+T T U U V V V V W W X X X X X #%*+*+=+=+=+=+B+` ` ` . . .+.+.+.+.+.@.#.#.#.#.#.#.$.$.$.$.$.$.%.%.&.&.&.*.*.*.*.*.=.[+5=]+]+]+]+7+7+7+7+7+8+9+0+0+0+0+m+m+m+m+J+J+K+K+K+K+K+a-].^.^.^.^.e./././././.(.(._._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.f.2.2.3.3.3.4.p=S==#=#8#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=1=e=h=4=4=4=4=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;A;J;k;k;k;C;C;I;", +"s s s s s s s s s r r r q q q q q q q q q q q q q q p p p p p p p p p p o o o o o o o n n n n n n m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l l l l l k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k l l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m m m n n n n n n o o o o o o o p p p p p p p p p p q q q q q q q q q q q q q q r r r s s s s s s s s s s t t t t t t u u u u u u v v v v v C&K#K#K#;;w w x x x x x x x x x x x a.y y y y y y y z z z A A A B B B B B B B C C C C C D@.#.#.#.#N@N@N@N@N@N@N@ # #r@r@r@r@~#G G G i.H H H H H H H I I I J J J J J c.K K K K L L M M M M M M M j.N N f+h+h+h+g@O P P Q Q Q R R R R S S `.5+5+5+5+F.U U V V V V V W X X X X X X I.*+=+=+=+=+=+L.` ` . . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.=.t*8+]+]+]+]+7+7+7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+K+K+K+K+K+K+ @p+^.^.^.^./././././.(.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.4.p=n#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=}=f=N=8=--;-D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;x;A;A;K;k;k;k;k;C;I;I;", +"t s s s s s s s s s s r r r q q q q q q q q q q q q q q q p p p p p p p p p p o o o o o o o o n n n n n n m m m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m m m m m n n n n n n o o o o o o o o p p p p p p p p p p q q q q q q q q q q q q q q q r r r s s s s s s s s s s t t t t t t u u u u u u v v v v v v v )$K#G#G#C&x x x x x x x x x x x x y y y y y y y z z z z A A A B B B B B B B C C C C C D `@.#.#N@N@N@N@N@N@N@N@ #r@r@r@r@r@r@M@G G H H H H H H H H I I I J J J J J c.K K K K L L M M M M M M M N N N N x%h+h+h+|+P P Q Q Q R R R R S S S T 5+5+5+5+H.U V V V V V W r.X X X X X X i$*+=+=+=+=+-+-+Q% . . ...+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=./+]+]+]+]+]+7+7+7+7+8+9+9+0+0+0+0+m+m+m+J+J+J+K+K+K+K+K+ @ @.@p+^.^.e./././././.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.3.3.t#S==#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=1=j-$-t-;-K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;A;d;k;k;k;C;C;I;I;", +"t t t s s s s s s s s s s r r r r q q q q q q q q q q q q q q q p p p p p p p p p p p o o o o o o o o n n n n n n n m m m m m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m m m m m m m n n n n n n n o o o o o o o o p p p p p p p p p p p q q q q q q q q q q q q q q q r r r r s s s s s s s s s s t t t t t t u u u u u u v v v v v v v v v U#G#G#x#L#x x x x x x x x x x a.y y y y y y z z z z A A A B B B B B B B C C C C C C D D C@.#N@N@N@N@N@N@N@O@ #r@r@r@r@r@r@r@W%G H H H H H H H H I I I J J J J J c.K K K L L L M M M M M M M N N N N N |+h+h+i+R$P Q Q Q R R R R S S S T T &+5+5+6+y&V V V V V W r.X X X X X X Y Y -+=+=+=+-+-+-+G+ . ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.=.u*]+]+]+]+]+7+7+7+7+7+8+a+_+_+~@0+m+m+m+m+J+J+K+K+K+K+K+K+ @.@.@.@y*H%/././././.(.(._._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.}.|.1.1.1.1.f.2.2.2.3.3.9#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=}=}=}=}=f=f=f=f=1=1=N=N=N=N=N=|-k-L;(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;x;A;A;A;M;k;k;k;C;I;I;I;", +"t t t t t s s s s s s s s s s s r r r q q q q q q q q q q q q q q q q p p p p p p p p p p p o o o o o o o o o n n n n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m m l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l l m m m m m m m m m m m m m m m m m m m m m m m m m m m m m n n n n n n n o o o o o o o o o p p p p p p p p p p p q q q q q q q q q q q q q q q q r r r s s s s s s s s s s s t t t t t t u u u u u u v v v v v v v v v w w O-G#x#x#x#x x x x x x x x x y y y y y y y z z z z A A A B B B B B B B C C C C C D D D D '-N@N@N@N@N@N@N@O@ #r@r@r@r@r@r@r@r@e@H H H H H H H H I I J J J J J c.K K K K L L L M M M M M M M N N N N N N g@h+i+j+C$Q Q Q Q R R R S S S T T T V&5+6+6+#+V V V V W W X X X X X X Y Y Z K.=+=+-+-+-+-+r$ ...+.+.+.+.+.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.*.=.X+]+]+]+]+]+7+7+7+7+7+`+)%'.'.'.'.'=5@m+m+J+J+K+K+K+K+K+K+ @.@.@.@.@.@y@/@/././.(.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.2.2.3.;#]==#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=N=q=q=q=q=|-2;N;l-w-w-w-w-w-&;&;&;&;(;(;t;t;v;v;v;v;x;A;A;t;!;k;k;C;C;I;I;I;", +"u t t t t t t s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q h.p p p p p p p p p p p o o o o o o o o o o n n n n n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m n n n n n n n n o o o o o o o o o o p p p p p p p p p p p h.q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s t t t t t t u u u u u u v v v v v v v v v w w w w C#x#x#x#x#`#x x x x x x y y y y y y y z z z z A A A B B B B B B B B C C C C C D D D D E m%N@N@N@N@N@N@ #r@r@r@r@r@r@r@r@r@.&e@_@H H H H H I I I J J J J J c.K K K K L L L M M M M M M M N N N N N N N O i+j+j+=%Q Q Q R R R S S S T T T T +6+6+%+%+V V V W W X X X X X X Y Y Z Z p*=+-+-+-+-+;+>+L&+.+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.&.&.&.&.*.*.*.*.=.Y+]+]+]+]+]+7+7+7+7+7+`+,.,.'.'.'.'.).).'=6%J+J+K+K+K+K+K+ @ @.@.@.@.@{@]@]@o=]./.(.(._._._.:.:.:.:.<.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.1.f.2.2.3.E&H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&f#f#f#0=0=0=}=}=}=f=f=f=f=f=1=N=N=N=N=N=N=k=q=q=q=q=q=q=y-y-.;k-8;9;&;&;&;&;(;(;t;t;t;v;v;v;x;A;A;A;O;k;k;k;C;I;I;I;I;", +"u u u u t t t t t t s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q p p p p p p p p p p p p p o o o o o o o o o o n n n n n n n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m n n n n n n n n n n o o o o o o o o o o p p p p p p p p p p p p p q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s t t t t t t u u u u u u u v v v v v v v v v w w w w x x 3#x#x#x#x#Z&x x x x a.y y y y y y y z z z z A A A B B B B B B B C C C C C D D D D D E E }#N@N@N@N@O@ #r@r@r@r@r@r@r@r@r@.&e@e@:@H H H H I I I J J J J J c.K K K K L L L M M M M M M M N N N N N N N O O -@j+k+k+Q Q R R R S S S T T T T T E*6+%+%+%+E.V W W X X X X X X Y Y Z Z Z (%-+-+-+-+;+;+;+(%+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.=.I=]+]+]+]+]+]+7+7+7+7+P;Q;,.'.'.'.'.).).).!.!.6%K+K+K+K+K+K+ @.@.@.@.@{@]@]@]@]@p%#@!%_._._._.:.:.:.l.<.[.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.f.2.2.J@!=H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=1=1=N=N=N=N=N=k=q=q=q=q=q=q=y-y-G-G-G-G-G-R;M;u;S;(;(;t;t;v;v;v;v;A;A;A;T;!;k;k;C;C;I;I;I;I;", +"u u u u u u t t t t t t s s s s s s s s s s s s r r r q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p o o o o o o o o o o o o n n n n n n n n n n n n m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m n n n n n n n n n n n n o o o o o o o o o o o o p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q r r r s s s s s s s s s s s s t t t t t t u u u u u u u v v v v v v v v v w w w w x x x x q#x#x#x#x# ;x x x y y y y y y y z z z z A A A B B B B B B B B C C C C C D D D D D E E E E #N@N@O@ #r@r@r@r@r@r@r@r@r@e@e@e@e@(=H H H I I I J J J J J c.K K K K L L L M M M M M M M N N N N N N N O O P C$k+k+k+z$R R R S S S T T T T T U U %+%+%+%+I.W W X X X X X X Y Y Z Z Z Z Z O.-+-+;+;+;+O.F$+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.%%]+]+]+]+]+]+7+7+7+7+0+-.,.'.'.'.'.).).).).!.!.~.&%K+K+K+K+ @.@.@.@.@.@]@]@]@]@]@6@6@7@#@(@_.:.:.:.:.<.<.[.[.[.[.[.}.}.}.}.}.}.|.|.1.1.1.1.2.J@-;H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0=}=}=}=f=f=f=f=f=1=N=N=N=N=N=N=i-q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-U;[;d;G;v;v;v;x;A;A;5;V;k;k;k;C;I;I;I;I;I;", +"v u u u u u u u t t t t t t t s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p o o o o o o o o o o o o o o n n n n n n n n n n n n n n n n n n n m m m m m m m m m m m m m m m m m m m n n n n n n n n n n n n n n n n n n n o o o o o o o o o o o o o o p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s t t t t t t t u u u u u u u v v v v v v v v v w w w w x x x x x x x L#x#x#x#%$x y y y y y y y z z z z z A A A B B B B B B B C C C C C C D D D D E E E E E E !#N@ # #r@r@r@r@r@r@r@r@.&e@e@e@e@e@<@H H I I I J J J J J c.K K K K L L L M M M M M M M N N N N N N N O O P P |+k+k+k+2@R R S S S S T T T T T U V #+%+%+%+++W X X X X X X Y Y Z Z Z Z Z ` F$-+;+;+;+>+O.6-+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.=.Z.]+]+]+]+]+]+7+7+7+7+9+-.,.'.'.'.'.'.).).).!.!.~.~.~.&%K+K+ @.@.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@B@P*:.:.<.<.[.[.[.[.[.[.}.}.}.}.}.|.|.1.1.1.**9#=#X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=f=}=}=}=}=f=f=f=f=1=1=N=N=N=N=N=k=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-';';';!;@;N;W;H;M;k;k;k;k;C;C;I;I;I;I;I;", +"v v v u u u u u u u t t t t t t t s s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q q q p p p p p p p p p p p p p p p p o o o o o o o o o o o o o o o o o o n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n n o o o o o o o o o o o o o o o o o o p p p p p p p p p p p p p p p p q q q q q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s s t t t t t t t u u u u u u u v v v v v v v v v w w w w x x x x x x x x x c#x#x#x#g#y y y y y y y z z z z A A A B B B B B B B B C C C C C D D D D D E E E E E E E X; #r@r@r@r@r@r@r@r@r@.&e@e@e@e@e@e@d@H I I J J J J J J c.K K K K L L L M M M M M M j.N N N N N N N O O P P Q z$k+3+3+B$R S S S S T T T T T U V V U+%+%+&+*&X X X X X X Y Y Z Z Z Z Z ` ` (%;+;+;+>+O.O.O.(%@.#.#.#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.*.H$~-]+]+]+]+]+7+7+7+7+K$[+,.,.'.'.'.'.).).).!.!.!.~.~.~.~.&% @ @.@.@.@.@{@]@]@]@]@]@6@6@7@7@7@7@m@m@Q*{#e-`*[.[.[.[.[.}.}.}.}.}.}.|.%=6=l#=#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#t#6.9.9.+=+=O=O=.-X=f;h;1=N=N=N=N=N=N=q=q=q=q=q=q=y-y-G-G-G-G-G-T-T-T-T-K-K-';';';';';';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;", +"v v v v v v u u u u u u u t t t t t t t s s s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p p p p p p o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o p p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s s s t t t t t t t u u u u u u u v v v v v v v v v v w w w w x x x x x x x x x x x B#x#h#h#s#y y y y y z z z z A A A B B B B B B B B C C C C C C D D D D E E E E E E E E b.M%r@r@r@r@r@r@r@r@r@e@e@e@e@e@e@e@e@*@I I J J J J J c.c.K K K K L L M M M M M M M j.N N N N N N N O O P Q Q Q Q 3+3+3+=%S S S S T T T T T U V V V G.%+&+&+*+i$X X X X X Y Z Z Z Z Z ` ` ` ` P.;+>+O.O.O.O.-&#.#.#.#.#.#.d.$.$.$.$.$.$.%.%.&.&.&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+[+,.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.L+.@.@.@.@{@]@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@|#B@%#z#z#7%z#z#B@N-]#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&f#f#{&8.9.9.+=+=+=4=4=4=4=4=D=K=;-q-.;G-N=k=q=q=q=q=q=q=y-y-G-G-G-G-T-T-T-T-K-K-K-';';';';';';k;k;k;k;k;k;k;C;C;I;I;I;I;I;Y;", +"v v v v v v v v u u u u u u u t t t t t t t t s s s s s s s s s s s s s r r r r q q q q q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p p p p p p p p p p o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o p p p p p p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q q q r r r r s s s s s s s s s s s s s t t t t t t t t u u u u u u u v v v v v v v v v v w w w w x x x x x x x x x x x x x Z;h#h#h#:#y y y z z z z z A A A B B B B B B B B C C C C C D D D D D E E E E E E E E F G }#r@r@r@r@r@r@r@.&e@e@e@e@e@e@e@e@}@}@S@J J J J J c.K K K K L L L M M M M M M M j.N N N N N N N O O P Q Q Q Q R 2+3+3+4+P S S T T T T T U V V V V V &+&+*+*+H.X X X X Y Y Z Z Z Z ` ` ` ` .F%>+O.O.O.O.O.N...#.#.#.#.#.d.$.$.$.$.$.%.%.k.&.&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+'@>.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].^.y*.@.@.@{@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@W@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f# -9.9.9.+=+=+=4=4=4=4=D=D=K=K=K=K=K=T=;-2;|-q=q=q=y-y-G-G-G-G-G-T-T-T-T-K-K-';';';';';';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;`;", +"v v v v v v v v v v v u u u u u u u t t t t t t t s s s s s s s s s s s s s s r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r s s s s s s s s s s s s s s t t t t t t t u u u u u u u v v v v v v v v v v v w w w w x x x x x x x x x x x x x a.y 5&h#h#h#h#a#y z z z z A A A B B B B B B B B C C C C C C D D D D E E E E E E E E b.F G G E r@r@r@r@r@r@.&e@e@e@e@e@e@e@e@}@}@}@%&J J J J c.K K K K L L L M M M M M M M j.N N N N N N N O O P Q Q Q Q R R ;@3+4+4+>@T T T T T T U V V V V V W V&*+*+*+U+X X X Y Y Z Z Z Z ` ` ` ` . .L&O.O.O.O.O.P.N.I+#.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.H$T.]+]+]+]+]+]+7+7+7+7+:+>.,.'.'.'.'.'.).).).!.!.~.~.~.~.~.{.].].^.~.$=.@{@]@]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#8=9.9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-T=q- >T-G-G-G-G-T-T-T-T-K-K-K-';';';';';';k;k;k;k;k;k;k;C;C;I;I;I;I;I;.>+>", +"w w v v v v v v v v v v v u u u u u u u u t t t t t t t s s s s s s s s s s s s s s s r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q h.p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p h.q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r s s s s s s s s s s s s s s s t t t t t t t u u u u u u u u v v v v v v v v v v v w w w w x x x x x x x x x x x x x x y y y H#h#h#h#h#`#z z z A A A B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G G G G 6#r@r@r@r@e@e@e@e@e@e@e@e@e@}@}@}@*@A-J J J c.K K K K L L L M M M M M M M j.N N N N N N N O P P Q Q Q Q R R R @>4+4+4+++T T T T T U V V V V V W W H.*+*+*+*+;%X Y Y Z Z Z Z ` ` ` ` . ...+.-&O.O.O.P.N.N.Q.#.#.#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.k.u$]+]+]+]+]+]+7+7+7+7+_+>.,.,.'.'.'.'.).).).!.!.!.~.~.~.~.~.{.].^.^.^.r+{@]@]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#8=9.+=+=+=+=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-9;#>>-T-T-T-T-T-K-K-';';';';';';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;Y;$>", +"x w w w w v v v v v v v v v v v u u u u u u u u t t t t t t t t s s s s s s s s s s s s s s s r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q h.h.p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p h.h.q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u v v v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y a#h#h#d#d#s&z A A A A B B B B B B B B C C C C C D D D D D E E E E E E E E b.F G G G G G G }-r@r@.&e@e@e@e@e@e@e@e@}@}@}@*@*@*@Q+J J c.K K K K L L L M M M M M M M N N N N N N N N O P P Q Q Q Q R R R R P 4+4+5+&+T T T T U V V V V V W W X i$*+*+*+*+-%Y Y Z Z Z Z ` ` ` ` . . .+.+.F+O.O.P.N.N.N.N.$%#.d.$.$.$.$.$.%.%.%.&.&.&.*.*.*.*.%.u$]+]+]+]+]+]+7+7+7+7+(+>.,.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].^.^.^.^.^.L+]@]@]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@W@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=8=9.+=+=+=4=4=4=4=4=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;*;c;Q-K-K-K-';';';';';';k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>%>&>", +"x x x w w w w v v v v v v v v v v v u u u u u u u u u t t t t t t t t s s s s s s s s s s s s s s s s r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r s s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u u v v v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y y y y c#d#d#d#r#A A A B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G G G G G G G ~#r@.&e@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*=c.K K K K K L L L M M M M M M M N N N N N N N N O P P Q Q Q Q R R R R S S C$5+5+5+F.T T U V V V V V W W X X X .=*+*+*+w*Y Z Z Z Z ` ` ` ` . . .+.+.+.+.6-P.N.N.N.N.N.Q.d.$.$.$.$.$.%.%.%.&.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+`+;.>.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].^.^.^.^.^.e.*%]@]@]@]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=*>+=+=+=4=4=4=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;(;S;=>[;k;';';';';i;k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>->&>", +"x x x x x w w w w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t s s s s s s s s s s s s s s s s s r r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r s s s s s s s s s s s s s s s s s t t t t t t t t u u u u u u u u u v v v v v v v v v v v v w w w w x x x x x x x x x x x x x x x y y y y y y y y z z p;d#d#:#4#A B B B B B B B B C C C C C C D D D D D E E E E E E E E b.F G G G G G G G G G m%e@e@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*@|@%@K K K L L L M M M M M M M j.N N N N N N N N O P P Q Q Q Q R R R R S S S ++5+5+5+,*T U V V V V V W W X X X X I.*+*+*+;>Z Z Z Z ` ` ` ` . . .+.+.+.+.+.U*N.N.N.N.N.N.l+G$$.$.$.$.$.%.%.&.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+~@;.>.,.'.'.'.'.'.).).).!.!.~.~.~.~.~.{.].].^.^.^.^.e./.F@]@]@]@6@6@7@7@7@7@m@m@m@m@z@z@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#=#8#n#n#n#n#n#n#M#R&R&f#f#f#0=0=>>+=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;,>N;H-U;k;k;k;k;k;k;k;C;I;I;I;I;I;I;.>'>&>)>", +"x x x x x x x x w w w w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t t s s s s s s s s s s s s s s s s s s r r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r s s s s s s s s s s s s s s s s s s t t t t t t t t t u u u u u u u u u v v v v v v v v v v v v w w w w x x x x x x x x x x x x x x x a.y y y y y y y y z z z z b#:#:#:#s#B B B B B B B B C C C C C C D D D D D E E E E E E E E F F G G G G G G G G G i._@e@e@e@e@e@e@e@}@}@}@*@*@*@*@*@*@|@=@X%K K L L L M M M M M M M j.N N N N N N N N O P P Q Q Q Q R R R S S S S T I*5+5+5+@+U V V V V V W W X X X X X h$*+*+*+=+E+Z Z ` ` ` ` . . ...+.+.+.+.@.^*N.N.N.N.N.l+,+!+$.$.$.$.%.%.k.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+0+,@>.,.,.'.'.'.'.).).).!.!.!.~.~.~.~.~.{.].^.^.^.^.e././.].]@]@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@W@*#]#]#]#]#]#X@X@X@H@H@=#=#=#=#=#=#n#n#n#n#n#n#M#R&R&f#f#f#0=0=0={&+=+=4=4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;5;#;!;k;k;k;C;C;I;I;I;I;I;.>.>!>&>)>", +"x x x x x x x x x x w w w w w v v v v v v v v v v v v u u u u u u u u u t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s r r r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r r s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t u u u u u u u u u v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x a.y y y y y y y y z z z z z A L%:#:#:#:#y B B B B B C C C C C C D D D D D E E E E E E E E b.F G G G G G G G G G G i.H H W%e@e@e@e@e@}@}@}@*@*@*@*@*@|@|@=@=@P+K L L L M M M M M M M j.N N N N N N N O O P P Q Q Q Q R R R S S S S T T T 5+5+6+F=V V V V V W W X X X X X X X .=*+=+=+,%Z ` ` ` ` . . ...+.+.+.+.+.#.#.R.N.N.N.N.,+,+,+);$.$.%.%.%.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+0+,@>.,.,.'.'.'.'.).).).!.!.!.~.~.~.~.~.{.].^.^.^.^.^./././.].]@6@6@6@7@7@7@m@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#=#=#=#f#|=o#o#u#R&n#n#M#R&f#f#f#f#0=0=}=}=+=+=4=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;A;A;A;0;k;k;k;C;I;I;I;I;I;I;.>~>&>)>)>", +"x x x x x x x x x x x x x w w w w v v v v v v v v v v v v v u u u u u u u u u u t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s r r r r r r r r r r r q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q q r r r r r r r r r r r s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t u u u u u u u u u u v v v v v v v v v v v v v w w w w x x x x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A a#:#:#:#:#a#B B B C C C C C C D D D D D E E E E E E E E E F F G G G G G G G G G i.H H H H Q+e@e@e@e@}@}@}@*@*@*@*@*@|@=@=@=@=@&&L L L M M M M M M M j.N N N N N N N O O P P Q Q Q Q R R R S S S S T T T T V&6+6+%+-%V V V W W X X X X X X X Y ~&=+=+=+-+` ` ` ` . . ...+.+.+.+.+.#.#.#.L&N.N.N.l+,+,+,+,+Z%%.%.%.&.&.&.*.*.*.%.u${+]+]+]+]+]+]+7+7+7+K$c+>.,.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].^.^.^.^.^.e./././.z*]@6@6@7@7@7@7@m@m@m@O*z@A@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@X@H@H@H@=#f#/=**f.m.5.5.5.5.5.J-f=R&R&f#f#f#0=0=0=}=}=2*4=4=4=4=D=D=K=K=K=K=K=K=c-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;A;A;A;V;k;C;C;I;I;I;I;I;.>.>{>&>)>)>", +"x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v u u u u u u u u u u t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s r r r r r r r r r r r r r r r r r q q q q q q q q q q q q q q q q q q q r r r r r r r r r r r r r r r r r s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t u u u u u u u u u u v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A B B y :#:#:#:#(#B C C C C C C D D D D D D E E E E E E E E b.F G G G G G G G G G G i.H H H H H @#e@e@}@}@}@*@*@*@*@*@*@|@=@=@=@=@R+R+J M M M M M M M M N N N N N N N N O O P Q Q Q Q Q R R R S S S S T T T T T ,*6+%+%+U+V V W W X X X X X X X Y Y j$=+=+=+=+E+` ` . . ...+.+.+.+.+.#.#.#.#.#.R.N.l+,+,+,+,+,+T.%.%.&.&.&.*.*.*.%.T.{+]+]+]+]+]+]+]+7+7+7+<+>.>.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].].^.^.^.^.e././././.G@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@*#*#]#]#]#]#]#X@X@X@8#/=p=f.4.4.4.4.5.5.5.5.5.5.6.7.P=f#f#f#0=0=0=}=}=}=.-4=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;A;A;A;A;A;J;k;C;I;I;I;I;I;I;.>Y;$>)>)>]>", +"y x x x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v u u u u u u u u u u t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s r r r r r r r r r r r r r r r r r r r r r r r r r r r r r s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t u u u u u u u u u u v v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x x x y y y y y y y y y z z z z z A A A B B B B B s#:#:#:#u%C C C C C C D D D D D E E E E E E E E b.F F G G G G G G G G G i.H H H H H H H ##e@}@}@}@*@*@*@*@*@*@|@=@=@=@=@R+R+R+K&M M M M M M j.N N N N N N N N O P P Q Q Q Q R R R R S S S S T T T T T U >;%+%+%+q$V W W X X X X X X X Y Y Z Z O.=+=+-+L.` . . ...+.+.+.+.+.#.#.#.#.#.#.;&l+,+,+,+,+,+'+'+U.&.&.&.*.*.*.:%T.{+{+]+]+]+]+]+]+7+7+7+u&;.>.,.'.'.'.'.'.).).).!.!.!.~.~.~.~.~.].].^.^.^.^.e././././.#@6@6@6@7@7@7@m@m@m@m@z@z@A@A@A@A@A@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@^>k#3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.o#f#f#0=0=}=}=}=}=X=4=4=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;A;A;A;A;V;C;C;I;I;I;I;I;.>.>/>&>)>)>]>", +"y y y a.x x x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v u u u u u u u u u u u u t t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t u u u u u u u u u u u u v v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x x x a.y y y y y y y y y z z z z z A A A B B B B B B B b#:#:#)#_#C C C C D D D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H H H H H %@}@}@}@*@*@*@*@*@|@=@=@=@=@=@R+R+R+S+<*M M M M M j.N N N N N N N N O P P Q Q Q Q R R R R S S S S T T T T T U U V #+%+%+%+V+W X X X X X X X Y Y Z Z Z D+=+-+-+3%`& . ...+.+.+.+.+.@.#.#.#.#.#.#.d.e%,+,+,+,+'+'+'+!+V.&.&.*.*.:%(>{+{+]+]+]+]+]+]+7+7+7+:+;.>.,.,.'.'.'.'.).).).!.!.!.~.~.~.~.~.{.].^.^.^.^.^././././.o=6@6@6@7@7@7@m@m@m@m@O*z@A@A@n@B@U@A@W@W@W@W@W@W@*#*#]#]#]#]#X@X@1#1.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.p#f#0=0=0=}=}=}=f=8=4=4=4=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;x;A;A;A;A;A;t;k;C;I;I;I;I;I;I;.>Y;$>)>)>]>]>", +"y y y y y y x x x x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v v u u u u u u u u u u u u u t t t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t t u u u u u u u u u u u u u v v v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x x x x y y y y y y y y y y z z z z z A A A B B B B B B B B B (#)#)#)#.#C C D D D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H H H H H H I I R+*@*@*@*@*@*@|@=@=@=@=@R+R+R+R+S+S+/&M M M M j.N N N N N N N O O P P Q Q Q Q R R R R S S S S T T T T T U U V V ,*%+%+&+>@X X X X X X X Y Y Z Z Z Z =&3%-+-+;+F% ...+.+.+.+.+.@.#.#.#.#.#.#.d.$.W.,+,+,+'+'+'+!+!+!+S.u*Y.w$<%{+{+]+]+]+]+]+]+7+7+7+S$;.>.,.,.'.'.'.'.).).).!.!.!.~.~.~.~.~.{.].^.^.^.^.^.e./././.G@]@6@6@7@7@7@7@m@m@m@O*z@z@n@_=:.[.[.V*W@W@W@W@W@*#*#]#]#]#]#]#X@1#2.3.3.4.4.4.4.4.4.5.5.5.5.5.5.6.7.7.8.8.5.0=0=0=}=}=}=f=f=q=4=4=D=D=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;A;A;A;A;A;A;W;C;I;I;I;I;I;I;.>.>/>&>)>)>]>]>", +"y y y y y y y y a.x x x x x x x x x x x x x x x x x x w w w w w v v v v v v v v v v v v v v v v u u u u u u u u u u u u u u t t t t t t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t t t t t u u u u u u u u u u u u u u v v v v v v v v v v v v v v v v w w w w w x x x x x x x x x x x x x x x x x x a.y y y y y y y y y z z z z z z A A A B B B B B B B B B B C Z@)#)#)#)#L@D D D D D E E E E E E E E E F F G G G G G G G G G G i.H H H H H H H H H I I I A-*@*@*@*@|@|@=@=@=@=@R+R+R+S+S+S+S+S+v@M M N N N N N N N N O O P P Q Q Q Q R R R R S S S S T T T T T U U V V V h$%+&+&+.=X X X X X X Y Y Z Z Z Z Z ` q*-+;+;+P...+.+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.S.,+'+'+'+!+!+!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+Z+;.>.,.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].].^.^.^.^.e././.].L+]@6@6@6@7@7@7@m@m@m@m@z@z@|#a@[.[.[.[.}.7%W@W@W@W@*#*#]#]#]#]#X@N-|.3.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.8.8.f=0=0=}=}=}=f=f=f=4=D=D=K=K=K=K=K=K=(-(-(-(-(-(-(-l-l-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;A;A;A;A;A;_>C;I;I;I;I;I;I;.>Y;$>)>)>]>]>:>", +"z y y y y y y y y y y x x x x x x x x x x x x x x x x x x x w w w w w w v v v v v v v v v v v v v v v v v u u u u u u u u u u u u u u u t t t t t t t t t t t t t t t t t t t t t t s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s t t t t t t t t t t t t t t t t t t t t t t u u u u u u u u u u u u u u u v v v v v v v v v v v v v v v v v w w w w w w x x x x x x x x x x x x x x x x x x x y y y y y y y y y y z z z z z z A A A B B B B B B B B B B C C C t%)#)#.#.#C@D D D E E E E E E E E E b.F G G G G G G G G G G i.H H H H H H H H H I I I J J u@*@*@*@|@=@=@=@=@=@R+R+R+S+S+S+S+S+S+K&j.N N N N N N N N O O P P Q Q Q Q R R R S S S S T T T T T T U U V V V V V V&&+*+*+i$X X X X Y Y Z Z Z Z Z ` ` F+;+;+;+>+(%+.+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.M&,+'+'+)+!+!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+(+;.>.,.,.'.'.'.'.).).).).!.!.~.~.~.~.~.{.].].^.^.^.^.e././.T@L+]@6@6@6@7@7@7@m@m@m@m@O*z@)-[.[.[.[.[.}.}.c=9=W@W@*#*#]#]#]#]#X@X@/#3.3.3.4.4.4.4.4.m.5.5.5.5.5.5.6.7.7.8.8.8.9.a=0=}=}=}=f=f=f=f=O=D=K=K=K=K=K=K=]-(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;t;t;t;v;v;v;x;A;A;A;A;A;A;,>C;I;I;I;I;I;I;.>.>{>&>)>)>]>:>:>", +"z z z y y y y y y y y y y y x x x x x x x x x x x x x x x x x x x w w w w w w v v v v v v v v v v v v v v v v v v v u u u u u u u u u u u u u u u u u t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t t u u u u u u u u u u u u u u u u u v v v v v v v v v v v v v v v v v v v w w w w w w x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y z z z z z A A A A B B B B B B B B B B C C C C C C D%.#.#.#u%D E E E E E E E E E b.F G G G G G G G G G G G H H H H H H H H H H I I I J J J N+*@*@|@=@=@=@=@R+R+R+R+S+S+S+S+S+S+S+n%N N N N N N N N O P P Q Q Q Q Q R R R S S S S T T T T T T U U V V V V V W I**+*+*+I.X X X Y Y Z Z Z Z Z ` ` ` .P.;+>+O.-&+.+.+.@.#.#.#.#.#.#.d.$.$.$.$.$.$.a%'+)+!+!+!+~+~+~+~+{+{+]+]+]+]+]+]+7+7+7+~@;.>.>.,.'.'.'.'.).).).).!.!.!.~.~.~.~.~.{.].^.^.^.^.^././.z*p%]@6@6@6@7@7@7@m@m@m@m@O*&#p@[.[.[.[.[.}.}.}.}.&-W@*#*#]#]#]#]#]#X@X@,&3.3.4.4.4.4.4.4.5.5.5.5.5.5.6.7.7.8.8.8.9.9.E=}=}=}=}=f=f=f=f=2;D=K=K=K=K=K=K=(-(-(-(-(-(-l-l-w-w-w-w-w-&;&;&;&;(;(;(;t;t;v;v;v;v;A;A;A;A;A;A;A;<>C;I;I;I;I;I;.>.>_>&>)>)>]>]>:>:>"}; diff --git a/indy/Core/IdAntiFreeze.pas b/indy/Core/IdAntiFreeze.pas new file mode 100644 index 0000000..1fcfc5d --- /dev/null +++ b/indy/Core/IdAntiFreeze.pas @@ -0,0 +1,161 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 2004.02.03 4:16:42 PM czhower + For unit name changes. + + Rev 1.5 2004.01.01 3:13:32 PM czhower + Updated comment. + + Rev 1.4 2003.12.31 10:30:24 PM czhower + Comment update. + + Rev 1.3 2003.12.31 7:25:14 PM czhower + Now works in .net + + Rev 1.2 10/4/2003 9:52:08 AM GGrieve + add IdCoreGlobal to uses list + + Rev 1.1 2003.10.01 1:12:30 AM czhower + .Net + + Rev 1.0 11/13/2002 08:37:36 AM JPMugaas +} + +unit IdAntiFreeze; + +{ + NOTE - This unit must NOT appear in any Indy uses clauses. This is a ONE way + relationship and is linked in IF the user uses this component. This is done to + preserve the isolation from the massive FORMS unit. + + Because it links to Forms: + + - The Application.ProcessMessages cannot be done in IdCoreGlobal as an OS + independent function, and thus this unit is allowed to violate the IFDEF + restriction. +} + +interface + +{$I IdCompilerDefines.inc} +uses + Classes, + IdAntiFreezeBase, + IdBaseComponent; + +{ Directive needed for C++Builder HPP and OBJ files for this that will force it +to be statically compiled into the code } + +{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} +{$ELSE} + {$IFDEF WINDOWS} + {$HPPEMIT '#pragma link "IdAntiFreeze"'} {Do not Localize} + {$ENDIF} + {$IFDEF UNIX} + {$HPPEMIT '#pragma link "IdAntiFreeze.o"'} {Do not Localize} + {$ENDIF} +{$ENDIF} + +type + {$IFDEF HAS_ComponentPlatformsAttribute} + [ComponentPlatformsAttribute( + pidWin32 + {$IFDEF HAS_ComponentPlatformsAttribute_Win32} or pidWin32{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_Win64} or pidWin64{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_OSX32} or pidOSX32{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_iOS_Simulator} or pidiOSSimulator{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_Android} or pidAndroid{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_Linux32} or pidLinux32{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_iOS_Device32} or pidiOSDevice32{$ELSE} + {$IFDEF HAS_ComponentPlatformsAttribute_iOS_Device} or pidiOSDevice{$ENDIF}{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_WinNX32} or pidWinNX32{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_Linux64} or pidLinux64{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_WinIoT32} or pidWinIoT32{$ENDIF} + {$IFDEF HAS_ComponentPlatformsAttribute_iOS_Device64} or pidiOSDevice64{$ENDIF} + )] + {$ENDIF} + TIdAntiFreeze = class(TIdAntiFreezeBase) + public + procedure Process; override; + end; + +implementation + +uses + {$IFDEF WIDGET_KYLIX} + QForms, + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE} + Forms, + {$ENDIF} + {$IFDEF WINDOWS} + {$IFNDEF FMX} + Messages, + Windows, + {$ENDIF} + {$ENDIF} + {$IFDEF WIDGET_WINFORMS} + System.Windows.Forms, + {$ENDIF} + IdGlobal; + +{$IFDEF UNIX} +procedure TIdAntiFreeze.Process; +begin + //TODO: Handle ApplicationHasPriority + Application.ProcessMessages; +end; +{$ENDIF} + +{$IFDEF WINDOWS} + + {$IFNDEF FMX} +procedure TIdAntiFreeze.Process; +var + LMsg: TMsg; +begin + if ApplicationHasPriority then begin + Application.ProcessMessages; + end else begin + // This guarantees it will not ever call Application.Idle + if PeekMessage(LMsg, 0, 0, 0, PM_NOREMOVE) then begin + Application.HandleMessage; + end; + end; +end; + {$ELSE} +procedure TIdAntiFreeze.Process; +begin + //TODO: Handle ApplicationHasPriority + Application.ProcessMessages; +end; + {$ENDIF} + +{$ENDIF} + +{$IFDEF WIDGET_WINFORMS} +procedure TIdAntiFreeze.Process; +begin + //TODO: Handle ApplicationHasPriority + Application.DoEvents; +end; +{$ENDIF} + +end. diff --git a/indy/Core/IdAssignedNumbers.pas b/indy/Core/IdAssignedNumbers.pas new file mode 100644 index 0000000..7381f0d --- /dev/null +++ b/indy/Core/IdAssignedNumbers.pas @@ -0,0 +1,1948 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 10/15/2003 12:48:32 PM DSiders + Added l,ocalization comments. + + Rev 1.1 1/31/2003 02:54:04 PM JPMugaas + Updated with IdContext.pas. + + Rev 1.0 11/13/2002 08:37:54 AM JPMugaas +} + +unit IdAssignedNumbers; + +interface + +{ These values are all from RFC 1700 } + +const {Port values} + IdPORT_TCPMUX = 1; + IdPORT_COMPRESSNET_MGM = 2; //Management Utility + IdPORT_COMPRESSNET_CMP = 3; //Compression Proces + IdPORT_RJE = 5; //Remote Job Entry + IdPORT_ECHO = 7; + IdPORT_DISCARD = 9; + IdPORT_SYSTAT = 11; + IdPORT_DAYTIME = 13; + IdPORT_NETSTAT = 15; + IdPORT_QOTD = 17; + IdPORT_MSP = 18; //Message Send Protocol + IdPORT_CHARGEN = 19; {UDP Server!} + IdPORT_FTP_DATA = 20; //FTP Data default port + IdPORT_FTP = 21; + IdPORT_SSH = 22; //SSH Remote Login Protocol + IdPORT_TELNET = 23; + IdPORT_ANYTERMINIL = 24; //any private mail system + IdPORT_SMTP = 25; + IdPORT_NSW_FE = 27; //NSW New User System FE + IdPORT_MSG_ICMP = 29; + IdPORT_MSG_AUTH = 31; //MSG Authentication + IdPORT_DSP = 33; //Display Support Protocol + IdPORT_ANYPRINTER = 34; //any private printer server + + IdPORT_TIME = 37; + IdPORT_RAP = 38; //Remote Access Protocol + IdPORT_RLP = 39; //Resource Location Protocol + IdPORT_GRAPHICS = 41; //Graphics + IdPORT_NAMESERVER = 42; //Host Name Server + IdPORT_WHOIS = 43; + IdPORT_MPM_FLAGS = 44; //MPM Flags Protocol + IdPORT_MPM = 45; //Message Processing Protocol + IdPORT_MPM_SND = 46; //MPM (Default Send) + IdPORT_NI_FTP = 47; + IdPORT_AUDITD = 48; //Digital Audit Daemon + IdPORT_BBN_LOGIN = 49; //Login Host Protocol + IdPORT_RE_MAIL_CK = 50; //Remote Mail Checking Protocol + IdPORT_LA_MAINT = 51; //IMP Logical Address Maintenance + IdPORT_XNS_TIME = 52; //XNS Time Protocol + IdPORT_DOMAIN = 53; + IdPORT_XNS_CH = 54; //XNS Clearinghouse + IdPORT_ISI_GL = 55; //ISI Graphics Language + IdPORT_XNS_AUTH = 56; //XNS Authentication + IdPORT_ANYPRIVATE_TERMINAL = 57; //any private terminal access + IdPORT_XNS_MAIL = 58; //XNS Mail + IdPORT_ANY_FILE = 59; // Any private file service + + IdPORT_NI_MAIL = 61; + IdPORT_ACAS = 62; //ACA Services + IdPORT_WHOIS_PLUS = 63; //WhoIs++ + IdPORT_COVIA = 64; //Communications Integrator (CI) + IdPORT_TACACS_DS = 65; // TACACS-Database Service + IdPORT_SQLNET = 66; //Oracle SQL*NET + IdPORT_BOOTPS = 67; //Bootstrap Protocol Server - DHCP uses this as well + IdPORT_BOOTPC = 68; //Bootstrap Protocol Client - DHCP uses this as well + IdPORT_TFTP = 69; + IdPORT_GOPHER = 70; + IdPORT_NETRJS1 = 71; //Network Job Service + IdPORT_NETRJS2 = 72; + IdPORT_NETRJS3 = 73; + IdPORT_NETRJS4 = 74; + IdPORT_ANYDIAL = 75; //Any dial-out service + IdPORT_DEOS = 76; //Distributed External Object Store + IdPORT_ANYRJE = 77; //Any RJE Service + IdPORT_VETTCP = 78; + IdPORT_FINGER = 79; + IdPORT_HTTP = 80; + IdPORT_HOSTS2_NS = 81; //HOSTS2 Name Server + IdPORT_XFER = 82; //XFER Utility + IdPORT_MIT_ML_DEV = 83; //MIT ML Device + IdPORT_CTF = 84; //Common Trace Facility + IdPORT_MIT_ML_DEV2 = 85; //MIT ML Device + IdPORT_MFCOBOL = 86; //Micro Focus Cobol + IdPORT_ANYTERMINALLINK = 87; //Any private terminal link + IdPORT_KERBEROS = 88; //kerberos + IdPORT_SU_MIT_TG = 89; //SU/MIT Telnet Gateway + IdPORT_DNSIX = 90; // DNSIX Securit Attribute Token Map + IdPORT_MIT_DOV = 91; //MIT Dover Spooler + IdPORT_NPP = 92; //Network Printing Protocol + IdPORT_DCP = 93; //Device Control Protocol + IdPORT_OBJCALL = 94; //Tivoli Object Dispatcher + IdPORT_SUPDUP = 95; //SUPDUP + IdPORT_DIXIE = 96; //DIXIE Protocol Specification + IdPORT_SWIFT_RVF = 97; //Swift Remote Virtural File Protocol + IdPORT_TACNEWS = 98; //TAC News + IdPORT_METAGRAM = 99; // Metagram Relay + IdPORT_NEWACCT = 100; // [unauthorized use] + IdPORT_HOSTNAME = 101; + IdPORT_ISO_TSAP = 102; // ISO-TSAP Class 0 + IdPORT_GPPITNP = 103; // Genesis Point-to-Point Trans Net + IdPORT_ACR_NAME = 104; //ACR-NEMA Digital Imag. & Comm. 300 + IdPORT_CSNET_NS = 105; // Mailbox Name Nameserver + IdPORT_3COM_TSMUX = 106; // 3COM-TSMUX + IdPORT_RTELNET = 107;// Remote Telnet Service + IdPORT_SNAGAS = 108;// SNA Gateway Access Server + IdPORT_POP2 = 109; + IdPORT_POP3 = 110; + IdPORT_SUNRPC = 111;// SUN Remote Procedure Call + IdPORT_McIDAS = 112;// McIDAS Data Transmission Protocol + {This is called AUT in the RFC. The protocol was renamed to IdentD to better + refect what the protocol was doing} + IdPORT_AUTH = 113; + IdPORT_AUDIONEWS = 114;// Audio News Multicast + IdPORT_SFTP = 115; //tcp Simple File Transfer Protocol + IdPORT_ANSANOTIFY = 116; // ANSA REX Notify + IdPORT_UUCP_PATH = 117; // UUCP Path Service + IdPORT_SQLSERV = 118;//tcp SQL Services + IdPORT_NNTP = 119; + IdPORT_CFDPTKT = 120;//tcp CFDPTKT + IdPORT_ERPC = 121;//tcp Encore Expedited Remote Pro.Call + IdPORT_SMAKYNET = 122;//tcp SMAKYNET + IdPORT_SNTP = 123; + IdPORT_ANASTRADER = 124;// ANSA REX Trader + IdPORT_LOCUS_MAP = 125;// Locus PC-Interface Net Map Ser + IdPORT_UNITARY = 126;// Unisys Unitary Login + IdPORT_locus_con = 127;// Locus PC-Interface Conn Server + IdPORT_GSS_XLICEN = 128;// GSS X License Verification + IdPORT_PWDGEN = 129;// Password Generator Protocol + IdPORT_CISCO_FNA = 130;// cisco FNATIVE + IdPORT_cisco_tna = 131;// cisco TNATIVE + IdPORT_cisco_sys = 132;//cisco SYSMAINT + IdPORT_statsrv = 133;// Statistics Service + IdPORT_ingres_net = 134;//INGRES-NET Service + IdPORT_loc_srv = 135;//Location Service + IdPORT_profile = 136;// PROFILE Naming System + IdPORT_netbios_ns = 137;// NETBIOS Name Service + IdPORT_netbios_dgm = 138;// NETBIOS Datagram Service + IdPORT_netbios_ssn = 139;// NETBIOS Session Service + IdPORT_emfis_data = 140;// EMFIS Data Service + IdPORT_emfis_cntl = 141;// EMFIS Control Service + IdPORT_bl_idm = 142;//Britton-Lee IDM + IdPORT_IMAP4 = 143; + IdPORT_news = 144;//NewS + IdPORT_uaac = 145;//UAAC Protocol + IdPORT_iso_tp0 = 146;//ISO-IP0 + IdPORT_iso_ip = 147;//ISO-IP + IdPORT_cronus = 148;//CRONUS-SUPPORT + IdPORT_aed_512 = 149;//AED 512 Emulation Service + IdPORT_sql_net = 150;//SQL-NET + IdPORT_hems = 151;//HEMS + IdPORT_bftp = 152;// Background File Transfer Program + IdPORT_sgmp = 153;// SGMP + IdPORT_netsc_prod = 154;// NETSC + IdPORT_netsc_dev = 155;// NETSC + IdPORT_sqlsrv = 156;// SQL Service + IdPORT_knet_cmp = 157;// KNET/VM Command/Message Protocol + IdPORT_pcmail_srv = 158;// PCMail Server + IdPORT_nss_routing = 159;// NSS-Routing + IdPORT_sgmp_traps = 160;// SGMP-TRAPS + IdPORT_snmp = 161;// SNMP + IdPORT_snmptrap = 162;// SNMPTRAP + IdPORT_cmip_man = 163;// CMIP/TCP Manager + IdPORT_cmip_agent = 164;// CMIP/TCP Agent + IdPORT_xns_courier = 165;// Xerox + IdPORT_s_net = 166;// Sirius Systems + IdPORT_namp = 167;// NAMP + IdPORT_rsvd = 168;// tcp RSVD + IdPORT_send = 169;//tcp SEND + IdPORT_print_srv = 170;//tcp Network PostScript + IdPORT_multiplex = 171;// Network Innovations Multiplex + IdPORT_cl_1 = 172;//Network Innovations CL/1 + IdPORT_xyplex_mux = 173;//Xyplex + IdPORT_mailq = 174; // MAILQ + IdPORT_vmnet = 175;// VMNET + IdPORT_genrad_mux = 176;// GENRAD-MUX + IdPORT_xdmcp = 177;// X Display Manager Control Protocol + IdPORT_NextStep = 178;// NextStep Window Server + IdPORT_bgp = 179;// Border Gateway Protocol + IdPORT_ris = 180;// Intergraph + IdPORT_unify = 181;// Unify + IdPORT_audit = 182;// Unisys Audit SITP + IdPORT_ocbinder = 183;// OCBinder + IdPORT_ocserver = 184;// OCServer + IdPORT_remote_kis = 185;// Remote-KIS + IdPORT_kis = 186;// KIS Protocol + IdPORT_aci = 187;// Application Communication Interface + IdPORT_mumps = 188;// Plus Five's MUMPS {Do not Localize} + IdPORT_qft = 189;// Queued File Transport + IdPORT_gacp = 190;// Gateway Access Control Protocol + IdPORT_prospero = 191;// Prospero Directory Service + IdPORT_osu_nms = 192;// OSU Network Monitoring System + IdPORT_srmp = 193;// Spider Remote Monitoring Protocol +{not sure about this one +irc 194/tcp Internet Relay Chat Protocol +irc 194/udp Internet Relay Chat Protocol +# Jarkko Oikarinen +} + IdPORT_dn6_nlm_aud = 195;// DNSIX Network Level Module Audit + IdPORT_dn6_smm_red = 196;// DNSIX Session Mgt Module Audit Redir + IdPORT_dls = 197;// Directory Location Service + IdPORT_dls_mon = 198;// Directory Location Service Monitor + IdPORT_smux = 199;//SMUX + IdPORT_src = 200;//IBM System Resource Controller + IdPORT_at_rtmp = 201;//AppleTalk Routing Maintenance + IdPORT_at_nbp = 202;//ppleTalk Name Binding + IdPORT_at_3 = 203;//AppleTalk Unused + IdPORT_at_echo = 204;//AppleTalk Echo + IdPORT_at_5 = 205;//AppleTalk Unused + IdPORT_at_zis = 206;//AppleTalk Zone Information + IdPORT_at_7 = 207;//AppleTalk Unused + IdPORT_at_8 = 208;//AppleTalk Unused + IdPORT_qmtp = 209;//the Quick Mail Transfer Protocol + IdPORT_z39_50 = 210;//ANSI Z39.50 + IdPORT_914c_g = 211;//Texas Instruments 914C/G Terminal + IdPORT_anet = 212;//ATEXSSTR + IdPORT_ipx = 213;//IPX + IdPORT_vmpwscs = 214;//VM PWSCS + IdPORT_softpc = 215;//Insignia Solutions + IdPORT_CAIlic = 216;//Computer Associates Int'l License Server {Do not Localize} + IdPORT_dbase = 217;//dBASE Unix + IdPORT_mpp = 218;// Netix Message Posting Protocol + IdPORT_uarps = 219;// Unisys ARPs + IdPORT_imap3 = 220;// Interactive Mail Access Protocol v3 + IdPORT_fln_spx = 221;// Berkeley rlogind with SPX auth + IdPORT_rsh_spx = 222;// Berkeley rshd with SPX auth + IdPORT_cdc = 223;//Certificate Distribution Center + Id_PORT_masqdialer = 224;//tcp masqdialer + + Id_PORT_direct = 242;//tcp Direct + IdPORT_sur_meas = 243;// Survey Measurement + Id_PORT_inbusiness = 244;//tcp inbusiness + IdPORT_link = 245;// LINK + IdPORT_dsp3270 = 246;// Display Systems Protocol + IdPORT_pdap = 344;// Prospero Data Access Protocol + IdPORT_pawserv = 345;// Perf Analysis Workbench + IdPORT_zserv = 346;//Zebra server + IdPORT_fatserv = 347;//Fatmen Server + IdPORT_csi_sgwp = 348;//Cabletron Management Protocol + Id_PORT_mftp = 349; //mftp + Id_PORT_matip_type_a = 350; //MATIP Type A + Id_PORT_matip_type_b = 351; //MATIP Type B + Id_PORT_dtag_ste_sb = 352; //DTAG + Id_PORT_ndsauth = 353; //ndsauth + Id_PORT_bh611 = 354; //bh611 + Id_PORT_datex_asn = 355; //datex-asn + Id_PORT_cloanto_net_1 = 356; //Cloanto Net 1 + Id_PORT_bhevent = 357; //bhevent + Id_PORT_shrinkwrap = 358; //Shrinkwrap + Id_PORT_nsrmp = 359;//tcp Network Security Risk Management Protocol + Id_PORT_scoi2odialog = 360;//tcp scoi2odialog + Id_PORT_semantix = 361;//tcp Semantix + Id_PORT_srssend = 362;//tcp SRS Sen + Id_PORT_rsvp_tunnel = 363;//tcp RSVP Tunnel + Id_PORT_aurora_cmgr = 364;//tcp Aurora CMGR + Id_PORT_dtk = 365;//tcp DTK + Id_PORT_odmr = 366;//tcp ODMR + Id_PORT_mortgageware = 367;//tcp MortgageWare + Id_PORT_qbikgdp = 368;//tcp QbikGDP + Id_PORT_rpc2portmap = 369;//tcp rpc2portmap + Id_PORT_codaauth2 = 370;//tcp codaauth2 + IdPORT_clearcase = 371;//Clearcase + IdPORT_ulistserv = 372;//Unix Listserv + IdPORT_legent_1 = 373;//Legent Corporation + IdPORT_legent_2 = 374;//Legent Corporation + IdPORT_hassle = 375;//Hassle + IdPORT_nip = 376; // Amiga Envoy Network Inquiry Proto + IdPORT_tnETOS = 377;//NEC Corporation + IdPORT_dsETOS = 378;//NEC Corporation + IdPORT_is99c = 379;//TIA/EIA/IS-99 modem client + IdPORT_is99s = 380;//TIA/EIA/IS-99 modem server + IdPORT_hp_collector = 381;//hp performance data collector + IdPORT_hp_managed_node = 382;//hp performance data managed node + IdPORT_hp_alarm_mgr = 383;//hp performance data alarm manager + IdPORT_arns = 384;//A Remote Network Server System + IdPORT_ibm_app = 385;//IBM Application + IdPORT_asa = 386;//ASA Message Router Object Def. + IdPORT_aurp = 387;//Appletalk Update-Based Routing Pro. + IdPORT_unidata_ldm = 388;//Unidata LDM Version 4 + IdPORT_ldap = 389;//Lightweight Directory Access Protocol + IdPORT_uis = 390;//UIS + IdPORT_synotics_relay = 391;//SynOptics SNMP Relay Port + IdPORT_synotics_broker = 392;//SynOptics Port Broker Port + IdPORT_dis = 393;//Data Interpretation System + IdPORT_embl_ndt = 394;//EMBL Nucleic Data Transfer + IdPORT_etcp = 395;//NETscout Control Protocol + IdPORT_netware_ip = 396;//Novell Netware over IP + IdPORT_mptn = 397;//Multi Protocol Trans. Net. + IdPORT_kryptolan = 398;//Kryptolan + IdPORT_iso_tsap_c2 = 399;//ISO Transport Class 2 Non-Control over TCP + IdPORT_work_sol = 400;//Workstation Solutions + IdPORT_ups = 401;//Uninterruptible Power Supply + IdPORT_genie = 402;//Genie Protocol + IdPORT_decap = 403;//decap + IdPORT_nced = 404;//nced + IdPORT_ncld = 405;//ncld + IdPORT_imsp = 406;//Interactive Mail Support Protocol + IdPORT_timbuktu = 407;//Timbuktu + IdPORT_prm_sm = 408;//Prospero Resource Manager Sys. Man. + IdPORT_prm_nm = 409;//Prospero Resource Manager Node Man. + IdPORT_decladebug = 410;//DECLadebug Remote Debug Protocol + IdPORT_rmt = 411;//Remote MT Protocol + IdPORT_synoptics_trap = 412;//Trap Convention Port + IdPORT_smsp = 413;//SMSP + IdPORT_infoseek = 414;//InfoSeek + IdPORT_bnet = 415;//BNet + IdPORT_silverplatter = 416;//Silverplatter + IdPORT_onmux = 417;//Onmux + IdPORT_hyper_g = 418;//Hyper-G + IdPORT_ariel1 = 419;//Ariel + IdPORT_smpte = 420;//SMPTE + IdPORT_ariel2 = 421;//Ariel + IdPORT_ariel3 = 422;//Ariel + IdPORT_opc_job_start = 423;//IBM Operations Planning and Control Start + IdPORT_opc_job_track = 424;//IBM Operations Planning and Control Track + IdPORT_icad_el = 425;//ICAD + IdPORT_smartsdp = 426;//smartsdp + IdPORT_svrloc = 427;//Server Location + IdPORT_ocs_cmu = 428;//OCS_CMU + IdPORT_ocs_amu = 429;//OCS_AMU + IdPORT_utmpsd = 430;//UTMPSD + IdPORT_utmpcd = 431;//UTMPCD + IdPORT_iasd = 432;//IASD + IdPORT_nnsp = 433;//NNSP + IdPORT_mobileip_agent = 434;//MobileIP-Agent + IdPORT_mobilip_mn = 435;//MobilIP-MN + IdPORT_dna_cml = 436;//DNA-CML + IdPORT_comscm = 437;//comscm + IdPORT_dsfgw = 438;//dsfgw + IdPORT_dasp = 439;//dasp Thomas Obermair + IdPORT_sgcp = 440;//sgcp + IdPORT_decvms_sysmgt = 441;//decvms-sysmgt + IdPORT_cvc_hostd = 442;//cvc_hostd + IdPORT_https = 443; //HTTPS + IdPORT_npp2 = 444;//Simple Network Paging Protocol [RFC1568] + IdPORT_microsoft_ds = 445;//Microsoft-DS + IdPORT_ddm_rdb = 446; //DDM-RDB + IdPORT_ddm_dfm = 447;//DDM-RFM + IdPORT_ddm_byte = 448;//DDM-BYTE + IdPORT_as_servermap = 449;//AS Server Mapper + IdPORT_tserver = 450;//TServer + IdPORT_sfs_smp_net = 451;//Cray Network Semaphore server + IdPORT_sfs_config = 452;//Cray SFS config server + IdPORT_creativeserver = 453;//CreativeServer + IdPORT_contentserver = 454;//ContentServer + IdPORT_creativepartnr = 455;//CreativePartnr + IdPORT_macon_tcp = 456;//macon-tcp + IdPORT_scohelp = 457;//scohelp + IdPORT_appleqtc = 458;//apple quick time + IdPORT_ampr_rcmd = 459;//ampr-rcmd + IdPORT_skronk = 460;//skronk + IdPORT_datasurfsrv = 461;//DataRampSrv + IdPORT_datasurfsrvsec = 462;//DataRampSrvSec + IdPORT_alpes = 463;//alpes + IdPORT_kpasswd = 464;//kpasswd + IdPORT_ssmtp = 465;//ssmtp + IdPORT_digital_vrc = 466;//digital-vrc + IdPORT_mylex_mapd = 467;//mylex-mapd + IdPORT_photuris = 468;//proturis + IdPORT_rcp = 469;//Radio Control Protocol + IdPORT_scx_proxy = 470;//scx-proxy + IdPORT_mondex = 471;//Mondex + IdPORT_ljk_login = 472;//jk-login + IdPORT_hybrid_pop = 473;//hybrid-pop + IdPORT_tn_tl_w1 = 474;//tcp tn-tl-w1 + IdPORT_tn_tl_w2 = 474;//udp tn-tl-w2 + IdPORT_tcpnethaspsrv = 475;//tcp tcpnethaspsrv + IdPORT_tn_tl_fd1 = 476;//tn-tl-fd1 + IdPORT_ss7ns = 477;//ss7ns + IdPORT_spsc = 478;//spsc + IdPORT_iafserver = 479;//iafserver + IdPORT_iafdbase = 480;//iafdbase + IdPORT_ph = 481;//Ph service + IdPORT_bgs_nsi = 482;//bgs-nsi + IdPORT_ulpnet = 483;//ulpnet + IdPORT_integra_sme = 484;//Integra Software Management Environment + IdPORT_powerburst = 485;//Air Soft Power Burst + IdPORT_avian = 486;//avian + IdPORT_saft = 487;//saft + IdPORT_gss_http = 488;//gss-http + IdPORT_nest_protocol = 489;//nest-protocol + IdPORT_micom_pfs = 490;//micom-pfs + IdPORT_go_login = 491;//go-login + IdPORT_ticf_1 = 492;//Transport Independent Convergence for FNA + IdPORT_ticf_2 = 493;//Transport Independent Convergence for FNA + IdPORT_pov_ray = 494;//tcp POV-Ray + IdPORT_intecourier = 495; //tcp/udp intecourier + Id_PORT_pim_rp_disc = 496; //tcp/udp pim-rp-disc + Id_PORT_dantz = 497; //tcp/udp dantz + Id_PORT_siam = 498; //tcp/udp siam + Id_PORT_ISO_ILL = 499; //tcp/udp iso-ill ISO ILL Protocol + Id_PORT_isakmp = 500; //tcp/udp isakmp + Id_PORT_stmf = 501; //tcp/udp stmf + Id_PORT_asa_appl_proto = 502; //tcp/udp asa-appl-proto + Id_PORT_intrinsa = 503; // intrinsa + Id_PORT_citadel = 504; //citadel + Id_PORT_mailbox_lm = 505; //mailbox-lm + Id_PORT_ohimsrv = 506; //ohimsrv + Id_PORT_crs = 507; //crs + Id_PORT_xvttp = 508; //xvttp + Id_PORT_snare = 509; //snare + Id_PORT_FirstClass = 510; //FirstClass Protocol + Id_PORT_passgo = 511; //PassGo + Id_PORT_exec = 512; {/tcp remote process execution; + authentication performed using + passwords and UNIX loppgin names } + + Id_PORT_biff = 512;{udp used by mail system to notify users + of new mail received; currently + receives messages only from + processes on the same machine } + Id_PORT_login = 513;{tcp remote login a la telnet; + automatic authentication performed + based on priviledged port numbers + and distributed data bases which + identify "authentication domains" } + IdPORT_who = 513;{udp maintains data bases showing who's + logged in to machines on a local + net and the load average of the + machine } + IdPORT_cmd = 514;{tcp like exec, but automatic + authentication is performed as for + login server } + IdPORT_syslog = 514;{udp } + IdPORT_LPD = 515; + IdPORT_talk = 517;{tcp like tenex link, but across + machine - unfortunately, doesn't + use link protocol (this is actually + just a rendezvous port from which a + tcp connection is established)} + IdPORT_ntalk = 518; + IdPORT_utime = 519; // unixtime + IdPORT_efs = 520; //tcp extended file name server + IdPORT_router = 520; {udp local routing process (on site); + uses variant of Xerox NS routing + information protocol } + IdPORT_timed = 525; // timeserver + IdPORT_tempo = 526; // newdate + IdPORT_courier = 530; // rpc + IdPORT_conference = 531;//chat + IdPORT_netnews = 532;//readnews + IdPORT_netwall = 533;// for emergency broadcasts + + IdPORT_apertus_ldp = 539;// Apertus Technologies Load Determination + IdPORT_uucp = 540;// uucpd + IdPORT_uucp_rlogin = 541;//uucp-rlogin + IdPORT_klogin = 543;// + IdPORT_kshell = 544;//krcmd + IdPORT_appleqtcsrvr = 545; // appleqtcsrvr + IdPORT_dhcp_client_v6 = 546; //DHCP Client IP 6 + IdPORT_dhcp_server_v6 = 547; // DHCP Server IP 6 + Id_PORT_afpovertcp = 548;//tcp AFP over TCP + Id_PORT_idfp = 549;//tcp IDFP + IdPORT_new_rwho = 550; // new-who + IdPORT_cybercash = 551; // cybercash + IdPORT_deviceshare = 552; // deviceshare + IdPORT_pirp = 553; // pirp + IdPORT_rtsp = 554; // Real Time Stream Control Protocol + IdPORT_dsf = 555; + IdPORT_remotefs = 556; //rfs server + IdPORT_openvms_sysipc = 557;// openvms-sysipc + IdPORT_sdnskmp = 558;// SDNSKMP + IdPORT_teedtap = 559;// TEEDTAP + IdPORT_rmonitor = 560;// rmonitord + IdPORT_monitor = 561;// + IdPORT_chshell = 562;// chcmd + IdPORT_SNEWS = 563; + IdPORT_9pfs = 564;/// plan 9 file service + IdPORT_whoami = 565;// whoami + IdPORT_streettalk = 566;// streettalk + IdPORT_banyan_rpc = 567;//banyan-rpc + IdPORT_ms_shuttle = 568;//microsoft shuttle + IdPORT_ms_rome = 569;//microsoft rome + IdPORT_meter = 570;//demon + IdPORT_meter_udemon = 571;//udemon + IdPORT_sonar = 572;//sonar + IdPORT_banyan_vip = 573;//banyan-vip + IdPORT_ftp_agent = 574;//FTP Software Agent System + IdPORT_vemmi = 575;//VEMMI + Id_PORT_ipcd = 576;//tcp ipcd + Id_PORT_vnas = 577;//tcp vnas + Id_PORT_ipdd = 578;//tcp ipdd + Id_PORT_decbsrv = 579;//tcp decbsrv + Id_PORT_sntp_heartbeat = 580;//tcp SNTP HEARTBEAT + Id_PORT_bdp = 581;//tcp Bundle Discovery Protocol + Id_PORT_scc_security = 582;//tcp SCC Security + Id_PORT_philips_vc = 583;//tcp Philips Video-Conferencing + Id_PORT_keyserver = 584;//tcp Key Server + Id_PORT_imap4_ssl_dp = 585;//tcp IMAP4+SSL (use 993 instead) +// Use of 585 is not recommended, use 993 instead + Id_PORT_password_chg = 586;//tcp Password Change + Id_PORT_submission = 587;//tcp Submission + Id_PORT_cal = 588;//tcp CAL + Id_PORT_eyelink = 589;//tcp EyeLink + Id_PORT_tns_cml = 590;//tcp TNS CML + Id_PORT_http_alt = 591;//tcp FileMaker, Inc. - HTTP Alternate (see Port 80) + Id_PORT_eudora_set = 592;//tcp Eudora Set + Id_PORT_http_rpc_epmap = 593;//tcp HTTP RPC Ep Map + Id_PORT_tpip = 594;//tcp TPIP + Id_PORT_cab_protocol = 595;//tcp CAB Protocol + Id_PORT_smsd = 596;//tcp SMSD + Id_PORT_ptcnameservice = 597;//tcp PTC Name Service + Id_PORT_sco_websrvrmg3 = 598;//tcp SCO Web Server Manager 3 + Id_PORT_acp = 599;//tcp Aeolon Core Protocol + IdPORT_ipcserver = 600;//Sun IPC server + Id_PORT_syslog_conn = 601;//Reliable Syslog protocol + + IdPORT_urm = 606;//Cray Unified Resource Manager + IdPORT_nqs = 607;//nqs + IdPORT_sift_uft = 608;//Sender-Initiated/Unsolicited File Transfer + IdPORT_npmp_trap = 609;//npmp-trap + IdPORT_npmp_local = 610;//npmp-local + IdPORT_npmp_gui = 611;//npmp-gui + Id_PORT_hmmp_ind = 612;//tcp HMMP Indication + Id_PORT_hmmp_op = 613;//tcp HMMP Operation + Id_PORT_sshell = 614;//tcp SSLshell + Id_PORT_sco_inetmgr = 615;//tcp Internet Configuration Manager + Id_PORT_sco_sysmgr = 616;//tcp SCO System Administration Server + Id_PORT_sco_dtmgr = 617;//tcp SCO Desktop Administration Server + Id_PORT_dei_icda = 618;//tcp DEI-ICDA + Id_PORT_compaq_evm = 619;//tcp Compaq EVM + Id_PORT_sco_websrvrmgr = 620;//tcp SCO WebServer Manager + Id_PORT_escp_ip = 621;//tcp ESCP + Id_PORT_collaborator = 622;//tcp Collaborator + Id_PORT_aux_bus_shunt = 623;//tcp Aux Bus Shunt + Id_PORT_cryptoadmin = 624;//tcp Crypto Admin + Id_PORT_dec_dlm = 625;//tcp DEC DLM + Id_PORT_asia = 626;//tcp ASIA + Id_PORT_passgo_tivoli = 627;//tcp PassGo Tivoli + Id_PORT_qmqp = 628;//tcp QMQP + Id_PORT_3com_amp3 = 629;//tcp 3Com AMP3 + Id_PORT_rda = 630;//tcp RDA + Id_PORT_ipp = 631;//tcp IPP (Internet Printing Protocol) + Id_PORT_bmpp = 632;//tcp bmpp + IdPORT_servstat = 633;// Service Status update (Sterling Software) + IdPORT_ginad = 634;// ginad + Id_PORT_rlzdbase = 635;//tcp RLZ DBase + Id_PORT_ldaps = 636;//tcp ldap protocol over TLS/SSL (was sldap) + Id_PORT_lanserver = 637;//tcp lanserver + Id_PORT_mcns_sec = 638;//tcp mcns-sec + Id_PORT_msdp = 639;//tcp MSDP + Id_PORT_entrust_sps = 640;//tcp entrust-sps + Id_PORT_repcmd = 641;//tcp repcmd + Id_PORT_esro_emsdp = 642;//tcp ESRO-EMSDP V1.3 + Id_PORT_sanity = 643;//tcp SANity + Id_PORT_dwr = 644;//tcp dwr + Id_PORT_pssc = 645;//tcp PSSC + Id_PORT_ldp = 646;//tcp LDP + Id_PORT_dhcp_failover = 647;//tcp DHCP Failover + Id_PORT_rrp = 648;//tcp Registry Registrar Protocol (RRP) + Id_PORT_aminet = 649;//tcp Aminet + Id_PORT_obex = 650;//tcp OBEX + Id_PORT_ieee_mms = 651;//tcp IEEE MMS + Id_PORT_hello_port = 652;//tcp HELLO_PORT + Id_PORT_repscmd = 653;//tcp RepCmd + Id_PORT_aodv = 654;//tcp AODV + Id_PORT_tinc = 655;//tcp TINC + Id_PORT_spmp = 656;//tcp SPMP + Id_PORT_rmc = 657;//tcp RMC + Id_PORT_tenfold = 658;//tcp TenFold + + Id_PORT_mac_srvr_admin = 660;//tcp MacOS Server Admin + Id_PORT_hap = 661;//tcp HAP + Id_PORT_pftp = 662;//tcp PFTP + Id_PORT_purenoise = 663;//tcp PureNoise + Id_PORT_secure_aux_bus = 664;//tcp Secure Aux Bus + Id_PORT_sun_dr = 665;//tcp Sun DR + IdPORT_mdqs = 666;// + IdPORT_doom = 666;//doom Id Software + Id_PORT_disclose = 667;//campaign contribution disclosures - SDR Technologies + Id_PORT_mecomm = 668;//MeComm + Id_PORT_meregister = 669;//MeRegister + Id_PORT_vacdsm_sws = 670;//VACDSM-SWS + Id_PORT_vacdsm_app = 671;//VACDSM-APP + Id_PORT_vpps_qua = 672; //VPPS-QUA + Id_PORT_cimplex = 673; //CIMPLEX + Id_PORT_acap = 674; //ACAP + Id_PORT_dctp = 675; //DCTP + Id_PORT_vpps_via = 676; //VPPS Via + Id_PORT_vpp = 677; //Virtual Presence Protocol + Id_PORT_gnf_ncp = 678;//GNU Generation Foundation NCP + Id_PORT_mrm = 679;//MRM + Id_PORT_entrust_aaas = 680;//entrust-aaas + Id_PORT_entrust_aams = 681;//entrust-aams + Id_PORT_xfr = 682;//XFR + Id_PORT_corba_iiop = 683;//CORBA IIOP + Id_PORT_corba_iiop_ssl = 684; //CORBA IIOP SSL + Id_PORT_mdc_portmapper = 685; //MDC Port Mapper + Id_PORT_hcp_wismar = 686; //Hardware Control Protocol Wismar + Id_PORT_asipregistry = 687;//asipregistry + Id_PORT_realm_rusd = 688; //REALM-RUSD + Id_PORT_nmap = 689;//NMAP + Id_PORT_vatp = 690; //VATP + Id_PORT_msexch_routing = 691; //msexch-routing + Id_PORT_hyperwave_isp = 692; //Hyperwave-ISP + Id_PORT_connendp = 693; //connendp + Id_PORT_ha_cluster = 694; //ha-cluster + Id_PORT_ieee_mms_ssl = 695; //IEEE-MMS-SSL + Id_PORT_rushd = 696; //RUSHD + Id_PORT_uuidgen = 697; //UUIDGEN + Id_PORT_olsr = 698; //OLSR + Id_PORT_accessnetwork = 699; //Access Network + + IdPORT_elcsd = 704;//errlog copy/server daemon + Id_PORT_agentx = 705; //AgentX + Id_PORT_silc = 706; //SILC + Id_PORT_borland_dsj = 707; // Borland DSJ + + IdPORT_entrustmanager = 709;//EntrustManager + Id_PORT_entrust_ash = 710;// Entrust Administration Service Handler + Id_PORT_cisco_tdp = 711; //Cisco TDP + + IdPORT_netviewdm1 = 729;//IBM NetView DM/6000 Server/Client + IdPORT_netviewdm2 = 730;//IBM NetView DM/6000 send/tcp + IdPORT_netviewdm3 = 731;//IBM NetView DM/6000 receive/tcp + IdPORT_netgw = 741;//netGW + IdPORT_netrcs = 742;//Network based Rev. Cont. Sys. + + IdPORT_flexlm = 744;//Flexible License Manager + + IdPORT_fujitsu_dev = 747;//Fujitsu Device Control + IdPORT_ris_cm = 748;//Russell Info Sci Calendar Manager + IdPORT_kerberos_adm = 749;//kerberos administration + IdPORT_rfile = 750;//tcp + IdPORT_loadav = 750;//udp + IdPORT_pump = 751; + IdPORT_qrh = 752; + IdPORT_rrh = 753; + IdPORT_tell = 754;// send + IdPORT_nlogin = 758; + IdPORT_con = 759; + IdPORT_ns = 760; + IdPORT_rxe = 761; + IdPORT_quotad = 762; + IdPORT_cycleserv = 763; + IdPORT_omserv = 764; + IdPORT_webster = 765; + IdPORT_phonebook = 767;// phone + IdPORT_vid = 769; + IdPORT_cadlock = 770; + IdPORT_rtip = 771; + IdPORT_cycleserv2 = 772; + IdPORT_submit = 773;//tcp + IdPORT_notify = 773;//udp + IdPORT_rpasswd = 774;//tcp + IdPORT_acmaint_dbd = 774; //udp + IdPORT_entomb = 775; + IdPORT_acmaint_transd = 775; + IdPORT_wpages = 776; + IdPORT_wpgs = 780; + + IdPORT_concert = 786;//Concert + IdPORT_qsc = 787; //QSC + + IdPORT_mdbs_daemon = 800; + + IdPORT_device = 801; + + Id_PORT_fcp = 810; //FCP or FCP datagram (udp) + + Id_PORT_itm_mcell_s = 828; //itm-mcell-s + Id_PORT_pkix_3_ca_ra = 829; //PKIX-3 CA/RA + + + Id_PORT_dhcp_failover2 = 847; //dhcp-failover 2 + + Id_PORT_rsync = 873; //rsync + Id_PORT_iclcnet_locate = 886; //ICL coNETion locate server + Id_PORT_iclcnet_svinfo = 887; //ICL coNETion server info + IdPORT_accessbuilder = 888;//udp AccessBuilder + + Id_PORT_omginitialrefs = 900;//omginitialrefs + Id_PORT_smpnameres = 901; //smpnameres + Id_PORT_ideafarm_chat = 902; //IDEAFARM CHAT + Id_PORT_ideafarm_catch = 903; //IDEAFARM CATCH + + IdPOPRT_xact_backup = 911;//xact-backup + IdPORT_ftps_data = 989; //ftp protocol, data, over TLS/SSL + IdPORT_ftps = 990; //ftp protocol, control, over TLS/SSL + IdPORT_nas = 991; //Netnews Administration System + IdPORT_TelnetS = 992; //telnet protocol over TLS/SSL + IdPORT_IMAP4S = 993; //imap4 protocol over TLS/SSL + IdPORT_IRCS = 994; // irc protocol over TLS/SSL + IdPORT_POP3S = 995; // POP3 protocol over TLS/SSL + IdPORT_vsinet = 996;//tcp vsinet + IdPORT_maitrd = 997; //tcp + IdPORT_busboy = 998;//tcp + IdPORT_puparp = 998;//udp + {There are port conflicts - beware} + IdPORT_garcon = 999;//tcp + IdPORT_applix = 999;//udp Applix ac + IdPORT_puprouter = 999;//tcp + IdPORT_cadlock2 = 1000;//tcp + IdPORT_ock = 1000; //udp + + ID_PORT_surf = 1010; //surf + + {These were added simply for compatibility and were not listed in RFC 1700} + IdPORT_SOCKS = 1080; + IdPORT_DICT = 2628; + IdPORT_IRC = 6667; + +const + {GSSAPI/Kerberos/SASL service names} + IdGSKSSN_rcmd = 'rcmd'; //remote command/rlogin/telnet [RFC1411] + IdGSKSSN_imap = 'imap'; //mailstore access/IMAP4 [RFC3501] + IdGSKSSN_pop = 'pop'; //maildrop access/POP3 [RFC-siemborski-rfc1734bis-11.txt] + IdGSKSSN_acap = 'acap'; //remote configuration access/ACAP [RFC2244] + IdGSKSSN_nfs = 'nfs'; //distributed file system protocol [RFC1813][RFC1094] + IdGSKSSN_ftp = 'ftp'; //file transfer/FTP/TFTP [RFC2228] + IdGSKSSN_ldap = 'ldap'; //Lightweight Directory Access Protocol (LDAP) [RFC2251][RFC2829] + IdGSKSSN_smtp = 'smtp'; //message transfer/SMTP [RFC4954] + IdGSKSSN_beep = 'beep'; //Blocks Extensible Exchange Protocol [RFC3080] + IdGSKSSN_mupdate = 'mupdate'; //Mailbox Update (MUPDATE) Protocol [RFC3656] + IdGSKSSN_sacred = 'sacred'; //Secure Available Credentials (SACRED) Protocol [RFC3767] + IdGSKSSN_xmpp = 'xmpp'; //Extensible Messaging and Presence Protocol (XMPP) [RFC3920] + IdGSKSSN_nntp = 'nntp'; //Network News Transfer Protocol (NNTP) [RFC4643] + +const + + Id_AIVN_Rserved = 0;// Reserved [JBP] + Id_AIVN_IP = 4; // IP Internet Protocol [RFC791,JBP] + Id_AIVN_ST = 5; // ST ST Datagram Mode [RFC1190,JWF] + Id_AIVN_SIP = 6; // SIP Simple Internet Protocol [RH6] + Id_AIVN_TP_IX = 7; // TP/IX TP/IX: The Next Internet [RXU] + Id_AIVN_PIP = 8; // PIP The P Internet Protocol [PXF] + Id_AIVN_Tuba = 9; // TUBA TUBA [RXC] + Id_AIVN_Reserved2 = 15; // Reserved + + +const + {Assigned Internet Protocol Numbers from RFC 1700} + Id_AIPN_Reserved = 0; + Id_AIPN_ICMP = 1; // ICMP Internet Control Message [RFC792,JBP] + Id_AIPN_IGMP = 2; // IGMP Internet Group Management [RFC1112,JBP] + Id_AIPN_GGP = 3; // GGP Gateway-to-Gateway [RFC823,MB] + Id_AIPN_IP = 4; // IP IP in IP (encasulation) [JBP] + Id_AIPN_ST = 5; // ST Stream [RFC1190,IEN119,JWF] + Id_AIPN_TCP = 6; // TCP Transmission Control [RFC793,JBP] + Id_AIPN_UCL = 7; // UCL UCL [PK] + Id_AIPN_EGP = 8; // EGP Exterior Gateway Protocol [RFC888,DLM1] + Id_AIPN_IGP = 9; // IGP any private interior gateway [JBP] + Id_AIPN_BBN_RCC_MON = 10; // BBN-RCC-MON BBN RCC Monitoring [SGC] + Id_AIPN_NVP_II = 11; // NVP-II Network Voice Protocol [RFC741,SC3] + Id_AIPN_PUP = 12; // PUP PUP [PUP,XEROX] + Id_AIPN_ARGUS = 13; // ARGUS ARGUS [RWS4] + Id_AIPN_EMCON = 14; // EMCON EMCON [BN7] + Id_AIPN_XNET = 15; // XNET Cross Net Debugger [IEN158,JFH2] + Id_AIPN_CHAOS = 16; // CHAOS Chaos [NC3] + Id_AIPN_UDP = 17; // UDP User Datagram [RFC768,JBP] + Id_AIPN_MUX = 18; // MUX Multiplexing [IEN90,JBP] + Id_AIPN_DCN_MEAS = 19; // DCN-MEAS DCN Measurement Subsystems [DLM1] + Id_AIPN_HMP = 20; // HMP Host Monitoring [RFC869,RH6] + Id_AIPN_PRM = 21; // PRM Packet Radio Measurement [ZSU] + Id_AIPN_XNS_IDP = 22; // XNS-IDP XEROX NS IDP [ETHERNET,XEROX] + Id_AIPN_TRUNK_1 = 23; // TRUNK-1 Trunk-1 [BWB6] + Id_AIPN_TRUNK_2 = 24; // TRUNK-2 Trunk-2 [BWB6] + Id_AIPN_LEAF_1 = 25; // LEAF-1 Leaf-1 [BWB6] + Id_AIPN_LEAF_2 = 26; // LEAF-2 Leaf-2 [BWB6] + Id_AIPN_RDP = 27; // RDP Reliable Data Protocol [RFC908,RH6] + Id_AIPN_IRTP = 28; // IRTP Internet Reliable Transaction [RFC938,TXM] + Id_AIPN_ISO_TP4 = 29; // ISO-TP4 ISO Transport Protocol Class 4 [RFC905,RC77] + Id_AIPN_NETBLT = 30; // NETBLT Bulk Data Transfer Protocol [RFC969,DDC1] + Id_AIPN_NFE_NSP = 31; // MFE-NSP MFE Network Services Protocol [MFENET,BCH2] + Id_AIPN_MERIT_IMP = 32; // MERIT-INP MERIT Internodal Protocol [HWB] + Id_AIPN_SEP = 33; // SEP Sequential Exchange Protocol [JC120] + Id_AIPN_3PC = 34; // 3PC Third Party Connect Protocol [SAF3] + Id_AIPN_IDPR = 35; // IDPR Inter-Domain Policy Routing Protocol [MXS1] + Id_AIPN_XTP = 36; // XTP XTP [GXC] + Id_AIPN_DDP = 37; // DDP Datagram Delivery Protocol [WXC] + Id_AIPN_IDPR_CMTP = 38; // IDPR-CMTP IDPR Control Message Transport Proto [MXS1] + Id_AIPN_TP_PLUS_PLUS = 39; // TP++ TP++ Transport Protocol [DXF] + Id_AIPN_IL = 40; // IL IL Transport Protocol [DXP2] + Id_AIPN_SIP = 41; // SIP Simple Internet Protocol [SXD] + Id_AIPN_SDRP = 42; // SDRP Source Demand Routing Protocol [DXE1] + Id_AIPN_SIP_SR = 43; // SIP-SR SIP Source Route [SXD] + Id_AIPN_SIP_FRAG = 44; // SIP-FRAG SIP Fragment [SXD] + Id_AIPN_IDRP = 45; // IDRP Inter-Domain Routing Protocol [Sue Hares] + Id_AIPN_RSVP = 46; // RSVP Reservation Protocol [Bob Braden] + Id_AIPN_GRE = 47; // GRE General Routing Encapsulation [Tony Li] + Id_AIPN_MHRP = 48; // MHRP Mobile Host Routing Protocol[David Johnson] + Id_AIPN_BNA = 49; // BNA BNA [Gary Salamon] + Id_AIPN_SIPP_ESB = 50; // SIPP-ESP SIPP Encap Security Payload [Steve Deering] + Id_AIPN_SIPP_AH = 51; // SIPP-AH SIPP Authentication Header [Steve Deering] + Id_AIPN_I_NLSP = 52; // I-NLSP Integrated Net Layer Security TUBA [GLENN] + Id_AIPN_SWIPE = 53; // SWIPE IP with Encryption [JI6] + Id_AIPN_NHRP = 54; // NHRP NBMA Next Hop Resolution Protocol + Id_AIPN_MOBILE = 55;// MOBILE IP Mobility [Perkins] + Id_AIPN_TLSP = 56;// TLSP Transport Layer Security Protocol [Oberg] + Id_AIPN_Kryptonet = 58;// using Kryptonet key management + Id_AIPN_SKIP = 57;// SKIP SKIP [Markson] + Id_AIPN_IPV6_ICMP = 58;// IPv6-ICMP ICMP for IPv6 [RFC1883] + Id_AIPN_IPV6_NO_NEXT = 59;// IPv6-NoNxt No Next Header for IPv6 [RFC1883] + Id_AIPN_IPV6_OPTS = 60;// IPv6-Opts Destination Options for IPv6 [RFC1883] + Id_AIPN_Any_Host_Internal = 61; // any host internal protocol [JBP] + Id_AIPN_CFTP = 62; // CFTP CFTP [CFTP,HCF2] + Id_AIPN_Any_LAN = 63; // any local network [JBP] + Id_AIPN_SAT_EXPACK = 64; // SAT-EXPAK SATNET and Backroom EXPAK [SHB] + Id_AIPN_KRYPTOLAN = 65; // KRYPTOLAN Kryptolan [PXL1] + Id_AIPN_RVD = 66; // RVD MIT Remote Virtual Disk Protocol [MBG] + Id_AIPN_IPPC = 67; // IPPC Internet Pluribus Packet Core [SHB] + Id_AIPN_Any_Distributed_File_System = 68; //any distributed file system [JBP] + Id_AIPN_SAT_MON = 69; // SAT-MON SATNET Monitoring [SHB] + Id_AIPN_VISA = 70;// VISA VISA Protocol [GXT1] + Id_AIPN_IPCV = 71;// IPCV Internet Packet Core Utility [SHB] + Id_AIPN_CPNX = 72; // CPNX Computer Protocol Network Executive [DXM2] + Id_AIPN_CPHB = 73; // CPHB Computer Protocol Heart Beat [DXM2] + Id_AIPN_WSM = 74; // WSN Wang Span Network [VXD] + Id_AIPN_PVP = 75; // PVP Packet Video Protocol [SC3] + Id_AIPN_BR_SAT_MON = 76; // BR-SAT-MON Backroom SATNET Monitoring [SHB] + Id_AIPN_SUN_ND = 77; // SUN-ND SUN ND PROTOCOL-Temporary [WM3] + Id_AIPN_WB_MON = 78; // WB-MON WIDEBAND Monitoring [SHB] + Id_AIPN_EXPAK = 79; // WB-EXPAK WIDEBAND EXPAK [SHB] + Id_AIPN_ISO_IP = 80; // ISO-IP ISO Internet Protocol [MTR] + Id_AIPN_VMTP = 81; // VMTP VMTP [DRC3] + Id_AIPN_SECURE_VMTP = 82; // SECURE-VMTP SECURE-VMTP [DRC3] + Id_AIPN_VINES = 83; // VINES VINES [BXH] + Id_AIPN_TTP = 84;// TTP TTP [JXS] + Id_AIPN_NSFNET_IGP = 85;// NSFNET-IGP NSFNET-IGP [HWB] + Id_AIPN_DGP = 86;// DGP Dissimilar Gateway Protocol [DGP,ML109] + Id_AIPN_TCF = 87; // TCF TCF [GAL5] + Id_AIPN_IGRP = 88;// IGRP IGRP [CISCO,GXS] + Id_AIPN_OSPFIGP = 89;// OSPFIGP OSPFIGP [RFC1583,JTM4] + Id_AIPN_Sprite_RPC = 90; // Sprite-RPC Sprite RPC Protocol [SPRITE,BXW] + Id_AIPN_LARP = 91; // LARP Locus Address Resolution Protocol [BXH] + Id_AIPN_MTP = 92;// MTP Multicast Transport Protocol [SXA] + Id_AIPN_AX_25 = 93;// AX.25 AX.25 Frames [BK29] + Id_AIPN_IPIP = 94;// IPIP IP-within-IP Encapsulation Protocol [JI6] + Id_AIPN_MICP = 95;// MICP Mobile Internetworking Control Pro. [JI6] + Id_AIPN_SCC_SP = 96;// SCC-SP Semaphore Communications Sec. Pro. [HXH] + Id_AIPN_ETHERIP = 97;// ETHERIP Ethernet-within-IP Encapsulation [RXH1] + Id_AIPN_ENCAP = 98; // ENCAP Encapsulation Header [RFC1241,RXB3] + Id_AIPN_Any_Private_Encryption = 99; //any private encryption scheme [JBP] + Id_AIPN_GMTP = 100;// GMTP GMTP [RXB5] + Id_AIPN_IFMP = 101;// IFMP Ipsilon Flow Management Protocol [Hinden] + Id_AIPN_PNNI = 102;// PNNI PNNI over IP [Callon] + Id_AIPN_PIM = 103;// PIM Protocol Independent Multicast [Farinacci] + Id_AIPN_ARIS = 104;// ARIS ARIS [Feldman] + Id_AIPN_SCPS = 105;// SCPS [Durst] + Id_AIPN_QNX = 106;// QNX QNX [Hunter] + Id_AIPN_A_N = 107; // A/N Active Networks [Braden] + Id_AIPN_IPComp = 108;// IPComp IP Payload Compression Protocol [RFC2393] + Id_AIPN_SNP = 109;// SNP Sitara Networks Protocol [Sridhar] + Id_AIPN_Compaq_Peer = 110;// Compaq-Peer Compaq Peer Protocol [Volpe] + Id_AIPN_IPX_In_IP = 111;// IPX-in-IP IPX in IP [Lee] + Id_AIPN_VRRP = 112; //VRRP Virtual Router Redundancy Protocol [Hinden] + Id_AIPN_PGM = 113; //PGM PGM Reliable Transport Protocol [Speakman] + Id_AIPN_0_HOP = 114;// any 0-hop protocol [IANA] + Id_AIPN_L2TP = 115;// L2TP Layer Two Tunneling Protocol [Aboba] + Id_AIPN_DDX = 116;// DDX D-II Data Exchange (DDX) [Worley] + Id_AIPN_IATP = 117;// IATP Interactive Agent Transfer Protocol [Murphy] + Id_AIPN_STP = 118;// STP Schedule Transfer Protocol [JMP] + Id_AIPN_SRP = 119;// SRP SpectraLink Radio Protocol [Hamilton] + Id_AIPN_UTI = 120;// UTI [Lothberg] + Id_AIPN_SMP = 121;// SMP Simple Message Protocol [Ekblad] + Id_AIPN_SM = 122;// SM SM [Crowcroft] + Id_AIPN_PTP = 123;// PTP Performance Transparency Protocol [Welzl] + Id_AIPN_ISIS = 124;//ISIS over IPv4 [Przygienda] + Id_AIPN_FIRE = 125;// FIRE [Partridge] + Id_AIPN_CRTP = 126;// CRTP Combat Radio Transport Protocol [Sautter] + Id_AIPN_CRUDP = 127; // CRUDP Combat Radio User Datagram [Sautter] + Id_AIPN_SSCOPMCE = 128;// SSCOPMCE [Waber] + Id_AIPN_IPLT = 129;// IPLT [Hollbach] + Id_AIPN_SPS = 130;// SPS Secure Packet Shield [McIntosh] + Id_AIPN_PIPE = 131;// PIPE Private IP Encapsulation within IP [Petri] + Id_AIPN_SCTP = 132;// SCTP Stream Control Transmission Protocol [Stewart] + Id_AIPN_FC = 133;// FC Fibre Channel [Rajagopal] + Id_AIPN_RSVP_E2E_IGNORE = 134;// RSVP-E2E-IGNORE [RFC3175] + Id_AIPN_Reserved2 = 255;// Reserved + + {Operating SYstem names} + Id_OS_Aegis = 'AEGIS'; {Do not Localize} + Id_OS_Amiga_1_2 ='AMIGA-OS-1.2'; {Do not Localize} + Id_OS_Amiga_1_3 = 'AMIGA-OS-1.3'; {Do not Localize} + Id_OS_Amiga_2_0 = 'AMIGA-OS-2.0'; {Do not Localize} + Id_OS_Amiga_2_1 = 'AMIGA-OS-2.1'; {Do not Localize} + Id_OS_Amiga_3_0 = 'AMIGA-OS-3.0'; {Do not Localize} + Id_OS_Amiga_3_1 = 'AMIGA-OS-3.1'; {Do not Localize} + Id_OS_Amiga_3_5 = 'AMIGA-OS-3.5'; {Do not Localize} + Id_OS_Amiga_3_9 = 'AMIGA-OS-3.9'; {Do not Localize} + Id_OS_Apollo = 'APOLLO'; {Do not Localize} + Id_OS_AIX_370 = 'AIX/370'; {Do not Localize} + Id_OS_AIX_PS2 = 'AIX-PS/2'; {Do not Localize} + Id_OS_BEOS_4_5_2 = 'BEOS-4.5.2'; {Do not Localize} + Id_OS_BEOS_5_0 ='BEOS-5.0'; {Do not Localize} + Id_OS_BS_2000 = 'BS-2000'; {Do not Localize} + Id_OS_Cedar = 'CEDAR'; {Do not Localize} + Id_OS_CGW = 'CGW'; {Do not Localize} + Id_OS_CHORUS = 'CHORUS'; {Do not Localize} + Id_OS_Chrysalis = 'CHRYSALIS'; {Do not Localize} + Id_OS_CMOS = 'CMOS'; {Do not Localize} + Id_OS_CMS ='CMS'; {Do not Localize} + Id_OS_COS = 'COS'; {Do not Localize} + Id_OS_CPIX = 'CPIX'; {Do not Localize} + Id_OS_CTOS = 'CTOS'; {Do not Localize} + Id_OS_CTSS = 'CTSS'; {Do not Localize} + Id_OS_DCN = 'DCN'; {Do not Localize} + Id_OS_DDNOS = 'DDNOS'; {Do not Localize} + Id_OS_DOMAIN = 'DOMAIN'; {Do not Localize} + Id_OS_DOS = 'DOS'; {Do not Localize} + Id_OS_EDX = 'EDX'; {Do not Localize} + Id_OS_ELF = 'ELF'; {Do not Localize} + Id_OS_EMBOS = 'EMBOS'; {Do not Localize} + Id_OS_EMMOS = 'EMMOS'; {Do not Localize} + Id_OS_EPOS = 'EPOS'; {Do not Localize} + Id_OS_FOONEX = 'FOONEX'; {Do not Localize} + Id_OS_FORTH = 'FORTH'; {Do not Localize} + Id_OS_FUZZ = 'FUZZ'; {Do not Localize} + Id_OS_GCOS = 'GCOS'; {Do not Localize} + Id_OS_GPOS = 'GPOS'; {Do not Localize} + Id_OS_HDOS = 'HDOS'; {Do not Localize} + Id_OS_Imagen = 'IMAGEN'; {Do not Localize} + Id_OS_Instant_Internet = 'INSTANT-INTERNET'; {Do not Localize} + Id_OS_Intercom = 'INTERCOM'; {Do not Localize} + Id_OS_Impress = 'IMPRESS'; {Do not Localize} + Id_OS_Interlisp = 'INTERLISP'; {Do not Localize} + Id_OS_IOS = 'IOS'; {Do not Localize} + Id_OS_IRIX = 'IRIX'; {Do not Localize} + Id_OS_ISI = 'ISI-68020'; {Do not Localize} + Id_OS_ITS = 'ITS'; {Do not Localize} + Id_OS_KOSOS = 'KOSOS'; {Do not Localize} + Id_OS_Linux = 'LINUX'; {Do not Localize} + Id_OS_Linux_1_0 = 'LINUX-1.0'; {Do not Localize} + Id_OS_Linux_1_2 = 'LINUX-1.2'; {Do not Localize} + Id_OS_Linux_2_0 = 'LINUX-2.0'; {Do not Localize} + Id_OS_Linux_2_2 = 'LINUX-2.2'; {Do not Localize} + + Id_OS_LISP = 'LISP'; {Do not Localize} + Id_OS_LISPM = 'LISPM'; {Do not Localize} + Id_OS_LOCUS = 'LOCUS'; {Do not Localize} + Id_OS_MACOS = 'MACOS'; {Do not Localize} + Id_OS_MINOS = 'MINOS'; {Do not Localize} + Id_OS_MOS = 'MOS'; {Do not Localize} + Id_OS_MPE5 = 'MPE5'; {Do not Localize} + Id_OS_MPEV = 'MPE/V'; {Do not Localize} + Id_OS_MPEIX = 'MPE/IX'; {Do not Localize} + Id_OS_MSDOS = 'MSDOS'; {Do not Localize} + Id_OS_MULTICS = 'MULTICS'; {Do not Localize} + Id_OS_MUSIC = 'MUSIC'; {Do not Localize} + Id_OS_MUSICSP = 'MUSIC/SP'; {Do not Localize} + Id_OS_MVS = 'MVS'; {Do not Localize} + Id_OS_MVSSP = 'MVS/SP'; {Do not Localize} + Id_NET_BSD_1_0 = 'NETBSD-1.0'; {Do not Localize} + Id_NET_BSD_1_1 = 'NETBSD-1.1'; {Do not Localize} + Id_NET_BSD_1_2 = 'NETBSD-1.2'; {Do not Localize} + Id_NET_BSD_1_3 = 'NETBSD-1.3'; {Do not Localize} + Id_NET_BSD_3_0 = 'NETWARE-3'; {Do not Localize} + Id_NET_BSD_3_11 = 'NETWARE-3.11'; {Do not Localize} + Id_NET_BSD_4_0 = 'NETWARE-4.0'; {Do not Localize} + Id_NET_BSD_4_1 = 'NETWARE-4.1'; {Do not Localize} + Id_NET_BSD_5_0 = 'NETWARE-5.0'; {Do not Localize} + + Id_OS_NEXUS = 'NEXUS'; {Do not Localize} + Id_OS_NMS = 'NMS'; {Do not Localize} + Id_OS_NONSTOP = 'NONSTOP'; {Do not Localize} + Id_OS_NOS_2 = 'NOS-2'; {Do not Localize} + Id_OS_NTOS = 'NTOS'; {Do not Localize} + Id_OS_OpenBSD = 'OPENBSD'; {Do not Localize} + Id_OS_OpenVMS = 'OPENVMS'; {Do not Localize} + Id_OS_OSDDP = 'OS/DDP'; {Do not Localize} + Id_OS_OS_2 = 'OS/2'; {Do not Localize} + Id_OS_OS_4 = 'OS4'; {Do not Localize} + Id_OS_OS_6 = 'OS86'; {Do not Localize} + Id_OS_OSX = 'OSX'; {Do not Localize} + Id_OS_PCDOS = 'PCDOS'; {Do not Localize} + Id_OS_PERQOS = 'PERQ/OS'; {Do not Localize} + Id_OS_PLI = 'PLI'; {Do not Localize} + Id_OS_PSDDOSMIT = 'PSDOS/MIT'; {Do not Localize} + Id_OS_Primos = 'PRIMOS'; {Do not Localize} + Id_OS_RISC_OS = 'RISC-OS'; {Do not Localize} + Id_OS_RISC_OS_3_10 = 'RISC-OS-3.10'; {Do not Localize} + Id_OS_RISC_OS_3_50 = 'RISC-OS-3.50'; {Do not Localize} + Id_OS_RISC_OS_3_60 = 'RISC-OS-3.60'; {Do not Localize} + Id_OS_RISC_OS_3_70 = 'RISC-OS-3.70'; {Do not Localize} + Id_OS_RISC_OS_4_00 = 'RISC-OS-4.00'; {Do not Localize} + + Id_OS_RMXRDOS = 'RMX/RDOS'; {Do not Localize} + Id_OS_ROS = 'ROS'; {Do not Localize} + Id_OS_RSX11M = 'RSX11M'; {Do not Localize} + Id_OS_RTE_A = 'RTE-A'; {Do not Localize} + Id_OS_Satops = 'SATOPS'; {Do not Localize} + Id_OS_Sinix = 'SINIX'; {Do not Localize} + Id_OS_SCO_Open_Desktop_1_0 = 'SCO-OPEN-DESKTOP-1.0'; {Do not Localize} + Id_OS_SCO_Open_Desktop_1_1 = 'SCO-OPEN-DESKTOP-1.1'; {Do not Localize} + Id_OS_SCO_Open_Desktop_2_0 = 'SCO-OPEN-DESKTOP-2.0'; {Do not Localize} + Id_OS_SCO_Open_Desktop_3_0 = 'SCO-OPEN-DESKTOP-3.0'; {Do not Localize} + Id_OS_SCO_Open_Desktop_Lite_3_0 = 'SCO-OPEN-DESKTOP-LITE-3.0'; {Do not Localize} + Id_OS_SCO_Open_Server_3_0 = 'SCO-OPEN-SERVER-3.0'; {Do not Localize} + Id_OS_SCO_Unix_3_2_0 = 'SCO-UNIX-3.2.0'; {Do not Localize} + Id_OS_SCO_Unix_3_2V2_0 = 'SCO-UNIX-3.2V2.0'; {Do not Localize} + Id_OS_SCO_Unix_3_2V1_0 = 'SCO-UNIX-3.2V2.1'; {Do not Localize} + Id_OS_SCO_Unix_S_2V4_0 = 'SCO-UNIX-3.2V4.0'; {Do not Localize} + Id_OS_SCO_Unix_3_2V4_1 = 'SCO-UNIX-3.2V4.1'; {Do not Localize} + Id_OS_SCO_Unix_3_2V4_2 = 'SCO-UNIX-3.2V4.2'; {Do not Localize} + Id_OS_SCO_Xenix_386_2_3_2 = 'SCO-XENIX-386-2.3.2'; {Do not Localize} + Id_OS_SCO_Xenix_386_2_3_3 = 'SCO-XENIX-386-2.3.3'; {Do not Localize} + Id_OS_SCO_Xenix_386_2_3_4 = 'SCO-XENIX-386-2.3.4'; {Do not Localize} + Id_OS_SCS = 'SCS'; {Do not Localize} + Id_OS_SIMP = 'SIMP'; {Do not Localize} + Id_OS_SUN = 'SUN'; {Do not Localize} + Id_OS_SUN_OS_3_5 = 'SUN-OS-3.5'; {Do not Localize} + Id_OS_SUN_OS_4_0 = 'SUN-OS-4.0'; {Do not Localize} + Id_OS_Swift = 'SWIFT'; {Do not Localize} + Id_OS_Tac = 'TAC'; {Do not Localize} + Id_OS_Tandem = 'TANDEM'; {Do not Localize} + Id_OS_Tenex = 'TENEX'; {Do not Localize} + Id_OS_The_Major_BBS = 'THE-MAJOR-BBS'; {Do not Localize} + Id_OS_Tops10 = 'TOPS10'; {Do not Localize} + Id_OS_Tops20 = 'TOPS20'; {Do not Localize} + Id_OS_TOS = 'TOS'; {Do not Localize} + Id_OS_TP3010 = 'TP3010'; {Do not Localize} + Id_OS_TRSDOS = 'TRSDOS'; {Do not Localize} + Id_OS_Ultrix = 'ULTRIX'; {Do not Localize} + Id_OS_Unix = 'UNIX'; {Do not Localize} + Id_OS_Unix_BSD = 'UNIX-BSD'; {Do not Localize} + Id_OS_Unix_V1AT = 'UNIX-V1AT'; {Do not Localize} + Id_OS_Unix_V = 'UNIX-V'; {Do not Localize} + Id_OS_Unix_V_1 = 'UNIX-V.1'; {Do not Localize} + Id_OS_Unix_V_2 = 'UNIX-V.2'; {Do not Localize} + Id_OS_Unix_V_3 = 'UNIX-V.3'; {Do not Localize} + Id_OS_Unix_PC = 'UNIX-PC'; {Do not Localize} + Id_OS_Unix_Unknown = 'UNKNOWN'; {Do not Localize} + Id_OS_UT2D = 'UT2D'; {Do not Localize} + Id_OS_V = 'V'; {Do not Localize} + Id_OS_VM = 'VM'; {Do not Localize} + Id_OS_VM_370 = 'VM/370'; {Do not Localize} + Id_OS_VM_CMS = 'VM/CMS'; {Do not Localize} + Id_OS_VM_SP = 'VM/SP'; {Do not Localize} + Id_OS_VMS = 'VMS'; {Do not Localize} + Id_OS_VMS_Eunice = 'VMS/EUNICE'; {Do not Localize} + Id_OS_VRTX = 'VRTX'; {Do not Localize} + Id_OS_Waits = 'WAITS'; {Do not Localize} + Id_OS_Wang = 'WANG'; {Do not Localize} + Id_OS_Win32 = 'WIN32'; {Do not Localize} + Id_OS_Windows_95 = 'WINDOWS-95'; {Do not Localize} + Id_OS_Windows_95OSR1 = 'WINDOWS-95-OSR1'; {Do not Localize} + Id_OS_Windows_95OSR2 = 'WINDOWS-95-OSR2'; {Do not Localize} + Id_OS_Windows_98 = 'WINDOWS-98'; {Do not Localize} + Id_OS_Windows_CE = 'WINDOWS-CE'; {Do not Localize} + Id_OS_Windows_NT = 'WINDOWS-NT'; {Do not Localize} + Id_OS_Windows_NT_2 = 'WINDOWS-NT-2'; {Do not Localize} + Id_OS_Windows_NT_3 = 'WINDOWS-NT-3'; {Do not Localize} + Id_OS_Windows_NT_3_5 = 'WINDOWS-NT-3.5'; {Do not Localize} + Id_OS_Windows_NT_3_51 = 'WINDOWS-NT-3.51'; {Do not Localize} + Id_OS_Windows_NT_4 = 'WINDOWS-NT-4'; {Do not Localize} + Id_OS_Windows_NT_5 = 'WINDOWS-NT-5'; {Do not Localize} + Id_OS_WorldGroup = 'WORLDGROUP'; {Do not Localize} + Id_OS_Wyse_Wyxware = 'WYSE-WYXWARE'; {Do not Localize} + Id_OS_X11R3 = 'X11R3'; {Do not Localize} + Id_OS_XDE = 'XDE'; {Do not Localize} + Id_OS_Xenix = 'XENIX'; {Do not Localize} + +const +{ Machine Names } + + Id_MN_Amiga_500 = 'AMIGA-500'; {Do not Localize} + Id_MN_Amiga_500_010 = 'AMIGA-500/010'; {Do not Localize} + Id_MN_Amiga_500_020 = 'AMIGA-500/020'; {Do not Localize} + Id_MN_Amiga_500_EC030 = 'AMIGA-500/EC030'; {Do not Localize} + Id_MN_Amiga_500_030 = 'AMIGA-500/030'; {Do not Localize} + Id_MN_Amiga_600 = 'AMIGA-600'; {Do not Localize} + Id_MN_Amiga_1000 = 'AMIGA-1000'; {Do not Localize} + Id_MN_Amiga_1000_010 = 'AMIGA-1000/010'; {Do not Localize} + Id_MN_Amiga_1000_020 = 'AMIGA-1000/020'; {Do not Localize} + Id_MN_Amiga_1000_EC030 = 'AMIGA-1000/EC030'; {Do not Localize} + Id_MN_Amiga_1000_030 = 'AMIGA-1000/030'; {Do not Localize} + Id_MN_Amiga_1200 = 'AMIGA-1200'; {Do not Localize} + Id_MN_Amiga_1200_EC030 = 'AMIGA-1200/EC030'; {Do not Localize} + Id_MN_Amiga_1200_030 = 'AMIGA-1200/030'; {Do not Localize} + Id_MN_Amiga_1200_EC040 = 'AMIGA-1200/EC040'; {Do not Localize} + Id_MN_Amiga_1200_LC040 = 'AMIGA-1200/LC040'; {Do not Localize} + Id_MN_Amiga_1200_040 = 'AMIGA-1200/040'; {Do not Localize} + Id_MN_Amiga_2000 = 'AMIGA-2000'; {Do not Localize} + Id_MN_Amiga_2000_010 = 'AMIGA-2000/010'; {Do not Localize} + Id_MN_Amiga_2000_020 = 'AMIGA-2000/020'; {Do not Localize} + Id_MN_Amiga_2000_EC030 = 'AMIGA-2000/EC030'; {Do not Localize} + Id_MN_Amiga_2000_030 = 'AMIGA-2000/030'; {Do not Localize} + Id_MN_Amiga_2000_LC040 = 'AMIGA-2000/LC040'; {Do not Localize} + Id_MN_Amiga_2000_EC040 = 'AMIGA-2000/EC040'; {Do not Localize} + Id_MN_Amiga_2000_040 = 'AMIGA-2000/040'; {Do not Localize} + Id_MN_Amiga_3000 = 'AMIGA-3000'; {Do not Localize} + Id_MN_Amiga_3000_EC040 = 'AMIGA-3000/EC040'; {Do not Localize} + Id_MN_Amiga_3000_LC040 = 'AMIGA-3000/LC040'; {Do not Localize} + Id_MN_Amiga_3000_040 = 'AMIGA-3000/040'; {Do not Localize} + Id_MN_Amiga_3000_060 = 'AMIGA-3000/060'; {Do not Localize} + Id_MN_Amiga_4000_EC030 = 'AMIGA-4000/EC030'; {Do not Localize} + Id_MN_Amiga_4000_030 = 'AMIGA-4000/030'; {Do not Localize} + Id_MN_Amiga_4000_LC040 = 'AMIGA-4000/LC040'; {Do not Localize} + Id_MN_Amiga_4000_040 = 'AMIGA-4000/040'; {Do not Localize} + Id_MN_Amiga_4000_060 = 'AMIGA-4000/060'; {Do not Localize} + Id_MN_Alto = 'ALTO'; {Do not Localize} + Id_MN_Altos_6800 = 'ALTOS-6800'; {Do not Localize} + Id_MN_Amdahl_V7 = 'AMDAHL-V7'; {Do not Localize} + Id_MN_Apollo = 'APOLLO'; {Do not Localize} + Id_MN_Apple_Macintosh = 'APPLE-MACINTOSH'; {Do not Localize} + Id_MN_Apple_Powerbook = 'APPLE-POWERBOOK'; {Do not Localize} + Id_MN_Atari_104ST = 'ATARI-104ST'; {Do not Localize} + Id_MN_ATT_3B1 = 'ATT-3B1'; {Do not Localize} + Id_MN_ATT_3B2 = 'ATT-3B2'; {Do not Localize} + Id_MN_ATT_3B20 = 'ATT-3B20'; {Do not Localize} + Id_MN_ATT_7300 = 'ATT-7300'; {Do not Localize} + Id_MN_AXP = 'AXP'; {Do not Localize} + Id_MN_BBN_C_60 = 'BBN-C/60'; {Do not Localize} + Id_MN_Burroughs_B_29 = 'BURROUGHS-B/29'; {Do not Localize} + Id_MN_Burroughs_B_4800 = 'BURROUGHS-B/4800'; {Do not Localize} + Id_MN_Butterfly = 'BUTTERFLY'; {Do not Localize} + Id_MN_C_30 = 'C/30'; {Do not Localize} + Id_MN_C_70 = 'C/70'; {Do not Localize} + Id_MN_Cadlinc = 'CADLINC'; {Do not Localize} + Id_MN_CadR = 'CADR'; {Do not Localize} + Id_MN_CDC_170 = 'CDC-170'; {Do not Localize} + Id_MN_CDC_170_750 = 'CDC-170/750'; {Do not Localize} + Id_MN_CDC_173 = 'CDC-173'; {Do not Localize} + Id_MN_CDTV = 'CDTV'; {Do not Localize} + Id_MN_CDTV_060 = 'CDTV/060'; {Do not Localize} + Id_MN_CD32 = 'CD32'; {Do not Localize} + Id_MN_Celerity_1200 = 'CELERITY-1200'; {Do not Localize} + Id_MN_Club_386 = 'CLUB-386'; {Do not Localize} + Id_MN_Compaq_386_20 = 'COMPAQ-386/20'; {Do not Localize} + Id_MN_Comten_3690 = 'COMTEN-3690'; {Do not Localize} + Id_MN_CP8040 = 'CP8040'; {Do not Localize} + Id_MN_Cray_1 = 'CRAY-1'; {Do not Localize} + Id_MN_Cray_X_MP = 'CRAY-X/MP'; {Do not Localize} + Id_MN_Cray_2 = 'CRAY-2'; {Do not Localize} + Id_MN_CTIWS_117 = 'CTIWS-117'; {Do not Localize} + Id_MN_Dandelion = 'DANDELION'; {Do not Localize} + Id_MN_DEC_10 = 'DEC-10'; {Do not Localize} + Id_MN_DEC_1050 = 'DEC-1050'; {Do not Localize} + Id_MN_DEC_1077 = 'DEC-1077'; {Do not Localize} + Id_MN_DEC_1080 = 'DEC-1080'; {Do not Localize} + Id_MN_DEC_1090 = 'DEC-1090'; {Do not Localize} + Id_MN_DEC_1090B = 'DEC-1090B'; {Do not Localize} + Id_MN_DEC_1090T = 'DEC-1090T'; {Do not Localize} + Id_MN_DEC_2020T = 'DEC-2020T'; {Do not Localize} + Id_MN_DEC_2040 = 'DEC-2040'; {Do not Localize} + Id_MN_DEC_2040T = 'DEC-2040T'; {Do not Localize} + Id_MN_DEC_2050T = 'DEC-2050T'; {Do not Localize} + Id_MN_DEC_2060 = 'DEC-2060'; {Do not Localize} + Id_MN_DEC_2060T = 'DEC-2060T'; {Do not Localize} + Id_MN_DEC_2065 = 'DEC-2065'; {Do not Localize} + Id_MN_DEC_AXP = 'DEC-AXP'; {Do not Localize} + Id_MN_DEC_Falcon = 'DEC-FALCON'; {Do not Localize} + Id_MN_DEC_KS10 = 'DEC-KS10'; {Do not Localize} + Id_MN_DECStation = 'DECSTATION'; {Do not Localize} + Id_MN_DEC_VAX = 'DEC-VAX'; {Do not Localize} + Id_MN_DEC_VAXCluster = 'DEC-VAXCLUSTER'; {Do not Localize} + Id_MN_DEC_VAXStation = 'DEC-VAXSTATION'; {Do not Localize} + Id_MN_DEC_VAX_11730 = 'DEC-VAX-11730'; {Do not Localize} + Id_MN_Dorado = 'DORADO'; {Do not Localize} + Id_MN_DPS8_70M = 'DPS8/70M'; {Do not Localize} + Id_MN_Elxsi_6400 = 'ELXSI-6400'; {Do not Localize} + Id_MN_EverEx_386 = 'EVEREX-386'; {Do not Localize} + Id_MN_Foonly_F2 = 'FOONLY-F2'; {Do not Localize} + Id_MN_Foonly_F3 = 'FOONLY-F3'; {Do not Localize} + Id_MN_Foonly_F4 = 'FOONLY-F4'; {Do not Localize} + Id_MN_Gould = 'GOULD'; {Do not Localize} + Id_MN_Gould_6050 = 'GOULD-6050'; {Do not Localize} + Id_MN_Gould_6080 = 'GOULD-6080'; {Do not Localize} + Id_MN_Gould_9050 = 'GOULD-9050'; {Do not Localize} + Id_MN_Gould_9080 = 'GOULD-9080'; {Do not Localize} + Id_MN_H_316 = 'H-316'; {Do not Localize} + Id_MN_H_60_68 = 'H-60/68'; {Do not Localize} + Id_MN_H_68 = 'H-68'; {Do not Localize} + Id_MN_H_68_80 = 'H-68/80'; {Do not Localize} + Id_MN_H_89 = 'H-89'; {Do not Localize} + Id_MN_Honeywell_DPS_6 = 'HONEYWELL-DPS-6'; {Do not Localize} + Id_MN_Honeywell_BPS_8_70 = 'HONEYWELL-DPS-8/70'; {Do not Localize} + Id_MN_HP3000 = 'HP3000'; {Do not Localize} + Id_MN_HP3000_64 = 'HP3000/64'; {Do not Localize} + Id_MN_IBM_158 = 'IBM-158'; {Do not Localize} + Id_MN_IBM_360_67 = 'IBM-360/67'; {Do not Localize} + Id_MN_IBM_370_3033 = 'IBM-370/3033'; {Do not Localize} + Id_MN_IBM_3081 = 'IBM-3081'; {Do not Localize} + Id_MN_IBM_3084QX = 'IBM-3084QX'; {Do not Localize} + Id_MN_IBM_3101 = 'IBM-3101'; {Do not Localize} + Id_MN_IBM_4331 = 'IBM-4331'; {Do not Localize} + Id_MN_IBM_4341 = 'IBM-4341'; {Do not Localize} + Id_MN_IBM_4361 = 'IBM-4361'; {Do not Localize} + Id_MN_IBM_4381 = 'IBM-4381'; {Do not Localize} + Id_MN_IBM_4956 = 'IBM-4956'; {Do not Localize} + Id_MN_IBM_6152 = 'IBM-6152'; {Do not Localize} + Id_MN_IBM_PC = 'IBM-PC'; {Do not Localize} + Id_MN_IBM_PC_AT = 'IBM-PC/AT'; {Do not Localize} + Id_MN_IBM_PC_RT = 'IBM-PC/RT'; {Do not Localize} + Id_MN_IBM_PC_XT = 'IBM-PC/XT'; {Do not Localize} + Id_MN_IBM_RS_6000 = 'IBM-RS/6000'; {Do not Localize} + Id_MN_IBM_Series_1 = 'IBM-SERIES/1'; {Do not Localize} + + Id_MN_Imagen = 'IMAGEN'; {Do not Localize} + Id_MN_Imagen_8_300 = 'IMAGEN-8/300'; {Do not Localize} + Id_MN_Imsai = 'IMSAI'; {Do not Localize} + Id_MN_Integrated_Solutions = 'INTEGRATED-SOLUTIONS'; {Do not Localize} + Id_MN_Integrated_Solutions_68K = 'INTEGRATED-SOLUTIONS-68K'; {Do not Localize} + Id_MN_Integrated_Solutions_Creator = 'INTEGRATED-SOLUTIONS-CREATOR'; {Do not Localize} + Id_MN_Integrated_Solutions_Creator_8 = 'INTEGRATED-SOLUTIONS-CREATOR-8'; {Do not Localize} + Id_MN_INTEL_386 = 'INTEL-386'; {Do not Localize} + Id_MN_INTEL_IPSC = 'INTEL-IPSC'; {Do not Localize} + Id_MN_Is_1 = 'IS-1'; {Do not Localize} + Id_MN_Is_68010 = 'IS-68010'; {Do not Localize} + Id_MN_LMI = 'LMI'; {Do not Localize} + Id_MN_LSI_11 = 'LSI-11'; {Do not Localize} + Id_MN_LSI_11_2 = 'LSI-11/2'; {Do not Localize} + Id_MN_LSI_11_23 = 'LSI-11/23'; {Do not Localize} + Id_MN_LSI_11_73 = 'LSI-11/73'; {Do not Localize} + Id_MN_M68000 = 'M68000'; {Do not Localize} + Id_MN_Mac_II = 'MAC-II'; {Do not Localize} + Id_MN_Mac_Powerbook = 'MAC-POWERBOOK'; {Do not Localize} + Id_MN_MacIntosh = 'MACINTOSH'; {Do not Localize} + Id_MN_MassComp = 'MASSCOMP'; {Do not Localize} + Id_MN_MC500 = 'MC500'; {Do not Localize} + Id_MN_68000 = 'MC68000'; {Do not Localize} + Id_MN_Microport = 'MICROPORT'; {Do not Localize} + Id_MN_MicroVAX = 'MICROVAX'; {Do not Localize} + Id_MN_MicroVAX_I = 'MICROVAX-I'; {Do not Localize} + Id_MN_MV_8000 = 'MV/8000'; {Do not Localize} + Id_MN_NAS3_5 = 'NAS3-5'; {Do not Localize} + Id_MN_NCR_Comten_3690 = 'NCR-COMTEN-3690'; {Do not Localize} + Id_MN_Next_N1000_316 = 'NEXT/N1000-316'; {Do not Localize} + Id_MN_Now = 'NOW'; {Do not Localize} + Id_MN_Onyx_Z8000 = 'ONYX-Z8000'; {Do not Localize} + + Id_MN_PDP_11 = 'PDP-11'; {Do not Localize} + Id_MN_PDP_11_3 = 'PDP-11/3'; {Do not Localize} + Id_MN_PDP_11_23 = 'PDP-11/23'; {Do not Localize} + Id_MN_PDP_11_24 = 'PDP-11/24'; {Do not Localize} + Id_MN_PDP_11_34 = 'PDP-11/34'; {Do not Localize} + Id_MN_PDP_11_40 = 'PDP-11/40'; {Do not Localize} + Id_MN_PDP_11_44 = 'PDP-11/44'; {Do not Localize} + Id_MN_PDP_11_45 = 'PDP-11/45'; {Do not Localize} + Id_MN_PDP_11_50 = 'PDP-11/50'; {Do not Localize} + Id_MN_PDP_11_70 = 'PDP-11/70'; {Do not Localize} + Id_MN_PDP_11_73 = 'PDP-11/73'; {Do not Localize} + Id_MN_PE_7_32 = 'PE-7/32'; {Do not Localize} + Id_MN_PE_3205 = 'PE-3205'; {Do not Localize} + Id_MN_PE_Perq = 'PERQ'; {Do not Localize} + Id_MN_Plexus_P_60 = 'PLEXUS-P/60'; {Do not Localize} + Id_MN_PLI = 'PLI'; {Do not Localize} + + Id_MN_Pluribus = 'PLURIBUS'; {Do not Localize} + Id_MN_Prime_2350 = 'PRIME-2350'; {Do not Localize} + Id_MN_Prime_2450 = 'PRIME-2450'; {Do not Localize} + Id_MN_Prime_2755 = 'PRIME-2755'; {Do not Localize} + Id_MN_Prime_9655 = 'PRIME-9655'; {Do not Localize} + Id_MN_Prime_9755 = 'PRIME-9755'; {Do not Localize} + Id_MN_Prime_9955II = 'PRIME-9955II'; {Do not Localize} + Id_MN_Prime_2250 = 'PRIME-2250'; {Do not Localize} + Id_MN_Prime_2655 = 'PRIME-2655'; {Do not Localize} + Id_MN_Prime_9955 = 'PRIME-9955'; {Do not Localize} + Id_MN_Prime_9950 = 'PRIME-9950'; {Do not Localize} + Id_MN_Prime_9650 = 'PRIME-9650'; {Do not Localize} + Id_MN_Prime_9750 = 'PRIME-9750'; {Do not Localize} + Id_MN_Prime_750 = 'PRIME-750'; {Do not Localize} + Id_MN_Prime_850 = 'PRIME-850'; {Do not Localize} + Id_MN_Prime_550II = 'PRIME-550II'; {Do not Localize} + Id_MN_Pyramid_90 = 'PYRAMID-90'; {Do not Localize} + Id_MN_Pyramid_90MX = 'PYRAMID-90MX'; {Do not Localize} + Id_MN_Pyramid_90X = 'PYRAMID-90X'; {Do not Localize} + Id_MN_Ridge = 'RIDGE'; {Do not Localize} + Id_MN_Ridge_32 = 'RIDGE-32'; {Do not Localize} + Id_MN_Ridge_32C = 'RIDGE-32C'; {Do not Localize} + Id_MN_ROLM_1666 = 'ROLM-1666'; {Do not Localize} + Id_MN_RS_6000 = 'RS/6000'; {Do not Localize} + Id_MN_S1_MKIIA = 'S1-MKIIA'; {Do not Localize} + Id_MN_SMI = 'SMI'; {Do not Localize} + Id_MN_Sequent_Balance_8000 = 'SEQUENT-BALANCE-8000'; {Do not Localize} + Id_MN_Emens = 'SIEMENS'; {Do not Localize} + Id_MN_Silicon_Graphics = 'SILICON-GRAPHICS'; {Do not Localize} + Id_MN_Silicon_Graphics_Iris = 'SILICON-GRAPHICS-IRIS'; {Do not Localize} + + Id_MN_SGI_Iris_2400 = 'SGI-IRIS-2400'; {Do not Localize} + Id_MN_SGI_Iris_2500 = 'SGI-IRIS-2500'; {Do not Localize} + Id_MN_SGI_Iris_3010 = 'SGI-IRIS-3010'; {Do not Localize} + Id_MN_SGI_Iris_3020 = 'SGI-IRIS-3020'; {Do not Localize} + Id_MN_SGI_Iris_3030 = 'SGI-IRIS-3030'; {Do not Localize} + Id_MN_SGI_Iris_3110 = 'SGI-IRIS-3110'; {Do not Localize} + Id_MN_SGI_Iris_3115 = 'SGI-IRIS-3115'; {Do not Localize} + Id_MN_SGI_Iris_3120 = 'SGI-IRIS-3120'; {Do not Localize} + Id_MN_SGI_Iris_3130 = 'SGI-IRIS-3130'; {Do not Localize} + Id_MN_SGI_Iris_4D_20 = 'SGI-IRIS-4D/20'; {Do not Localize} + Id_MN_SGI_Iris_4D_20G = 'SGI-IRIS-4D/20G'; {Do not Localize} + Id_MN_SGI_Iris_4D_25 = 'SGI-IRIS-4D/25'; {Do not Localize} + Id_MN_SGI_Iris_4D_25G = 'SGI-IRIS-4D/25G'; {Do not Localize} + Id_MN_SGI_Iris_4D_25S = 'SGI-IRIS-4D/25S'; {Do not Localize} + Id_MN_SGI_Iris_4D_50 = 'SGI-IRIS-4D/50'; {Do not Localize} + Id_MN_SGI_Iris_4D_50G = 'SGI-IRIS-4D/50G'; {Do not Localize} + Id_MN_SGI_Iris_4D_50GT = 'SGI-IRIS-4D/50GT'; {Do not Localize} + Id_MN_SGI_Iris_4D_60 = 'SGI-IRIS-4D/60'; {Do not Localize} + Id_MN_SGI_Iris_4D_60G = 'SGI-IRIS-4D/60G'; {Do not Localize} + Id_MN_SGI_Iris_4D_60T = 'SGI-IRIS-4D/60T'; {Do not Localize} + Id_MN_SGI_Iris_4D_60GT = 'SGI-IRIS-4D/60GT'; {Do not Localize} + Id_MN_SGI_Iris_4D_70 = 'SGI-IRIS-4D/70'; {Do not Localize} + Id_MN_SGI_Iris_4D_70G = 'SGI-IRIS-4D/70G'; {Do not Localize} + Id_MN_SGI_Iris_4D_70GT = 'SGI-IRIS-4D/70GT'; {Do not Localize} + Id_MN_SGI_Iris_4D_80GT = 'SGI-IRIS-4D/80GT'; {Do not Localize} + Id_MN_SGI_Iris_4D_80S = 'SGI-IRIS-4D/80S'; {Do not Localize} + Id_MN_SGI_Iris_4D_120GTX = 'SGI-IRIS-4D/120GTX'; {Do not Localize} + Id_MN_SGI_Iris_4D_120S = 'SGI-IRIS-4D/120S'; {Do not Localize} + Id_MN_SGI_Iris_4D_210GTX = 'SGI-IRIS-4D/210GTX'; {Do not Localize} + Id_MN_SGI_Iris_4D_210S = 'SGI-IRIS-4D/210S'; {Do not Localize} + Id_MN_SGI_Iris_4D_220GTX = 'SGI-IRIS-4D/220GTX'; {Do not Localize} + Id_MN_SGI_Iris_4D_220S = 'SGI-IRIS-4D/220S'; {Do not Localize} + Id_MN_SGI_Iris_4D_240GTX = 'SGI-IRIS-4D/240GTX'; {Do not Localize} + Id_MN_SGI_Iris_4D_240S = 'SGI-IRIS-4D/240S'; {Do not Localize} + Id_MN_SGI_Iris_4D_280GTX = 'SGI-IRIS-4D/280GTX'; {Do not Localize} + Id_MN_SGI_Iris_4D_280S = 'SGI-IRIS-4D/280S'; {Do not Localize} + Id_MN_SGI_Iris_CS_12 = 'SGI-IRIS-CS/12'; {Do not Localize} + Id_MN_SGI_Iris_4Server_8 = 'SGI-IRIS-4SERVER-8'; {Do not Localize} + Id_MN_Sperry_DCP_10 = 'SPERRY-DCP/10'; {Do not Localize} + Id_MN_Sun = 'SUN'; {Do not Localize} + Id_MN_Sun_2 = 'SUN-2'; {Do not Localize} + Id_MN_Sun_2_50 = 'SUN-2/50'; {Do not Localize} + Id_MN_Sun_2_100 = 'SUN-2/100'; {Do not Localize} + Id_MN_Sun_2_120 = 'SUN-2/120'; {Do not Localize} + Id_MN_Sun_2_130 = 'SUN-2/130'; {Do not Localize} + Id_MN_Sun_2_140 = 'SUN-2/140'; {Do not Localize} + Id_MN_Sun_2_150 = 'SUN-2/150'; {Do not Localize} + Id_MN_Sun_2_160 = 'SUN-2/160'; {Do not Localize} + Id_MN_Sun_2_170 = 'SUN-2/170'; {Do not Localize} + Id_MN_Sun_3_50 = 'SUN-3/50'; {Do not Localize} + Id_MN_Sun_3_60 = 'SUN-3/60'; {Do not Localize} + Id_MN_Sun_3_75 = 'SUN-3/75'; {Do not Localize} + Id_MN_Sun_3_80 = 'SUN-3/80'; {Do not Localize} + Id_MN_Sun_3_110 = 'SUN-3/110'; {Do not Localize} + Id_MN_Sun_3_140 = 'SUN-3/140'; {Do not Localize} + Id_MN_Sun_3_150 = 'SUN-3/150'; {Do not Localize} + Id_MN_Sun_3_160 = 'SUN-3/160'; {Do not Localize} + Id_MN_Sun_3_180 = 'SUN-3/180'; {Do not Localize} + Id_MN_Sun_3_200 = 'SUN-3/200'; {Do not Localize} + Id_MN_Sun_3_260 = 'SUN-3/260'; {Do not Localize} + Id_MN_Sun_3_280 = 'SUN-3/280'; {Do not Localize} + Id_MN_Sun_3_470 = 'SUN-3/470'; {Do not Localize} + Id_MN_Sun_3_480 = 'SUN-3/480'; {Do not Localize} + Id_MN_Sun_4_60 = 'SUN-4/60'; {Do not Localize} + Id_MN_Sun_4_110 = 'SUN-4/110'; {Do not Localize} + Id_MN_Sun_4_150 = 'SUN-4/150'; {Do not Localize} + Id_MN_Sun_4_200 = 'SUN-4/200'; {Do not Localize} + Id_MN_Sun_4_260 = 'SUN-4/260'; {Do not Localize} + Id_MN_Sun_4_280 = 'SUN-4/280'; {Do not Localize} + Id_MN_Sun_4_330 = 'SUN-4/330'; {Do not Localize} + Id_MN_Sun_4_370 = 'SUN-4/370'; {Do not Localize} + Id_MN_Sun_4_390 = 'SUN-4/390'; {Do not Localize} + Id_MN_Sun_50 = 'SUN-50'; {Do not Localize} + Id_MN_Sun_100 = 'SUN-100'; {Do not Localize} + Id_MN_Sun_120 = 'SUN-120'; {Do not Localize} + Id_MN_Sun_130 = 'SUN-130'; {Do not Localize} + Id_MN_Sun_150 = 'SUN-150'; {Do not Localize} + Id_MN_Sun_170 = 'SUN-170'; {Do not Localize} + Id_MN_Sun_386i_250 = 'SUN-386i/250'; {Do not Localize} + Id_MN_Sun_68000 = 'SUN-68000'; {Do not Localize} + Id_MN_Symbolics_3600 = 'SYMBOLICS-3600'; {Do not Localize} + Id_MN_Symbolics_3670 = 'SYMBOLICS-3670'; {Do not Localize} + Id_MN_Symmetric_375 = 'SYMMETRIC-375'; {Do not Localize} + Id_MN_Symult = 'SYMULT'; {Do not Localize} + Id_MN_Tandem_TXP = 'TANDEM-TXP'; {Do not Localize} + Id_MN_Tandy_6000 = 'TANDY-6000'; {Do not Localize} + Id_MN_Tek_6130 = 'TEK-6130'; {Do not Localize} + Id_MN_TI_Explorer = 'TI-EXPLORER'; {Do not Localize} + Id_MN_TP_4000 = 'TP-4000'; {Do not Localize} + Id_MN_TRS_80 = 'TRS-80'; {Do not Localize} + Id_MN_Univac_1100 = 'UNIVAC-1100'; {Do not Localize} + Id_MN_Univac_1100_60 = 'UNIVAC-1100/60'; {Do not Localize} + Id_MN_Univac_1100_62 = 'UNIVAC-1100/62'; {Do not Localize} + Id_MN_Univac_1100_63 = 'UNIVAC-1100/63'; {Do not Localize} + Id_MN_Univac_1100_64 = 'UNIVAC-1100/64'; {Do not Localize} + Id_MN_Univac_1100_70 = 'UNIVAC-1100/70'; {Do not Localize} + Id_MN_Univac_1160 = 'UNIVAC-1160'; {Do not Localize} + Id_MN_Unknown = 'UNKNOWN'; {Do not Localize} + Id_MN_VAX = 'VAX'; {Do not Localize} + Id_MN_VAX_11_725 = 'VAX-11/725'; {Do not Localize} + Id_MN_VAX_11_730 = 'VAX-11/730'; {Do not Localize} + Id_MN_VAX_11_750 = 'VAX-11/750'; {Do not Localize} + Id_MN_VAX_11_780 = 'VAX-11/780'; {Do not Localize} + Id_MN_VAX_11_785 = 'VAX-11/785'; {Do not Localize} + Id_MN_VAX_11_790 = 'VAX-11/790'; {Do not Localize} + Id_MN_VAX_11_8600 = 'VAX-11/8600'; {Do not Localize} + Id_MN_VAX_8600 = 'VAX-8600'; {Do not Localize} + Id_MN_VAXCluster = 'VAXCLUSTER'; {Do not Localize} + Id_MN_VAXStation = 'VAXSTATION'; {Do not Localize} + Id_MN_Wang_PC002 = 'WANG-PC002'; {Do not Localize} + Id_MN_Wang_VS100 = 'WANG-VS100'; {Do not Localize} + Id_MN_Wang_VS400 = 'WANG-VS400'; {Do not Localize} + Id_MN_Wyse_386 = 'WYSE-386'; {Do not Localize} + Id_MN_Wyse_WN5004 = 'WYSE-WN5004'; {Do not Localize} + Id_MN_Wyse_WN5008 = 'WYSE-WN5008'; {Do not Localize} + Id_MN_Wyse_WN5104 = 'WYSE-WN5104'; {Do not Localize} + Id_MN_Wyse_WN5108 = 'WYSE-WN5108'; {Do not Localize} + Id_MN_Wyse_WX15C = 'WYSE-WX15C'; {Do not Localize} + Id_MN_Wyse_WX17C = 'WYSE-WX17C'; {Do not Localize} + Id_MN_Wyse_WX17M = 'WYSE-WX17M'; {Do not Localize} + Id_MN_Wyse_WX19C = 'WYSE-WX19C'; {Do not Localize} + Id_MN_Wyse_WX19M = 'WYSE-WX19M'; {Do not Localize} + Id_MN_Wyse_WYX14M = 'WYSE-WYX14M'; {Do not Localize} + Id_MN_Wyse_WYX5 = 'WYSE-WYX5'; {Do not Localize} + Id_MN_Xerox_1108 = 'XEROX-1108'; {Do not Localize} + Id_MN_Xerox_8010 = 'XEROX-8010'; {Do not Localize} + Id_MN_Zenith_148 = 'ZENITH-148'; {Do not Localize} + +const + Id_CS_US_ASCII = 'US-ASCII'; // see ANSI_X3.4-1968 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_1 = 'ISO-8859-1'; // see ISO_8859-1:1987 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_2 = 'ISO-8859-2'; // see ISO_8859-2:1987 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_3 = 'ISO-8859-3'; // see ISO_8859-3:1988 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_4 = 'ISO-8859-4'; // see ISO_8859-4:1988 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_5 = 'ISO-8859-5'; // see ISO_8859-5:1988 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_6 = 'ISO-8859-6'; // see ISO_8859-6:1987 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_7 = 'ISO-8859-7'; // see ISO_8859-7:1987 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_8 = 'ISO-8859-8'; // see ISO_8859-8:1988 below [RFC1521,NSB] {Do not Localize} + Id_CS_ISO_8859_9 = 'ISO-8859-9'; // see ISO_8859-9:1989 below {Do not Localize} + +const +//These are multicast group IP addresses + Id_IPMC_Base_Address = '224.0.0.0'; // Base Address [RFC1112,JBP] {Do not Localize} + Id_IPMC_All_Systems = '224.0.0.1'; // All Systems on this Subne [RFC1112,JBP] {Do not Localize} + Id_IPMC_All_Routers = '224.0.0.2'; // All Routers on this Subnet [JBP] {Do not Localize} + Id_IPMC_Unassigned = '224.0.0.3'; // Unassigned [JBP] {Do not Localize} + Id_IPMC_DVMRP_Routers = '224.0.0.4'; // DVMRP Routers [RFC1075,JBP] {Do not Localize} + Id_IPMC_OSPFIGP_All_Routers = '224.0.0.5'; // OSPFIGP All Routers [RFC1583,JXM1] {Do not Localize} + Id_IPMC_OSPFIGP_Designated_Routers = '224.0.0.6'; //OSPFIGP Designated Routers [RFC1583,JXM1] {Do not Localize} +Id_IPMC_ST_Routers = '224.0.0.7'; // ST Routers [RFC1190,KS14] {Do not Localize} + Id_IPMC_ST_Hosts ='224.0.0.8'; // ST Hosts [RFC1190,KS14] {Do not Localize} + Id_IPMC_RIP2_Routers = '224.0.0.9'; // RIP2 Routers [GSM11] {Do not Localize} + Id_IPMC_IGRP_Routers = '224.0.0.10'; // IGRP Routers [Dino Farinacci] {Do not Localize} + Id_IPMC_Mobile_Agents ='224.0.0.11'; // Mobile-Agents [Bill Simpson] {Do not Localize} +//224.0.0.12-224.0.0.255 Unassigned [JBP] + Id_IPMC_DHCP_Server = '224.0.0.12'; // DHCP Server / Relay Agent [RFC1884] {Do not Localize} + Id_IPMC_DHCP_All_PIM_Routers = '224.0.0.13'; // All PIM Routers [Farinacci] {Do not Localize} + Id_IPMC_RSVP_ENCAPSULATION = '224.0.0.14'; // RSVP-ENCAPSULATION [Braden] {Do not Localize} + Id_IPMC_All_CDT_Routers = '224.0.0.15'; // all-cbt-routers [Ballardie] {Do not Localize} + Id_IPMC_designated_sbm = '224.0.0.16'; // designated-sbm [Baker] {Do not Localize} + Id_IPMC_ll_sbms = '224.0.0.17'; // all-sbms [Baker] {Do not Localize} + Id_IPMC_VRRP = '224.0.0.18'; // VRRP [Hinden] {Do not Localize} + Id_IPMC_IPAllL1ISs = '224.0.0.19'; // IPAllL1ISs [Przygienda] {Do not Localize} + Id_IPMC_IPAllL2ISs = '224.0.0.20'; // IPAllL2ISs [Przygienda] {Do not Localize} + Id_IPMC_IPAllIntermediate_Systems = '224.0.0.21';// IPAllIntermediate Systems [Przygienda] {Do not Localize} + Id_IPMC_IGMP = '224.0.0.22'; // IGMP [Deering] {Do not Localize} + Id_IPMC_GLOBECAST_ID = '224.0.0.23'; // GLOBECAST-ID [Scannell] {Do not Localize} + //224.0.0.24 unassigned + Id_IPMC_router_to_switch = '224.0.0.25'; // router-to-switch [Wu] {Do not Localize} + //224.0.0.26 unassigned + Id_IPMC_Al_MPP_Hello = '224.0.0.27'; // Al MPP Hello [Martinicky] {Do not Localize} + Id_IPMC_ETC_Control = '224.0.0.28'; // ETC Control [Polishinski] {Do not Localize} + Id_IPMC_GE_FANUC = '224.0.0.29'; // GE-FANUC [Wacey] {Do not Localize} + Id_IPMC_INDIGO_VHDP = '224.0.0.30'; // indigo-vhdp [Caughie] {Do not Localize} + Id_IPMC_shinbroadband = '224.0.0.31'; // shinbroadband [Kittivatcharapong] {Do not Localize} + Id_IPMC_digistar = '224.0.0.32'; // digistar [Kerkan] {Do not Localize} + Id_IPMC_ff_system_Management = '224.0.0.33'; // ff-system-management [Glanzer] {Do not Localize} + Id_IPMC_pt2_discover = '224.0.0.34'; // pt2-discover [Kammerlander] {Do not Localize} + Id_IPMC_DXCLUSTER = '224.0.0.35'; // DXCLUSTER [Koopman] {Do not Localize} + Id_IPMC_DTC_Announcement = '224.0.0.36'; // DTCP Announcement [Cipiere] {Do not Localize} + Id_IPMC_zeroconfaddr_Min = '224.0.0.37'; // zeroconfaddr (renew 12/02) [Guttman] {Do not Localize} + {Range} + Id_IPMC_zeroconfaddr_Max = '224.0.0.68'; {Do not Localize} + // 224.0.0.69-224.0.0.100 Reserved [IANA] + Id_IPMC_Cisco_nhap = '224.0.0.101'; // cisco-nhap [Bakke] {Do not Localize} + Id_IPMC_HSPP = '224.0.0.102'; // HSRP [Wilson] {Do not Localize} + Id_IPMC_MDAP = '224.0.0.103'; // MDAP [Deleu] {Do not Localize} + // 224.0.0.104-224.0.0.250 Unassigned [JBP] + Id_IPMC_mDNS = '224.0.0.251'; // mDNS [Cheshire] {Do not Localize} + // 224.0.0.252-224.0.0.255 Unassigned [JBP] + + // 224.0.1.0 - 224.0.1.255 (224.0.1/24) Internetwork Control Block + Id_IPMC_VMTP_Managers = '224.0.1.0'; // VMTP Managers Group [RFC1045,DRC3] {Do not Localize} + Id_IPMC_NTP_Protocol = '224.0.1.1'; // NTP Network Time Protocol [RFC1119,DLM1] {Do not Localize} + Id_IPMC_SGI_Dogfight ='224.0.1.2'; // SGI-Dogfight [AXC] {Do not Localize} + Id_IPMC_Rwhod = '224.0.1.3'; // Rwhod [SXD] {Do not Localize} + Id_IPMC_VNP = '224.0.1.4'; // VNP [DRC3] {Do not Localize} + Id_IPMC_Artificial_Horizons = '224.0.1.5'; // Artificial Horizons - Aviator [BXF] {Do not Localize} + Id_IPMC_NSS = '224.0.1.6'; // NSS - Name Service Server [BXS2] {Do not Localize} + Id_IPMC_AUDIONEWS = '224.0.1.7'; // AUDIONEWS - Audio News Multicast [MXF2]{Do not Localize} + Id_IPMC_SUN_NIS_Plus_Information = '224.0.1.8'; // SUN NIS+ Information Service [CXM3]{Do not Localize} + Id_IPMC_MTP_Protocol = '224.0.1.9'; // MTP Multicast Transport Protocol [SXA] {Do not Localize} + Id_IPMC_IETF_1_Low_Audio = '224.0.1.10'; // IETF-1-LOW-AUDIO [SC3] {Do not Localize} + Id_IPMC_IETF_1_Audio = '224.0.1.11'; // IETF-1-AUDIO [SC3] {Do not Localize} + Id_IPMC_IETF_1_Video = '224.0.1.12'; // IETF-1-VIDEO [SC3] {Do not Localize} + Id_IPMC_IETF_2_Low_Audio = '224.0.1.13'; // IETF-2-LOW-AUDIO [SC3] {Do not Localize} + Id_IPMC_IETF_2_Audio = '224.0.1.14'; // IETF-2-AUDIO [SC3] {Do not Localize} + Id_IPMC_IETF_2_Video = '224.0.1.15'; // IETF-2-VIDEO [SC3] {Do not Localize} + Id_IPMC_Music_Service = '224.0.1.16'; // MUSIC-SERVICE [Guido van Rossum] {Do not Localize} + Id_IPMC_SEANET_TELEMETRY = '224.0.1.17'; // SEANET-TELEMETRY [Andrew Maffei] {Do not Localize} + Id_IPMC_SEANET_IMAGE = '224.0.1.18'; // SEANET-IMAGE [Andrew Maffei] {Do not Localize} + Id_IPMC_MLOADD = '224.0.1.19'; // MLOADD [Braden] {Do not Localize} + Id_IPMC_Private_Expiriment = '224.0.1.20'; // any private experiment [JBP] {Do not Localize} + Id_IPMC_DVMRP_on_MOSPF = '224.0.1.21'; // DVMRP on MOSPF [John Moy] {Do not Localize} + Id_IPMC_SVRLOC = '224.0.1.22'; // SVRLOC {Do not Localize} + Id_IPMC_XINGTV = '224.0.1.23'; // XINGTV {Do not Localize} + Id_IPMC_Microsoft_DS = '224.0.1.24'; // microsoft-ds {Do not Localize} + Id_IPMC_NBC_Pro = '224.0.1.25'; // nbc-pro {Do not Localize} + Id_IPMC_NBC_Pfn = '224.0.1.26'; // nbc-pfn {Do not Localize} + Id_IPMC_lmsc_calren_1 = '224.0.1.27'; // lmsc-calren-1 [Uang] {Do not Localize} + Id_IPMC_lmsc_calren_2 = '224.0.1.28'; // lmsc-calren-2 [Uang] {Do not Localize} + Id_IPMC_lmsc_calren_3 = '224.0.1.29'; // lmsc-calren-3 [Uang] {Do not Localize} + Id_IPMC_lmsc_calren_4 = '224.0.1.30'; // lmsc-calren-4 [Uang] {Do not Localize} + Id_IPMC_ampr_info = '224.0.1.31'; // ampr-info [Janssen] {Do not Localize} + Id_IPMC_mtrace = '224.0.1.32'; // mtrace [Casner] {Do not Localize} + Id_IPMC_RSVP_encap_1 = '224.0.1.33'; // RSVP-encap-1 [Braden] {Do not Localize} + Id_IPMC_RSVP_encap_2 = '224.0.1.34'; // RSVP-encap-2 [Braden] {Do not Localize} + Id_IPMC_SVRLOC_DA = '224.0.1.35'; // SVRLOC-DA [Veizades] {Do not Localize} + Id_IPMC_rln_server = '224.0.1.36'; // rln-server [Kean] {Do not Localize} + Id_IPMC_proshare_mc = '224.0.1.37'; // proshare-mc [Lewis] {Do not Localize} + Id_IPMC_dantz = '224.0.1.38'; // dantz [Zulch] {Do not Localize} + Id_IPMC_cisco_rp_announce = '224.0.1.39'; // cisco-rp-announce [Farinacci] {Do not Localize} + Id_IPMC_cisco_rp_discovery = '224.0.1.40'; // cisco-rp-discovery [Farinacci] {Do not Localize} + Id_IPMC_gatekeeper = '224.0.1.41'; // gatekeeper [Toga] {Do not Localize} + Id_IPMC_iberiagames = '224.0.1.42'; // iberiagames [Marocho] {Do not Localize} + Id_IPMC_nwn_discovery = '224.0.1.43'; // nwn-discovery [Zwemmer] {Do not Localize} + Id_IPMC_nwn_adaptor = '224.0.1.44'; // nwn-adaptor [Zwemmer] {Do not Localize} + Id_IPMC_isma_1 = '224.0.1.45'; // isma-1 [Dunne] {Do not Localize} + Id_IPMC_isma_2 = '224.0.1.46'; // isma-2 [Dunne] {Do not Localize} + + Id_IPMC_telerate = '224.0.1.47'; // telerate [Peng] {Do not Localize} + Id_IPMC_ciena = '224.0.1.48'; // ciena [Rodbell] {Do not Localize} + Id_IPMC_dcap_servers = '224.0.1.49'; // dcap-servers [RFC2114] {Do not Localize} + Id_IPMC_dcap_clients = '224.0.1.50'; // dcap-clients [RFC2114] {Do not Localize} + Id_IPMC_mcntp_directory = '224.0.1.51'; // mcntp-directory [Rupp] {Do not Localize} + Id_IPMC_mbone_vcr_directory = '224.0.1.52'; // mbone-vcr-directory[Holfelder] {Do not Localize} + Id_IPMC_heartbeat = '224.0.1.53'; // heartbeat [Mamakos] {Do not Localize} + Id_IPMC_sun_mc_grp = '224.0.1.54'; // sun-mc-grp [DeMoney] {Do not Localize} + Id_IPMC_extended_sys = '224.0.1.55'; // extended-sys [Poole] {Do not Localize} + Id_IPMC_pdrncs = '224.0.1.56'; // pdrncs [Wissenbach] {Do not Localize} + Id_IPMC_tns_adv_multi = '224.0.1.57'; // tns-adv-multi [Albin] {Do not Localize} + Id_IPMC_vcals_dmu = '224.0.1.58'; // vcals-dmu [Shindoh] {Do not Localize} + Id_IPMC_zuba = '224.0.1.59'; // zuba [Jackson] {Do not Localize} + Id_IPMC_hp_device_disc = '224.0.1.60'; // hp-device-disc [Albright] {Do not Localize} + Id_IPMC_tms_production = '224.0.1.61'; // tms-production [Gilani] {Do not Localize} + Id_IPMC_sunscalar = '224.0.1.62'; // sunscalar [Gibson] {Do not Localize} + Id_IPMC_mmtp_poll = '224.0.1.63'; // mmtp-poll [Costales] {Do not Localize} + Id_IPMC_compaq_peer = '224.0.1.64'; // compaq-peer [Volpe] {Do not Localize} + Id_IPMC_iapp = '224.0.1.65'; // iapp [Meier] {Do not Localize} + Id_IPMC_multihasc_com = '224.0.1.66'; // multihasc-com [Brockbank] {Do not Localize} + Id_IPMC_serv_discovery = '224.0.1.67'; // serv-discovery [Honton] {Do not Localize} + Id_IPMC_mdhcpdisover = '224.0.1.68'; // mdhcpdisover [RFC2730] {Do not Localize} + Id_IPMC_MMP_bundle_discovery1 = '224.0.1.69'; // MMP-bundle-discovery1 [Malkin] {Do not Localize} + Id_IPMC_MMP_bundle_discovery2 = '224.0.1.70'; // MMP-bundle-discovery2 [Malkin] {Do not Localize} + Id_IPMC_XYPOINT = '224.0.1.71'; // XYPOINT DGPS Data Feed [Green] {Do not Localize} + Id_IPMC_GilatSkySurfer = '224.0.1.72'; // GilatSkySurfer [Gal] {Do not Localize} + Id_IPMC_SharesLive = '224.0.1.73'; // SharesLive [Rowatt] {Do not Localize} + Id_IPMC_NorthernData = '224.0.1.74'; // NorthernData [Sheers] {Do not Localize} + Id_IPMC_SIP = '224.0.1.75'; // SIP [Schulzrinne] {Do not Localize} + Id_IPMC_IAPP2 = '224.0.1.76'; // IAPP [Moelard] {Do not Localize} + Id_IPMC_AGENTVIEW = '224.0.1.77'; // AGENTVIEW [Iyer] {Do not Localize} + Id_IPMC_Tibco_1 = '224.0.1.78'; // Tibco Multicast1 [Shum] {Do not Localize} + Id_IPMC_Tibco_2 = '224.0.1.79'; // Tibco Multicast2 [Shum] {Do not Localize} + Id_IPMC_MSP = '224.0.1.80'; // MSP [Caves] {Do not Localize} + Id_IPMC_OTT = '224.0.1.81'; // OTT (One-way Trip Time) [Schwartz] {Do not Localize} + Id_IPMC_TRACKTICKER = '224.0.1.82'; // TRACKTICKER [Novick] {Do not Localize} + Id_IPMC_dtn_mc = '224.0.1.83'; // dtn-mc [Gaddie] {Do not Localize} + Id_IPMC_jini_announcement = '224.0.1.84'; // jini-announcement [Scheifler] {Do not Localize} + Id_IPMC_jini_request = '224.0.1.85'; // jini-request [Scheifler] {Do not Localize} + Id_IPMC_sde_discovery = '224.0.1.86'; // sde-discovery [Aronson] {Do not Localize} + Id_IPMC_DirecPC_SI = '224.0.1.87'; // DirecPC-SI [Dillon] {Do not Localize} + Id_IPMC_B1RMonitor = '224.0.1.88'; // B1RMonitor [Purkiss] {Do not Localize} + Id_IPMC_3Com_AMP3 = '224.0.1.89'; // 3Com-AMP3 dRMON [Banthia] {Do not Localize} + Id_IPMC_imFtmSvc = '224.0.1.90'; // imFtmSvc [Bhatti] {Do not Localize} + Id_IPMC_NQDS4 = '224.0.1.91'; // NQDS4 [Flynn] {Do not Localize} + Id_IPMC_NQDS5 = '224.0.1.92'; // NQDS5 [Flynn] {Do not Localize} + Id_IPMC_NQDS6 = '224.0.1.93'; // NQDS6 [Flynn] {Do not Localize} + Id_IPMC_NLVL12 = '224.0.1.94'; // NLVL12 [Flynn] {Do not Localize} + Id_IPMC_NTDS1 = '224.0.1.95'; // NTDS1 [Flynn] {Do not Localize} + Id_IPMC_NTDS2 = '224.0.1.96'; // NTDS2 [Flynn] {Do not Localize} + Id_IPMC_NODSA = '224.0.1.97'; // NODSA [Flynn] {Do not Localize} + Id_IPMC_NODSB = '224.0.1.98'; // NODSB [Flynn] {Do not Localize} + Id_IPMC_NODSC = '224.0.1.99'; // NODSC [Flynn] {Do not Localize} + Id_IPMC_NODSD = '224.0.1.100'; // NODSD [Flynn] {Do not Localize} + Id_IPMC_NQDS4R = '224.0.1.101'; // NQDS4R [Flynn] {Do not Localize} + Id_IPMC_NQDS5R = '224.0.1.102'; // NQDS5R [Flynn] {Do not Localize} + Id_IPMC_NQDS6R = '224.0.1.103'; // NQDS6R [Flynn] {Do not Localize} + Id_IPMC_NLVL12R = '224.0.1.104'; // NLVL12R [Flynn] {Do not Localize} + Id_IPMC_NTDS1R = '224.0.1.105'; // NTDS1R [Flynn] {Do not Localize} + Id_IPMC_NTDS2R = '224.0.1.106'; // NTDS2R [Flynn] {Do not Localize} + Id_IPMC_NODSAR = '224.0.1.107'; // NODSAR [Flynn] {Do not Localize} + Id_IPMC_NODSBR = '224.0.1.108'; // NODSBR [Flynn] {Do not Localize} + Id_IPMC_NODSCR = '224.0.1.109'; // NODSCR [Flynn] {Do not Localize} + Id_IPMC_NODSDR = '224.0.1.110'; // NODSDR [Flynn] {Do not Localize} + Id_IPMC_MRM = '224.0.1.111'; // MRM [Wei] {Do not Localize} + Id_IPMC_TVE_FILE = '224.0.1.112'; // TVE-FILE [Blackketter] {Do not Localize} + Id_IPMC_TVE_ANNOUNCE = '224.0.1.113'; // TVE-ANNOUNCE [Blackketter] {Do not Localize} + Id_IPMC_Mac = '224.0.1.114'; // Mac Srv Loc [Woodcock] {Do not Localize} + Id_IPMC_Simple = '224.0.1.115'; // Simple Multicast [Crowcroft] {Do not Localize} + Id_IPMC_SpectraLinkGW = '224.0.1.116'; // SpectraLinkGW [Hamilton] {Do not Localize} + Id_IPMC_dieboldmcast = '224.0.1.117'; // dieboldmcast [Marsh] {Do not Localize} + Id_IPMC_Tivoli = '224.0.1.118'; // Tivoli Systems [Gabriel] {Do not Localize} + Id_IPMC_pq_lic_mcast = '224.0.1.119'; // pq-lic-mcast [Sledge] {Do not Localize} + Id_IPMC_HYPERFEED = '224.0.1.120'; // HYPERFEED [Kreutzjans] {Do not Localize} + Id_IPMC_Pipesplatform = '224.0.1.121'; // Pipesplatform [Dissett] {Do not Localize} + Id_IPMC_LiebDevMgmg_DM = '224.0.1.122'; // LiebDevMgmg-DM [Velten] {Do not Localize} + Id_IPMC_TRIBALVOICE = '224.0.1.123'; // TRIBALVOICE [Thompson] {Do not Localize} +// Id_IPMC_Unassigned = '//224.0.1.124'; //Unassigned (Retracted 1/29/01) {Do not Localize} + Id_IPMC_PolyCom = '224.0.1.125'; // PolyCom Relay1 [Coutiere] {Do not Localize} + Id_IPMC_Infront = '224.0.1.126'; // Infront Multi1 [Lindeman] {Do not Localize} + Id_IPMC_XRX = '224.0.1.127'; // XRX DEVICE DISC [Wang] {Do not Localize} + Id_IPMC_CNN = '224.0.1.128'; // CNN [Lynch] {Do not Localize} + Id_IPMC_PTP_primary = '224.0.1.129'; // PTP-primary [Eidson] {Do not Localize} + Id_IPMC_PTP_alternate1 = '224.0.1.130'; // PTP-alternate1 [Eidson] {Do not Localize} + Id_IPMC_PTP_alternate2 = '224.0.1.131'; // PTP-alternate2 [Eidson] {Do not Localize} + Id_IPMC_PTP_alternate3 = '224.0.1.132'; // PTP-alternate3 [Eidson] {Do not Localize} + Id_IPMC_ProCast = '224.0.1.133'; // ProCast [Revzen] {Do not Localize} + Id_IPMC_3Com = '224.0.1.134'; // 3Com Discp [White] {Do not Localize} + Id_IPMC_CS_Multicasting = '224.0.1.135'; // CS-Multicasting [Stanev] {Do not Localize} + Id_IPMC_TS_MC_1 = '224.0.1.136'; // TS-MC-1 [Sveistrup] {Do not Localize} + Id_IPMC_Make = '224.0.1.137'; // Make Source [Daga] {Do not Localize} + Id_IPMC_Teleborsa = '224.0.1.138'; // Teleborsa [Strazzera] {Do not Localize} + Id_IPMC_SUMAConfig = '224.0.1.139'; // SUMAConfig [Wallach] {Do not Localize} +// Id_IPMC_Unassigned = '224.0.1.140'; //Unassigned {Do not Localize} + Id_IPMC_DHCP_SERVERS = '224.0.1.141'; // DHCP-SERVERS [Hall] {Do not Localize} + Id_IPMC_CN = '224.0.1.142'; // CN Router-LL [Armitage] {Do not Localize} + Id_IPMC_EMWIN = '224.0.1.143'; // EMWIN [Querubin] {Do not Localize} + Id_IPMC_Alchemy = '224.0.1.144'; // Alchemy Cluster [O'Rourke] {Do not Localize} + Id_IPMC_Satcast_1 = '224.0.1.145'; // Satcast One [Nevell] {Do not Localize} + Id_IPMC_Satcast_2 = '224.0.1.146'; // Satcast Two [Nevell] {Do not Localize} + Id_IPMC_Satcast_3 = '224.0.1.147'; // Satcast Three [Nevell] {Do not Localize} + Id_IPMC_Intline = '224.0.1.148'; // Intline [Sliwinski] {Do not Localize} + Id_IPMC_8x8 = '224.0.1.149'; // 8x8 Multicast [Roper] {Do not Localize} +// Id_IPMC__Unassigned = '224.0.1.150'; //Unassigned [JBP] {Do not Localize} + Id_IPMC_Intline_1 = '224.0.1.151'; // Intline-1 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_2 = '224.0.1.152'; // Intline-2 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_3 = '224.0.1.153'; // Intline-3 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_4 = '224.0.1.154'; // Intline-4 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_5 = '224.0.1.155'; // Intline-5 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_6 = '224.0.1.156'; // Intline-6 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_7 = '224.0.1.157'; // Intline-7 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_8 = '224.0.1.158'; // Intline-8 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_9 = '224.0.1.159'; // Intline-9 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_10 = '224.0.1.160'; // Intline-10 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_11 = '224.0.1.161'; // Intline-11 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_12 = '224.0.1.162'; // Intline-12 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_13 = '224.0.1.163'; // Intline-13 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_14 = '224.0.1.164'; // Intline-14 [Sliwinski] {Do not Localize} + Id_IPMC_Intline_15 = '224.0.1.165'; // Intline-15 [Sliwinski] {Do not Localize} + Id_IPMC_marratech_cc = '224.0.1.166'; // marratech-cc [Parnes] {Do not Localize} + Id_IPMC_EMS_InterDev = '224.0.1.167'; // EMS-InterDev [Lyda] {Do not Localize} + Id_IPMC_itb301 = '224.0.1.168'; // itb301 [Rueskamp] {Do not Localize} + Id_IPMC_rtv_audio = '224.0.1.169'; // rtv-audio [Adams] {Do not Localize} + Id_IPMC_rtv_video = '224.0.1.170'; // rtv-video [Adams] {Do not Localize} + Id_IPMC_HAVI_Sim = '224.0.1.171'; // HAVI-Sim [Wasserroth] {Do not Localize} + Id_IPMC_Nokia = '224.0.1.172'; // Nokia Cluster [O'Rourke] {Do not Localize} + Id_IPMC_host_request = '224.0.1.173'; // host-request [K.Thompson] {Do not Localize} + Id_IPMC_host_announce = '224.0.1.174'; // host-announce [K.Thompson] {Do not Localize} + Id_IPMC_ptk_cluster = '224.0.1.175'; // ptk-cluster [Hodgson] {Do not Localize} + Id_IPMC_Proxim = '224.0.1.176'; // Proxim Protocol [Shukla] {Do not Localize} + // Id_IPMC_Unassigned = '//224.0.1.177-224.0.1.255'; //Unassigned [JBP] {Do not Localize} + Id_IPMC_rwho = '224.0.2.1'; // "rwho" Group (BSD) [JBP] {Do not Localize} + Id_IPMC_SUN = '224.0.2.2'; // SUN RPC PMAPPROC_CALLIT [BXE1] {Do not Localize} + + // 224.0.2.0 - 224.0.255.0 AD-HOC Block + Id_IPMC_SIAC_Min = '224.0.2.064'; {Do not Localize} + Id_IPMC_SIAC_Max = '224.0.2.095'; // SIAC MDD Service [Tse] {Do not Localize} + Id_IPMC_CoolCast_Min = '224.0.2.096'; // CoolCast [Ballister] {Do not Localize} + Id_IPMC_CoolCast_Max = '224.0.2.127'; // CoolCast [Ballister] {Do not Localize} + Id_IPMC_WOZ_Garage_Min = '224.0.2.128'; // WOZ-Garage [Marquardt] {Do not Localize} + Id_IPMC_WOZ_Garage_Max = '224.0.2.191'; // WOZ-Garage [Marquardt] {Do not Localize} + Id_IPMC_SIAC_Market_Min = '224.0.2.192'; // SIAC MDD Market Service [Lamberg] {Do not Localize} + Id_IPMC_SIAC_Market_Max = '224.0.2.255'; // SIAC MDD Market Service [Lamberg] {Do not Localize} + Id_IPMC_RFE_Generic_Min = '224.0.3.000'; // RFE Generic Service [DXS3] {Do not Localize} + Id_IPMC_RFE_Generic_Max = '224.0.3.255'; // RFE Generic Service [DXS3] {Do not Localize} + Id_IPMC_RFE_Individual_Min = '224.0.4.000'; // RFE Individual Conferences [DXS3] {Do not Localize} + Id_IPMC_RFE_Individual_Max = '224.0.4.255'; // RFE Individual Conferences [DXS3] {Do not Localize} + Id_IPMC_CDPD_Min = '224.0.5.000'; {Do not Localize} + Id_IPMC_CDPD_Max = '224.0.5.127'; // CDPD Groups [Bob Brenner] {Do not Localize} + Id_IPMC_SIAC_Market2_Min = '224.0.5.128'; // SIAC Market Service [Cho] {Do not Localize} + Id_IPMC_SIAC_Market2_Max = '224.0.5.191'; {Do not Localize} + Id_IPMC_SIAC_MYSE_Min = '224.0.5.192'; // SIAC NYSE Order PDP protocol [Chan] {Do not Localize} + Id_IPMC_SIAC_MYSE_Max = '224.0.5.255'; {Do not Localize} + Id_IPMC_Cornell_Min = '224.0.6.000'; // Cornell ISIS Project [Tim Clark] {Do not Localize} + Id_IPMC_Cornell_Max = '224.0.6.127'; {Do not Localize} + // Id_IPMC_Unassigned = '224.0.6.128-224.0.6.255'; //Unassigned [IANA] {Do not Localize} + Id_IPMC_Where_Are_You_Min = '224.0.7.000'; // Where-Are-You [Simpson] {Do not Localize} + Id_IPMC_Where_Are_You_Max = '224.0.7.255'; {Do not Localize} + Id_IPMC_INTV_Min = '224.0.8.000'; // INTV [Tynan] {Do not Localize} + Id_IPMC_INTV_Max = '224.0.8.255'; {Do not Localize} + Id_IPMC_Invisible_Min = '224.0.9.000'; // Invisible Worlds [Malamud] {Do not Localize} + Id_IPMC_Invisible_Max = '224.0.9.255'; {Do not Localize} + Id_IPMC_DLSw_Min = '224.0.10.000'; // DLSw Groups [Lee] {Do not Localize} + Id_IPMC_DLSw_Max = '224.0.10.255'; {Do not Localize} + Id_IPMC_NCC_NET_Min = '224.0.11.000'; // NCC.NET Audio [Rubin] {Do not Localize} + Id_IPMC_NCC_NET_Max = '224.0.11.255'; {Do not Localize} + Id_IPMC_Microsoft_Min = '224.0.12.000'; // Microsoft and MSNBC [Blank] {Do not Localize} + Id_IPMC_Microsoft_Max = '224.0.12.063'; {Do not Localize} + Id_IPMC_UUNET_Min = '224.0.13.000'; // UUNET PIPEX Net News [Barber] {Do not Localize} + Id_IPMC_UUNET_Max = '224.0.13.255'; {Do not Localize} + Id_IPMC_NLANR_Min = '224.0.14.000'; // NLANR [Wessels] {Do not Localize} + Id_IPMC_NLANR_Max = '224.0.14.255'; {Do not Localize} + Id_IPMC_Hewlett_Min = '224.0.15.000'; // Hewlett Packard [van der Meulen] {Do not Localize} + Id_IPMC_Hewlett_Max = '224.0.15.255'; {Do not Localize} + Id_IPMC_XingNet_Min = '224.0.16.000'; // XingNet [Uusitalo] {Do not Localize} + Id_IPMC_XingNet_Max = '224.0.16.255'; {Do not Localize} + Id_IPMC_Mercantile_Min = '224.0.17.000'; // Mercantile & CommodityExchange [Gilani]{Do not Localize} + Id_IPMC_Mercantile_Max = '224.0.17.031'; {Do not Localize} + Id_IPMC_NDQMD1_Min = '224.0.17.032'; // NDQMD1 [Nelson] {Do not Localize} + Id_IPMC_NDQMD1_Max = '224.0.17.063'; {Do not Localize} + Id_IPMC_ODN_DTV_Min = '224.0.17.064'; // ODN-DTV [Hodges] {Do not Localize} + Id_IPMC_ODN_DTV_Max = '224.0.17.127'; {Do not Localize} + Id_IPMC_Dow_Min = '224.0.18.000'; // Dow Jones [Peng] {Do not Localize} + Id_IPMC_Dow_Max = '224.0.18.255'; {Do not Localize} + Id_IPMC_Walt_Min = '224.0.19.000'; // Walt Disney Company [Watson] {Do not Localize} + Id_IPMC_Walt_Max = '224.0.19.063'; {Do not Localize} + Id_IPMC_Cal_Min = '224.0.19.064'; // Cal Multicast [Moran] {Do not Localize} + Id_IPMC_Cal_Max = '224.0.19.095'; {Do not Localize} + Id_IPMC_SIAC_Market3_Min = '224.0.19.096'; // SIAC Market Service [Roy] {Do not Localize} + Id_IPMC_SIAC_Market3_Max = '224.0.19.127'; {Do not Localize} + Id_IPMC_IIG_Min = '224.0.19.128'; // IIG Multicast [Carr] {Do not Localize} + Id_IPMC_IIG_Max = '224.0.19.191'; {Do not Localize} + Id_IPMC_Metropol_Min = '224.0.19.192'; // Metropol [Crawford] {Do not Localize} + Id_IPMC_Metropol_Max = '224.0.19.207'; {Do not Localize} + Id_IPMC_Xenoscience_Min = '224.0.19.208'; // Xenoscience, Inc. [Timm] {Do not Localize} + Id_IPMC_Xenoscience_Max = '224.0.19.239'; {Do not Localize} + Id_IPMC_HYPERFEED_Min = '224.0.19.240'; // HYPERFEED [Felix] {Do not Localize} + Id_IPMC_HYPERFEED_Max = '224.0.19.255'; {Do not Localize} + Id_IPMC_MS_IP_TV_Min = '224.0.20.000'; // MS-IP/TV [Wong] {Do not Localize} + Id_IPMC_MS_IP_TV_Max = '224.0.20.063'; {Do not Localize} + Id_IPMC_Reliable_Min = '224.0.20.064'; // Reliable Network Solutions [Vogels] {Do not Localize} + Id_IPMC_Reliable_Max = '224.0.20.127'; {Do not Localize} + Id_IPMC_TRACKTICKER_Min = '224.0.20.128'; // TRACKTICKER Group [Novick] {Do not Localize} + Id_IPMC_TRACKTICKER_Max = '224.0.20.143'; {Do not Localize} + Id_IPMC_CNR_Min = '224.0.20.144'; // CNR Rebroadcast MCA [Sautter] {Do not Localize} + Id_IPMC_CNR_Max = '224.0.20.207'; {Do not Localize} + Id_IPMC_Talarian_Min = '224.0.21.000'; // Talarian MCAST [Mendal] {Do not Localize} + Id_IPMC_Talarian_Max = '224.0.21.127'; {Do not Localize} + Id_IPMC_WORLD_Min = '224.0.22.000'; // WORLD MCAST [Stewart] {Do not Localize} + Id_IPMC_WORLD_Max = '224.0.22.255'; {Do not Localize} + Id_IPMC_Domain_Min = '224.0.252.000'; // Domain Scoped Group [Fenner] {Do not Localize} + Id_IPMC_Domain_Max = '224.0.252.000-224.0.252.255'; {Do not Localize} + Id_IPMC_Report_Min = '224.0.253.000'; //Report Group [Fenner] {Do not Localize} + Id_IPMC_Report_Max = '224.0.253.255'; {Do not Localize} + Id_IPMC_Query_Min = '224.0.254.000'; //Query Group [Fenner] {Do not Localize} + Id_IPMC_Query_Max = '224.0.254.255'; {Do not Localize} + Id_IPMC_Border_Min = '224.0.255.000'; //Border Routers [Fenner] {Do not Localize} + Id_IPMC_Border_Max = '224.0.255.255'; {Do not Localize} + + // 224.1.0.0 - 224.1.255.255 (224.1/16) ST Multicast Groups [RFC1190,KS14] + Id_IPMC_Multimedia_Min = '224.2.0.0'; // Multimedia Conference Calls [SC3] {Do not Localize} + Id_IPMC_Multimedia_Max = '224.2.127.253'; // Multimedia Conference Calls [SC3] {Do not Localize} + Id_IPMC_SAPv1 = '224.2.127.254'; // SAPv1 Announcements [SC3] {Do not Localize} + Id_IPMC_SAPv0 = '224.2.127.255'; // SAPv0 Announcements [SC3] {Do not Localize} + Id_IPMC_SAP_Min = '224.2.128.0'; // SAP Dynamic Assignments [SC3] {Do not Localize} + Id_IPMC_SAP_Max = '224.2.255.255'; // SAP Dynamic Assignments [SC3] {Do not Localize} + + // Id_IPMC_Reserved_Min = '224.3.0.0'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '224.251.255.255'; // Reserved [IANA] {Do not Localize} + Id_IPMC_DIS_Min = '224.252.000.000'; // DIS Transient Groups [IANA] {Do not Localize} + Id_IPMC_DIS_Max = '224.255.255.255'; // DIS Transient Groups [IANA] {Do not Localize} + // Id_IPMC_Reserved_Min = '225.000.000.000'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '231.255.255.255'; // Reserved [IANA] {Do not Localize} + Id_IPMC_Source_Min = '232.000.000.000'; // Source Specific Multicast [DRC3] {Do not Localize} + Id_IPMC_Source_Max = '232.255.255.255'; // Source Specific Multicast [DRC3] {Do not Localize} + Id_IPMC_GLOP_Min = '233.000.000.000'; // GLOP Block [RFC3180] {Do not Localize} + Id_IPMC_GLOP_Max = '233.255.255.255'; // GLOP Block [RFC3180] {Do not Localize} + // Id_IPMC_Reserved_Min = '234.000.000.000'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '238.255.255.255'; // Reserved [IANA] {Do not Localize} + Id_IPMC_Administratively_Min = '239.000.000.000'; // Administratively Scoped [IANA,RFC2365] {Do not Localize} + Id_IPMC_Administratively_Max = '239.255.255.255'; // Administratively Scoped [IANA,RFC2365] {Do not Localize} + // Id_IPMC_Reserved_Min = '239.000.000.000'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '239.063.255.255'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Min = '239.064.000.000'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '239.127.255.255'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Min = '239.128.000.000'; // Reserved [IANA] {Do not Localize} + // Id_IPMC_Reserved_Max = '239.191.255.255'; // Reserved [IANA] {Do not Localize} + Id_IPMC_Organization_Local_Min = '239.192.000.000'; // Organization-Local Scope [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Organization_Local_Max = '239.251.255.255'; // Organization-Local Scope [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local1_Min = '239.252.000.000'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local1_Max = '239.252.255.255'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local2_Min = '239.253.000.000'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local2_Max = '239.253.255.255'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local3_Min = '239.254.000.000'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local3_Max = '239.254.255.255'; // Site-Local Scope(reserved) [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local4_Min = '239.255.000.000'; // Site-Local Scope [Meyer,RFC2365] {Do not Localize} + Id_IPMC_Site_Local4_Max = '239.255.255.255'; // Site-Local Scope [Meyer,RFC2365] {Do not Localize} + Id_IPMC_rasadv = '239.255.002.002'; // rasadv [Thaler] {Do not Localize} + +{ + There is a concept of relative addresses to be used with the scoped + multicast addresses. These relative addresses are listed here: + + Relative Description Reference + --------------------- ------------------------------------------------- --------- +} + Id_IPREL_SAP = 0; // SAP Session Announcement Protocol [Handley] + Id_IPREL_MADCAP = 1; // MADCAP Protocol [RFC2730] + Id_IPREL_SLPV2 = 2; // SLPv2 Discovery [Guttman] + Id_IPREL_MZAP = 3; // MZAP [Thaler] + Id_IPREL_DNS = 4; // Multicast Discovery of DNS Services [Manning] + Id_IPREL_SSDP = 5; // SSDP [Goland] + Id_IPREL_DHCPV4 = 6; // DHCP v4 [Hall] + Id_IPREL_AAP = 7; // AAP [Hanna] + Id_IPREL_MBUS = 8; // MBUS [Kutscher] +// 9-252 Reserved - To be assigned by the IANA +// 253 Reserved +// 254-255 Reserved - To be assigned by the IANA + +//IPv6 Multicast addresses +//http://www.iana.org/assignments/ipv6-multicast-addresses + +// Node-Local Scope +// ---------------- + Id_IPv6MC_NL_All_Nodes_Address = 'FF01:0:0:0:0:0:0:1'; // All Nodes Address [RFC4291] + Id_IPv6MC_NL_All_Routers_Address = 'FF01:0:0:0:0:0:0:2'; // All Routers Address [RFC4291] + Id_IPv6MC_NL_mDNSv6 = 'FF01:0:0:0:0:0:0:FB'; // mDNSv6 + +// Link-Local Scope +// ---------------- + Id_IPv6MC_LL_All_Nodes_Address = 'FF02:0:0:0:0:0:0:1'; // All Nodes Address [RFC4291] + Id_IPv6MC_LL_All_Routers_Address = 'FF02:0:0:0:0:0:0:2'; // All Routers Address [RFC4291] +// 'FF02:0:0:0:0:0:0:3'; Unassigned [JBP] + Id_IPv6MC_LL_DVMRP_Routers = 'FF02:0:0:0:0:0:0:4'; // DVMRP Routers [RFC1075,JBP] + Id_IPv6MC_LL_OSPFIGP = 'FF02:0:0:0:0:0:0:5'; // OSPFIGP [RFC2328,Moy] + Id_IPv6MC_LL_OSPFIGP_Designated_Routers = 'FF02:0:0:0:0:0:0:6'; // OSPFIGP Designated Routers [RFC2328,Moy] + Id_IPv6MC_LL_ST_Routers = 'FF02:0:0:0:0:0:0:7'; // ST Routers [RFC1190,KS14] + Id_IPv6MC_LL_ST_Hosts = 'FF02:0:0:0:0:0:0:8'; // ST Hosts [RFC1190,KS14] + Id_IPv6MC_LL_RIP_Routers = 'FF02:0:0:0:0:0:0:9'; // RIP Routers [RFC2080] + Id_IPv6MC_LL_EIGRP_Routers = 'FF02:0:0:0:0:0:0:A'; // EIGRP Routers [Farinacci] + Id_IPv6MC_LL_Mobile_Agents = 'FF02:0:0:0:0:0:0:B'; // Mobile-Agents [Bill Simpson] + Id_IPv6MC_LL_SSDP = 'FF02:0:0:0:0:0:0:C'; // SSDP [UPnP] + Id_IPv6MC_LL_All_PIM_Routers = 'FF02:0:0:0:0:0:0:D'; // All PIM Routers [Farinacci] + Id_IPv6MC_LL_RSVP_ENCAPSULATION = 'FF02:0:0:0:0:0:0:E'; // RSVP-ENCAPSULATION [Braden] + Id_IPv6MC_LL_UPnP = 'FF02:0:0:0:0:0:0:F'; // UPnP [UPnP] + Id_IPv6MC_LL_All_MLDv2_capable_routers = 'FF02:0:0:0:0:0:0:16'; // All MLDv2-capable routers [RFC3810] + Id_IPv6MC_LL_All_Snoopers = 'FF02:0:0:0:0:0:0:6A'; // All-Snoopers [RFC4286] + Id_IPv6MC_LL_PTP_pdelay = 'FF02:0:0:0:0:0:0:6B'; // PTP-pdelay [IEEE1588, K.Lee] 02 February 2007 + Id_IPv6MC_LL_Saratoga = 'FF02:0:0:0:0:0:0:6C'; // Saratoga [Wood] 30 August 2007 + Id_IPv6MC_LL_LL_MANET_Routers = 'FF02:0:0:0:0:0:0:6D'; // LL-MANET-Routers [RFC-ietf-manet-iana-07.txt] + Id_IPv6MC_LL_mDNSv6 = 'FF02:0:0:0:0:0:0:FB'; // mDNSv6 [Cheshire] + + Id_IPv6MC_LL_Link_Name = 'FF02:0:0:0:0:0:1:1'; // Link Name [Harrington] + Id_IPv6MC_LL_All_dhcp_agents = 'FF02:0:0:0:0:0:1:2'; // All-dhcp-agents [RFC3315] + Id_IPv6MC_LL_Link_local_Multicast_Name_Resolution = 'FF02:0:0:0:0:0:1:3'; // Link-local Multicast Name + // Resolution [RFC4795] + Id_IPv6MC_LL_DTCP_Announcement = 'FF02:0:0:0:0:0:1:4'; // DTCP Announcement + +// Site-Local Scope +// ---------------- + Id_IPv6MC_SL_All_Routers_Address = 'FF05:0:0:0:0:0:0:2'; // All Routers Address [RFC4291] + Id_IPv6MC_SL_mDNSv6 = 'FF05:0:0:0:0:0:0:FB'; // mDNSv6 [Cheshire] + + Id_IPv6MC_SL_All_dhcp_servers = 'FF05:0:0:0:0:0:1:3'; // All-dhcp-servers [RFC3315] + Id_IPv6MC_SL_Deprecated = 'FF05:0:0:0:0:0:1:4'; // Deprecated (2003-03-12) + +//Variable Scope Multicast Addresses +//---------------------------------- +//These permanently assigned multicast addresses are valid over all scope +//ranges. This is shown by an "X" in the scope field of the address that +//means any legal scope value. + +//Note that, as defined in [RFC4291], IPv6 multicast addresses which +//are only different in scope represent different groups. Nodes must +//join each group individually. + +//The IPv6 multicast addresses with variable scope are listed below. + + Id_IPv6MC_V_Reserved_Multicast_Address = 'FF0X:0:0:0:0:0:0:0'; // Reserved Multicast Address [RFC4291] + Id_IPv6MC_V_SSDP = 'FF0X:0:0:0:0:0:0:C'; // SSDP [UPnP] + Id_IPv6MC_V_mDNSv6 = 'FF0X:0:0:0:0:0:0:FB'; // mDNSv6 [Cheshire] + + Id_IPv6MC_V_VMTP_Managers_Group = 'FF0X:0:0:0:0:0:0:100'; // VMTP Managers Group [RFC1045,DRC3] + Id_IPv6MC_V_NTP = 'FF0X:0:0:0:0:0:0:101'; //Network Time Protocol (NTP) [RFC1119,DLM1] + Id_IPv6MC_V_SGI_Dogfight = 'FF0X:0:0:0:0:0:0:102'; // SGI-Dogfight [AXC] + Id_IPv6MC_V_Rwhod = 'FF0X:0:0:0:0:0:0:103'; // Rwhod [SXD] + Id_IPv6MC_V_VMP = 'FF0X:0:0:0:0:0:0:104'; // VNP [DRC3] + Id_IPv6MC_V_Artificial_Horizons = 'FF0X:0:0:0:0:0:0:105'; // Artificial Horizons - Aviator [BXF] + Id_IPv6MC_V_NSS = 'FF0X:0:0:0:0:0:0:106'; // NSS - Name Service Server [BXS2] + Id_IPv6MC_V_AUDIONEWS = 'FF0X:0:0:0:0:0:0:107'; //AUDIONEWS - Audio News Multicast [MXF2] + Id_IPv6MC_V_SUN_NIS_Plus = 'FF0X:0:0:0:0:0:0:108'; // SUN NIS+ Information Service [CXM3] + Id_IPv6MC_V_MTP = 'FF0X:0:0:0:0:0:0:109'; // MTP Multicast Transport Protocol [SXA] + Id_IPv6MC_V_IETF_1_LOW_AUDIO = 'FF0X:0:0:0:0:0:0:10A'; // IETF-1-LOW-AUDIO [SC3] + Id_IPv6MC_V_IETF_1_AUDIO = 'FF0X:0:0:0:0:0:0:10B'; // IETF-1-AUDIO [SC3] + Id_IPv6MC_V_IETF_1_VIDEO = 'FF0X:0:0:0:0:0:0:10C'; // IETF-1-VIDEO [SC3] + Id_IPv6MC_V_IETF_2_LOW_AUDIO = 'FF0X:0:0:0:0:0:0:10D'; // IETF-2-LOW-AUDIO [SC3] + Id_IPv6MC_V_IETF_2_AUDIO = 'FF0X:0:0:0:0:0:0:10E'; // IETF-2-AUDIO [SC3] + Id_IPv6MC_V_IETF_2_VIDEO = 'FF0X:0:0:0:0:0:0:10F'; // IETF-2-VIDEO [SC3] + + Id_IPv6MC_V_MUSIC_SERVICE = 'FF0X:0:0:0:0:0:0:110'; // MUSIC-SERVICE [Guido van Rossum] + Id_IPv6MC_V_SEANET_TELEMETRY = 'FF0X:0:0:0:0:0:0:111'; // SEANET-TELEMETRY [Andrew Maffei] + Id_IPv6MC_V_SEANET_IMAGE = 'FF0X:0:0:0:0:0:0:112'; // SEANET-IMAGE [Andrew Maffei] + Id_IPv6MC_V_MLOADD = 'FF0X:0:0:0:0:0:0:113';// MLOADD [Braden] + Id_IPv6MC_V_any_private_experiment = 'FF0X:0:0:0:0:0:0:114'; // any private experiment [JBP] + Id_IPv6MC_V_DVMRP_on_MOSPF = 'FF0X:0:0:0:0:0:0:115'; // DVMRP on MOSPF [Moy] + Id_IPv6MC_V_SVRLOC = 'FF0X:0:0:0:0:0:0:116'; // SVRLOC [Guttman] + Id_IPv6MC_V_XINGTV = 'FF0X:0:0:0:0:0:0:117'; // XINGTV + Id_IPv6MC_V_microsoft_ds = 'FF0X:0:0:0:0:0:0:118'; // microsoft-ds + Id_IPv6MC_V_nbc_pro = 'FF0X:0:0:0:0:0:0:119'; // nbc-pro + Id_IPv6MC_V_nbc_pfn = 'FF0X:0:0:0:0:0:0:11A'; // nbc-pfn + Id_IPv6MC_V_lmsc_calren_1 = 'FF0X:0:0:0:0:0:0:11B'; // lmsc-calren-1 [Uang] + Id_IPv6MC_V_lmsc_calren_2 = 'FF0X:0:0:0:0:0:0:11C'; // lmsc-calren-2 [Uang] + Id_IPv6MC_V_lmsc_calren_3 = 'FF0X:0:0:0:0:0:0:11D'; // lmsc-calren-3 [Uang] + Id_IPv6MC_V_lmsc_calren_4 = 'FF0X:0:0:0:0:0:0:11E'; // lmsc-calren-4 [Uang] + Id_IPv6MC_V_ampr_info = 'FF0X:0:0:0:0:0:0:11F'; // ampr-info [Janssen] + + Id_IPv6MC_V_mtrace = 'FF0X:0:0:0:0:0:0:120'; // mtrace [Casner] + Id_IPv6MC_V_RSVP_encap_1 = 'FF0X:0:0:0:0:0:0:121'; // RSVP-encap-1 [Braden] + Id_IPv6MC_V_RSVP_encap_2 = 'FF0X:0:0:0:0:0:0:122'; // RSVP-encap-2 [Braden] + Id_IPv6MC_V_SVRLOC_DA = 'FF0X:0:0:0:0:0:0:123'; // SVRLOC-DA [Guttman] + Id_IPv6MC_V_rln_server = 'FF0X:0:0:0:0:0:0:124'; // rln-server [Kean] + Id_IPv6MC_V_proshare_mc = 'FF0X:0:0:0:0:0:0:125'; // proshare-mc [Lewis] + Id_IPv6MC_V_dantz = 'FF0X:0:0:0:0:0:0:126'; // dantz [Yackle] + Id_IPv6MC_V_cisco_rp_announce = 'FF0X:0:0:0:0:0:0:127'; // cisco-rp-announce [Farinacci] + Id_IPv6MC_V_cisco_rp_discovery = 'FF0X:0:0:0:0:0:0:128'; // cisco-rp-discovery [Farinacci] + Id_IPv6MC_V_gatekeeper = 'FF0X:0:0:0:0:0:0:129'; // gatekeeper [Toga] + Id_IPv6MC_V_iberiagames = 'FF0X:0:0:0:0:0:0:12A'; // iberiagames [Marocho] + Id_IPv6MC_V_X_Display = 'FF0X:0:0:0:0:0:0:12B';// X Display [McKernan] + Id_IPv6MC_V_oap_multicast = 'FF0X:0:0:0:0:0:0:12C'; // oap-multicast [Eastham] + Id_IPv6MC_V_DvbServDisc = 'FF0X:0:0:0:0:0:0:12D'; // DvbServDisc [Willigen] + Id_IPv6MC_V_Ricoh_device_ctrl = 'FF0X:0:0:0:0:0:0:12E'; // Ricoh-device-ctrl [Ohhira] + Id_IPv6MC_V_Ricoh_device_ctrl2 = 'FF0X:0:0:0:0:0:0:12F'; // Ricoh-device-ctrl [Ohhira] + + Id_IPv6MC_V_UPnP = 'FF0X:0:0:0:0:0:0:130'; // UPnP [UPnP] 21 September 2006 + Id_IPv6MC_V_Systech_Mcast = 'FF0X:0:0:0:0:0:0:131'; // Systech Mcast [Jakubiec] 21 September 2006 + Id_IPv6MC_V_omasg = 'FF0X:0:0:0:0:0:0:132'; // omasg [Lipford] 21 September 2006 + + Id_IPv6MC_V_PTP_primary = 'FF0X:0:0:0:0:0:0:181';// PTP-primary [IEEE1588, K.Lee] 02 February 2007 + Id_IPv6MC_V_PTP_alternate1 = 'FF0X:0:0:0:0:0:0:182'; // PTP-alternate1 [IEEE1588, K.Lee] 02 February 2007 + Id_IPv6MC_V_PTP_alternate2 = 'FF0X:0:0:0:0:0:0:183'; // PTP-alternate2 [IEEE1588, K.Lee] 02 February 2007 + Id_IPv6MC_V_PTP_alternate3 = 'FF0X:0:0:0:0:0:0:184'; // PTP-alternate3 [IEEE1588, K.Lee] 02 February 2007 + + + Id_IPv6MC_V_rwho_Group = 'FF0X:0:0:0:0:0:0:201'; // "rwho" Group (BSD) (unofficial) [JBP] + Id_IPv6MC_V_SUN_RPC_PMAPPROC_CALLIT = 'FF0X:0:0:0:0:0:0:202'; // SUN RPC PMAPPROC_CALLIT [BXE1] + //1234 + Id_IPv6MC_V_Mbus_ipv6 = 'FF0X:0:0:0:0:0:0:300'; // Mbus/Ipv6 [RFC3259] + +// FF0X:0:0:0:0:0:2:0000 -FF0X:0:0:0:0:0:2:7FFD Multimedia Conference Calls [SC3] + Id_IPv6MC_V_SAPv1_Announcements = 'FF0X:0:0:0:0:0:2:7FFE'; // SAPv1 Announcements [SC3] + Id_IPv6MC_V_SAPv0_Announcements_deprecated = 'FF0X:0:0:0:0:0:2:7FFF'; // SAPv0 Announcements (deprecated) [SC3] +// FF0X:0:0:0:0:0:2:8000 -FF0X:0:0:0:0:0:2:FFFF SAP Dynamic Assignments [SC3] + +implementation + +end. diff --git a/indy/Core/IdBuffer.pas b/indy/Core/IdBuffer.pas new file mode 100644 index 0000000..58e8ec7 --- /dev/null +++ b/indy/Core/IdBuffer.pas @@ -0,0 +1,1030 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.47 1/24/2005 7:35:54 PM JPMugaas + Fixed CopyTIdIPV6Address. + + Rev 1.46 1/17/2005 7:28:44 PM JPMugaas + Added Index parameter to several functions so you can use TIdBuffer in a + random access manner instead of in a sequential manner. This is good for + some fixed-packet or data types. + + Added functions for reading and writing various types to TIdBuffer which use + Byte Order functions. This should facilitate a lot of development as this + gets used more. + + Rev 1.45 27.08.2004 21:58:18 Andreas Hausladen + Speed optimization ("const" for string parameters) + + Rev 1.44 2004.07.03 19:41:34 czhower + UTF8, SaveToStream + + Rev 1.43 6/11/2004 8:48:12 AM DSiders + Added "Do not Localize" comments. + + Rev 1.42 6/9/04 7:46:26 PM RLebeau + Updated ExtractToBytes() to allocate the output buffer only if the buffer + length is smaller than the requested number of bytes. + + Rev 1.41 5/29/04 10:44:58 PM RLebeau + Updated ExtractToBytes() to allocate the output buffer regardless of the + AAppend parameter + + Added empty string return value to Extract() when AByteCount <= 0 + + Rev 1.40 2004.05.20 11:39:06 AM czhower + IdStreamVCL + + Rev 1.39 2004.05.10 1:19:18 PM czhower + Removed unneeded code. + + Rev 1.38 5/3/2004 12:57:00 PM BGooijen + Fixes for 0-based + + Rev 1.37 2004.05.03 11:15:42 AM czhower + Changed Find to IndexOf and made 0 based to be consistent. + + Rev 1.36 2004.05.01 4:26:52 PM czhower + Added PeekByte + + Rev 1.35 2004.04.16 11:30:26 PM czhower + Size fix to IdBuffer, optimizations, and memory leaks + + Rev 1.34 2004.04.08 7:06:44 PM czhower + Peek support. + + Rev 1.33 2004.04.08 3:56:24 PM czhower + Fixed bug with Intercept byte count. Also removed Bytes from Buffer. + + Rev 1.32 2004.04.08 2:03:34 AM czhower + Fixes to Bytes. + + Rev 1.31 2004.04.07 3:59:44 PM czhower + Bug fix for WriteDirect. + + Rev 1.30 2004.04.07 3:46:30 PM czhower + Compile fix. + + Rev 1.29 4/7/2004 1:02:14 PM BGooijen + when extract* is called with -1 or no parameters all data it extracted + + Rev 1.28 2004.03.29 9:58:38 PM czhower + Is now buffered. Now takes 2/3rds the time as before. + + Rev 1.27 23/03/2004 18:33:44 CCostelloe + Bug fix: ReadLn returns a previously-read line if FBytes also accessed + in-between (causes MoveHeadToStartIfNecessary to be called) + + Rev 1.26 18/03/2004 20:24:26 CCostelloe + Speed improvement by adding FHeadIndex: 10 MB base64 decode reduced from 10 + hours to 62 seconds. + + Rev 1.25 2004.03.03 11:55:02 AM czhower + IdStream change + + Rev 1.24 3/1/04 7:33:12 PM RLebeau + Updated Remove() to call the OnBytesRemoved event handler. + + Rev 1.23 2004.02.03 4:17:14 PM czhower + For unit name changes. + + Rev 1.22 1/11/2004 5:48:48 PM BGooijen + Added AApend parameter to ExtractToBytes + + Rev 1.21 1/7/2004 8:36:32 PM BGooijen + Arguments were in wrong order + + Rev 1.20 22/11/2003 10:35:04 PM GGrieve + Reverse copy direction in TIdBuffer.ExtractToStream + + Rev 1.19 2003.10.24 10:44:54 AM czhower + IdStream implementation, bug fixes. + + Rev 1.18 10/15/2003 1:03:40 PM DSiders + Created resource strings for TIdBuffer.Find exceptions. + + Rev 1.17 2003.10.14 1:27:06 PM czhower + Uupdates + Intercept support + + Rev 1.16 2003.10.11 5:47:00 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.15 10/5/2003 10:24:20 PM BGooijen + Changed WriteBytes(var ...) to WriteBytes(const ...) + + Rev 1.14 10/3/2003 10:46:38 PM BGooijen + Fixed Range Check Exception, and fixed ExtractToStream + + Rev 1.13 2003.10.02 8:29:12 PM czhower + Changed names of byte conversion routines to be more readily understood and + not to conflict with already in use ones. + + Rev 1.12 2003.10.02 12:44:58 PM czhower + Comment added + + Rev 1.11 10/2/2003 5:23:14 PM GGrieve + make Bytes a public property + + Rev 1.10 10/2/2003 5:00:38 PM GGrieve + Fix bug in find - can't find last char + + Rev 1.9 2003.10.02 10:37:00 AM czhower + Comments + + Rev 1.8 10/2/2003 3:54:06 PM GGrieve + Finish cleaning up - no $IFDEFs but still optimal on both win32 and DontNet + + Rev 1.7 10/1/2003 10:58:38 PM BGooijen + Removed unused var + + Rev 1.6 10/1/2003 8:15:58 PM BGooijen + Fixed Range Check Error on D7 + + Rev 1.5 10/1/2003 8:02:22 PM BGooijen + Removed some ifdefs and improved code + + Rev 1.4 10/1/2003 10:49:02 PM GGrieve + Rework buffer for Octane Compability + + Rev 1.3 2003.10.01 2:30:44 PM czhower + .Net + + Rev 1.2 2003.10.01 1:37:32 AM czhower + .Net + + Rev 1.1 2003.10.01 1:12:32 AM czhower + .Net + + Rev 1.0 2003.09.30 10:33:56 PM czhower + Readd after accidental delete. + + Rev 1.14 2003.09.30 10:33:16 PM czhower + Updates + + Rev 1.13 2003.07.16 5:05:06 PM czhower + Phase 1 of IdBuffer changes for compat. + + Rev 1.12 6/29/2003 10:56:22 PM BGooijen + Removed .Memory from the buffer, and added some extra methods + + Rev 1.11 2003.06.25 4:29:06 PM czhower + Free --> FreeAndNil + + Rev 1.10 2003.01.17 2:18:36 PM czhower + + Rev 1.9 12-14-2002 22:08:24 BGooijen + Changed FMemory to FMemory.Memory in some places + + Rev 1.8 12-14-2002 22:02:34 BGooijen + changed Memory to FMemory in some places, to remove some issues + + Rev 1.7 12/11/2002 04:27:02 AM JPMugaas + Fixed compiler warning. + + Rev 1.6 12/11/2002 03:53:44 AM JPMugaas + Merged the buffer classes. + + Rev 1.5 2002.12.07 12:26:18 AM czhower + + Rev 1.4 12-6-2002 20:34:06 BGooijen + Now compiles on Delphi 5 + + Rev 1.3 6/12/2002 11:00:14 AM SGrobety + + Rev 1.2 12/5/2002 02:55:44 PM JPMugaas + Added AddStream method for reading a stream into the buffer class. + + Rev 1.1 23.11.2002 12:59:48 JBerg + fixed packbuffer + + Rev 1.0 11/13/2002 08:38:32 AM JPMugaas +} +unit IdBuffer; + +{$I IdCompilerDefines.inc} + +{ + .Net forces us to perform copies from strings to Bytes so that it can do the + proper unicode and other conversions. + + IdBuffer is for storing data we cannot deal with right now and we do not know + the size. It must be optimized for adding to the end, and extracting from the + beginning. First pass we are just making it work, later using bubbling we will + optimize it for such tasks. + + The copy is a separate issue and we considered several options. For .net we will + always have to copy data to send or to receive to translate it to binary. For + example if we have a string it must be converted to bytes. This conversion + requires a copy. All strings are Unicode and must be converted to single + bytes by a convertor. This is not limited to strings. + + In VCL previously all strings were AnsiString so we used a pointer and just + accessed the memory directly from the string. This avoided the overhead of a + copy. + + We have come up with several ideas on how to allow the copy on .net, while + avoiding the copy on VCL to keep the performance benefit. However we must do + it in a single source manner and in a manner that does not impact the code + negatively. + + For now for VCL we also do a copy. This has the advantage that Byte arrays are + reference counted and automaticaly handled by Delphi. For example: + + WriteBytes(StringToBytes(s)); + + The array returned by this function will automatically be freed by Delphi. + + There are other options that are nearly as transparent but have the additional + overhead of requiring class creation. These classes can be used to copy for .net + and proxy on VCL. It all works very nice and has low memory overhead. The + objects can then be freed by default in methods that accept them. + + However after analysis, copy on VCL may not be that bad after all. The copy + only really impacts strings. The overhead to copy strings is minimal and only + used in commands etc. The big transfers come from files, streams, or other. + Such transfers have to be mapped into memory in VCL anyways, and if we map + directly into the byte array instead of the previous classes peformance should + be fine. + + In short - copy under VCL should be acceptable if we watch for bottlenecks and + fix them appropriately without having to creat proxy classes. The only problem + remains for transmitting large memory blocks. But if this is done against a + fixed copy buffer the performance hit will be neglible and it is not a common + task to transmit large memory blocks. + + For such transfers from streams, etc the user can declare a persistent array + of bytes that is not freed between each call to WriteBytes. + + -Kudzu +} + +interface + +uses + Classes, + IdException, + IdGlobal, + SysUtils; + +type + EIdNotEnoughDataInBuffer = class(EIdException); + EIdTooMuchDataInBuffer = class(EIdException); // only 2GB is allowed - + + TIdBufferBytesRemoved = procedure(ASender: TObject; ABytes: Integer) of object; + + // TIdBuffer is used as an internal buffer to isolate Indy from pointers and + // memory allocations. It also allows optimizations to be kept in a single place. + // + // TIdBuffer is primarily used as a read/write buffer for the communication layer. + + TIdBuffer = class(TObject) + private + function GetAsString: string; + protected + FBytes: TIdBytes; + FByteEncoding: IIdTextEncoding; + {$IFDEF STRING_IS_ANSI} + FAnsiEncoding: IIdTextEncoding; + {$ENDIF} + FGrowthFactor: Integer; + FHeadIndex: Integer; + FOnBytesRemoved: TIdBufferBytesRemoved; + FSize: Integer; + // + procedure CheckAdd(AByteCount : Integer; const AIndex : Integer); + procedure CheckByteCount(var VByteCount : Integer; const AIndex : Integer); + function GetCapacity: Integer; + procedure SetCapacity(AValue: Integer); + public + procedure Clear; + constructor Create; overload; + constructor Create(AOnBytesRemoved: TIdBufferBytesRemoved); overload; + constructor Create(AGrowthFactor: Integer); overload; + constructor Create(const ABytes : TIdBytes; const ALength : Integer = -1); overload; + procedure CompactHead(ACanShrink: Boolean = True); + destructor Destroy; override; + { + Most of these now have an AIndex parameter. If that is less than 0, + we are accessing data sequentially. That means, read the data from the HeadIndex + and "remove" the data you read. + + If AIndex is 0 or greater, the HeadIndex is disregarded and no deletion is done. + You are just reading from a particular location in a random access manner. + + } + // will extract number of bytes and decode as specified + function Extract(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToString()'{$ENDIF};{$ENDIF} + function ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + // all 3 extract routines append to existing data, if any + procedure ExtractToStream(const AStream: TStream; AByteCount: Integer = -1; const AIndex: Integer = -1); + procedure ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer = -1; const AIndex : Integer = -1); + procedure ExtractToBytes(var VBytes: TIdBytes; AByteCount: Integer = -1; + AAppend: Boolean = True; AIndex : Integer = -1); + function ExtractToUInt8(const AIndex : Integer): UInt8; + function ExtractToByte(const AIndex : Integer): UInt8; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt8()'{$ENDIF};{$ENDIF} + function ExtractToUInt16(const AIndex : Integer): UInt16; + function ExtractToWord(const AIndex : Integer): UInt16; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt16()'{$ENDIF};{$ENDIF} + function ExtractToUInt32(const AIndex : Integer): UInt32; + function ExtractToLongWord(const AIndex : Integer): UInt32; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt32()'{$ENDIF};{$ENDIF} + function ExtractToUInt64(const AIndex : Integer): TIdUInt64; + procedure ExtractToIPv6(const AIndex : Integer; var VAddress: TIdIPv6Address); + function IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer; overload; + function IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer; overload; + function IndexOf(const AString: string; AStartPos: Integer = 0; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): Integer; overload; + function PeekByte(AIndex: Integer): Byte; + procedure Remove(AByteCount: Integer); + procedure SaveToStream(const AStream: TStream); + { Most of these now have an ADestIndex parameter. If that is less than 0, + we are writing data sequentially. + + If ADestIndex is 0 or greater, you are setting bytes in a particular + location in a random access manner. + } + // Write + procedure Write(const AString: string; AByteEncoding: IIdTextEncoding = nil; + const ADestIndex: Integer = -1 + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Write(const ABytes: TIdBytes; const ADestIndex: Integer = -1); overload; + procedure Write(const ABytes: TIdBytes; const ALength, AOffset : Integer; const ADestIndex: Integer = -1); overload; + procedure Write(AStream: TStream; AByteCount: Integer = 0); overload; + procedure Write(const AValue: TIdUInt64; const ADestIndex: Integer = -1); overload; + procedure Write(const AValue: UInt32; const ADestIndex: Integer = -1); overload; + procedure Write(const AValue: UInt16; const ADestIndex: Integer = -1); overload; + procedure Write(const AValue: UInt8; const ADestIndex: Integer = -1); overload; + procedure Write(const AValue: TIdIPv6Address; const ADestIndex: Integer = -1); overload; + // + //Kudzu: I have removed the Bytes property. Do not add it back - it allowed "internal" access + // which caused compacting or internal knowledge. Access via Extract or other such methods + // instead. Bytes could also be easily confused with FBytes internally and cause issues. + // + // Bytes also allowed direct acces without removing which could cause concurrency issues if + // the reference was kept. + // + property Capacity: Integer read GetCapacity write SetCapacity; + property Encoding: IIdTextEncoding read FByteEncoding write FByteEncoding; + {$IFDEF STRING_IS_ANSI} + property AnsiEncoding: IIdTextEncoding read FAnsiEncoding write FAnsiEncoding; + {$ENDIF} + property GrowthFactor: Integer read FGrowthFactor write FGrowthFactor; + property Size: Integer read FSize; + //useful for testing. returns buffer as string without extraction. + property AsString: string read GetAsString; + end; + +implementation + +uses + IdResourceStringsCore, + IdStream, + IdStack; //needed for byte order functions + +procedure TIdBuffer.CheckAdd(AByteCount : Integer; const AIndex : Integer); +begin + if (MaxInt - AByteCount) < (Size + AIndex) then begin + raise EIdTooMuchDataInBuffer.Create(RSTooMuchDataInBuffer); + end; +end; + +procedure TIdBuffer.CheckByteCount(var VByteCount : Integer; const AIndex : Integer); +begin + if VByteCount = -1 then begin + VByteCount := Size+AIndex; + end + else if VByteCount > (Size+AIndex) then begin + raise EIdNotEnoughDataInBuffer.CreateFmt('%s (%d/%d)', [RSNotEnoughDataInBuffer, VByteCount, Size]); {do not localize} + end; +end; + +procedure TIdBuffer.Clear; +begin + SetLength(FBytes, 0); + FHeadIndex := 0; + FSize := Length(FBytes); +end; + +constructor TIdBuffer.Create(AGrowthFactor: Integer); +begin + Create; + FGrowthFactor := AGrowthFactor; +end; + +constructor TIdBuffer.Create(AOnBytesRemoved: TIdBufferBytesRemoved); +begin + Create; + FOnBytesRemoved := AOnBytesRemoved; +end; + +constructor TIdBuffer.Create(const ABytes: TIdBytes; const ALength: Integer); +begin + Create; + if ALength < 0 then + begin + FBytes := ABytes; + FSize := Length(ABytes); + end else + begin + SetLength(FBytes, ALength); + if ALength > 0 then + begin + CopyTIdBytes(ABytes, 0, FBytes, 0, ALength); + FSize := ALength; + end; + end; +end; + +destructor TIdBuffer.Destroy; +begin + Clear; + inherited Destroy; + //do only at the last moment + TIdStack.DecUsage; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdBuffer.Extract(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ExtractToString(AByteCount, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); +end; + +function TIdBuffer.ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LBytes: TIdBytes; +begin + if AByteCount < 0 then begin + AByteCount := Size; + end; + if AByteCount > 0 then + begin + if AByteEncoding = nil then begin + AByteEncoding := FByteEncoding; + EnsureEncoding(AByteEncoding); + end; + {$IFDEF STRING_IS_ANSI} + if ADestEncoding = nil then begin + ADestEncoding := FAnsiEncoding; + EnsureEncoding(ADestEncoding, encOSDefault); + end; + {$ENDIF} + ExtractToBytes(LBytes, AByteCount); + Result := BytesToString(LBytes, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + end else begin + Result := ''; + end; +end; + +procedure TIdBuffer.ExtractToBytes(var VBytes: TIdBytes; AByteCount: Integer = -1; + AAppend: Boolean = True; AIndex : Integer = -1); +var + LOldSize: Integer; + LIndex : Integer; +begin + if AByteCount < 0 then begin + AByteCount := Size; + end; + LIndex := IndyMax(AIndex, 0); + if AByteCount > 0 then begin + CheckByteCount(AByteCount, LIndex); + if AAppend then begin + LOldSize := Length(VBytes); + SetLength(VBytes, LOldSize + AByteCount); + end else begin + LOldSize := 0; + if Length(VBytes) < AByteCount then begin + SetLength(VBytes, AByteCount); + end; + end; + if AIndex < 0 then + begin + CopyTIdBytes(FBytes, FHeadIndex, VBytes, LOldSize, AByteCount); + Remove(AByteCount); + end else + begin + CopyTIdBytes(FBytes, AIndex, VBytes, LOldSize, AByteCount); + end; + end; +end; + +procedure TIdBuffer.ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer = -1; + const AIndex: Integer = -1); +var + LBytes: TIdBytes; +begin + if AByteCount < 0 then begin + AByteCount := Size; + end; + //TODO: Optimize this routine to directly copy from one to the other + ExtractToBytes(LBytes, AByteCount, True, AIndex); + ABuffer.Write(LBytes); +end; + +procedure TIdBuffer.ExtractToStream(const AStream: TStream; AByteCount: Integer = -1; + const AIndex: Integer = -1); +var + LIndex : Integer; + LBytes : TIdBytes; +begin + if AByteCount < 0 then begin + AByteCount := Size; + end; + LIndex := IndyMax(AIndex, 0); + if AIndex < 0 then + begin + // TODO: remove CompactHead() here and pass FHeadIndex to TIdStreamHelper.Write(): + { + CheckByteCount(AByteCount, FHeadIndex); + TIdStreamHelper.Write(AStream, FBytes, AByteCount, FHeadIndex); + Remove(AByteCount); + } + CompactHead; + CheckByteCount(AByteCount, LIndex); + TIdStreamHelper.Write(AStream, FBytes, AByteCount); + Remove(AByteCount); + end else + begin + // TODO: remove CopyTIdBytes() here and pass FBytes and AIndex to TIdStreamHelper.Write(): + { + CheckByteCount(AByteCount, LIndex); + TIdStreamHelper.Write(AStream, FBytes, AByteCount, AIndex); + } + CheckByteCount(AByteCount, LIndex); + SetLength(LBytes, AByteCount); + CopyTIdBytes(FBytes, AIndex, LBytes, 0, AByteCount); + TIdStreamHelper.Write(AStream, LBytes, AByteCount); + end; +end; + +procedure TIdBuffer.Remove(AByteCount: Integer); +begin + if AByteCount >= Size then begin + Clear; + end else begin + Inc(FHeadIndex, AByteCount); + Dec(FSize, AByteCount); + if FHeadIndex > GrowthFactor then begin + CompactHead; + end; + end; + if Assigned(FOnBytesRemoved) then begin + FOnBytesRemoved(Self, AByteCount); + end; +end; + +procedure TIdBuffer.CompactHead(ACanShrink: Boolean = True); +begin + // Only try to compact if needed. + if FHeadIndex > 0 then begin + CopyTIdBytes(FBytes, FHeadIndex, FBytes, 0, Size); + FHeadIndex := 0; + if ACanShrink and ((Capacity - Size - FHeadIndex) > GrowthFactor) then begin + SetLength(FBytes, FHeadIndex + Size + GrowthFactor); + end; + end; +end; + +procedure TIdBuffer.Write(const ABytes: TIdBytes; const ADestIndex: Integer = -1); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Write(ABytes, Length(ABytes), 0, ADestIndex); +end; + +procedure TIdBuffer.Write(AStream: TStream; AByteCount: Integer); +var + LAdded: Integer; + LLength: Integer; +begin + if AByteCount < 0 then begin + // Copy remaining + LAdded := AStream.Size - AStream.Position; + end else if AByteCount = 0 then begin + // Copy all + AStream.Position := 0; + LAdded := AStream.Size; + end else begin + LAdded := IndyMin(AByteCount, AStream.Size - AStream.Position); + end; + if LAdded > 0 then begin + LLength := Size; + CheckAdd(LAdded, 0); + CompactHead; + SetLength(FBytes, LLength + LAdded); + TIdStreamHelper.ReadBytes(AStream, FBytes, LAdded, LLength); + Inc(FSize, LAdded); + end; +end; + +function TIdBuffer.IndexOf(const AString: string; AStartPos: Integer = 0; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): Integer; +begin + if AByteEncoding = nil then begin + AByteEncoding := FByteEncoding; + end; + {$IFDEF STRING_IS_ANSI} + if ASrcEncoding = nil then begin + ASrcEncoding := FAnsiEncoding; + end; + {$ENDIF} + Result := IndexOf( + ToBytes(AString, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), + AStartPos); +end; + +function TIdBuffer.IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer; +var + i, j, LEnd, BytesLen: Integer; + LFound: Boolean; +begin + Result := -1; + // Dont search if it empty + if Size > 0 then begin + if Length(ABytes) = 0 then begin + raise EIdException.Create(RSBufferMissingTerminator); + end; + if (AStartPos < 0) or (AStartPos >= Size) then begin + raise EIdException.Create(RSBufferInvalidStartPos); + end; + BytesLen := Length(ABytes); + LEnd := FHeadIndex + Size; + for i := FHeadIndex + AStartPos to LEnd - BytesLen do begin + LFound := True; + for j := 0 to BytesLen - 1 do begin + if (i + j) >= LEnd then begin + Break; + end; + if FBytes[i + j] <> ABytes[j] then begin + LFound := False; + Break; + end; + end; + if LFound then begin + Result := i - FHeadIndex; + Break; + end; + end; + end; +end; + +function TIdBuffer.IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer; +var + i: Integer; +begin + Result := -1; + // Dont search if it empty + if Size > 0 then begin + if (AStartPos < 0) or (AStartPos >= Size) then begin + raise EIdException.Create(RSBufferInvalidStartPos); + end; + for i := (FHeadIndex + AStartPos) to (FHeadIndex + Size - 1) do begin + if FBytes[i] = AByte then begin + Result := i - FHeadIndex; + Break; + end; + end; + end; +end; + +procedure TIdBuffer.Write(const AString: string; AByteEncoding: IIdTextEncoding = nil; + const ADestIndex : Integer = -1 + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + if AByteEncoding = nil then begin + AByteEncoding := FByteEncoding; + end; + {$IFDEF STRING_IS_ANSI} + if ASrcEncoding = nil then begin + ASrcEncoding := FAnsiEncoding; + end; + {$ENDIF} + Write( + ToBytes(AString, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), + ADestIndex); +end; + +function TIdBuffer.GetCapacity: Integer; +begin + Result := Length(FBytes); +end; + +procedure TIdBuffer.SetCapacity(AValue: Integer); +begin + if AValue < Size then begin + raise EIdException.Create('Capacity cannot be smaller than Size'); {do not localize} + end; + CompactHead; + SetLength(FBytes, AValue); +end; + +constructor TIdBuffer.Create; +begin + inherited Create; + FGrowthFactor := 2048; + Clear; + TIdStack.IncUsage; +end; + +function TIdBuffer.PeekByte(AIndex: Integer): Byte; +begin + if Size = 0 then begin + raise EIdException.Create('No bytes in buffer.'); {do not localize} + end; + if (AIndex < 0) or (AIndex >= Size) then begin + raise EIdException.Create('Index out of bounds.'); {do not localize} + end; + Result := FBytes[FHeadIndex + AIndex]; +end; + +procedure TIdBuffer.SaveToStream(const AStream: TStream); +begin + CompactHead(False); + TIdStreamHelper.Write(AStream, FBytes, Size); +end; + +procedure TIdBuffer.ExtractToIPv6(const AIndex: Integer; var VAddress: TIdIPv6Address); +var + LIndex : Integer; +begin + if AIndex < 0 then begin + LIndex := FHeadIndex; + end else begin + LIndex := AIndex; + end; + BytesToIPv6(FBytes, VAddress, LIndex); + VAddress := GStack.NetworkToHost(VAddress); + if AIndex < 0 then begin + Remove(16); + end; +end; + +function TIdBuffer.ExtractToUInt64(const AIndex: Integer): TIdUInt64; +var + LIndex : Integer; +begin + if AIndex < 0 then begin + LIndex := FHeadIndex; + end else begin + LIndex := AIndex; + end; + Result := BytesToUInt64(FBytes, LIndex); + Result := GStack.NetworkToHost(Result); + if AIndex < 0 then begin + Remove(8); + end; +end; + +function TIdBuffer.ExtractToUInt32(const AIndex: Integer): UInt32; +var + LIndex : Integer; +begin + if AIndex < 0 then begin + LIndex := FHeadIndex; + end else begin + LIndex := AIndex; + end; + Result := BytesToUInt32(FBytes, LIndex); + Result := GStack.NetworkToHost(Result); + if AIndex < 0 then begin + Remove(4); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdBuffer.ExtractToLongWord(const AIndex: Integer): UInt32; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ExtractToUInt32(AIndex); +end; + +function TIdBuffer.ExtractToUInt16(const AIndex: Integer): UInt16; +var + LIndex : Integer; +begin + if AIndex < 0 then begin + LIndex := FHeadIndex; + end else begin + LIndex := AIndex; + end; + Result := BytesToUInt16(FBytes, LIndex); + Result := GStack.NetworkToHost(Result); + if AIndex < 0 then begin + Remove(2); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdBuffer.ExtractToWord(const AIndex: Integer): UInt16; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ExtractToUInt16(AIndex); +end; + +function TIdBuffer.ExtractToUInt8(const AIndex: Integer): UInt8; +var + LIndex : Integer; +begin + if AIndex < 0 then begin + LIndex := FHeadIndex; + end else begin + LIndex := AIndex; + end; + Result := FBytes[LIndex]; + if AIndex < 0 then begin + Remove(1); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdBuffer.ExtractToByte(const AIndex: Integer): UInt8; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ExtractToUInt8(AIndex); +end; + +procedure TIdBuffer.Write(const AValue: UInt16; const ADestIndex: Integer); +var + LVal : UInt16; + LIndex : Integer; +begin + if ADestIndex < 0 then + begin + LIndex := FHeadIndex + Size; + SetLength(FBytes, LIndex+2); + end else + begin + LIndex := ADestIndex; + end; + LVal := GStack.HostToNetwork(AValue); + CopyTIdUInt16(LVal, FBytes, LIndex); + if LIndex >= FSize then begin + FSize := LIndex+2; + end; +end; + +procedure TIdBuffer.Write(const AValue: UInt8; const ADestIndex: Integer); +var + LIndex : Integer; +begin + if ADestIndex < 0 then + begin + LIndex := FHeadIndex + Size; + SetLength(FBytes, LIndex+1); + end else + begin + LIndex := ADestIndex; + end; + FBytes[LIndex] := AValue; + if LIndex >= FSize then begin + FSize := LIndex+1; + end; +end; + +procedure TIdBuffer.Write(const AValue: TIdIPv6Address; const ADestIndex: Integer); +var + LVal : TIdIPv6Address; + LIndex : Integer; +begin + if ADestIndex < 0 then + begin + LIndex := FHeadIndex + Size; + SetLength(FBytes, LIndex + 16); + end else + begin + LIndex := ADestIndex; + end; + LVal := GStack.HostToNetwork(AValue); + CopyTIdIPV6Address(LVal, FBytes, LIndex); + if LIndex >= FSize then begin + FSize := LIndex+16; + end; +end; + +procedure TIdBuffer.Write(const AValue: TIdUInt64; const ADestIndex: Integer); +var + LVal: TIdUInt64; + LIndex: Integer; +begin + if ADestIndex < 0 then + begin + LIndex := FHeadIndex + Size; + SetLength(FBytes, LIndex + 8); + end else + begin + LIndex := ADestIndex; + end; + LVal := GStack.HostToNetwork(AValue); + CopyTIdUInt64(LVal, FBytes, LIndex); + if LIndex >= FSize then begin + FSize := LIndex + 8; + end; +end; + +procedure TIdBuffer.Write(const AValue: UInt32; const ADestIndex: Integer); +var + LVal : UInt32; + LIndex : Integer; +begin + if ADestIndex < 0 then + begin + LIndex := FHeadIndex + Size; + SetLength(FBytes, LIndex + 4); + end else + begin + LIndex := ADestIndex; + end; + LVal := GStack.HostToNetwork(AValue); + CopyTIdUInt32(LVal, FBytes, LIndex); + if LIndex >= FSize then begin + FSize := LIndex+4; + end; +end; + +procedure TIdBuffer.Write(const ABytes: TIdBytes; const ALength, AOffset : Integer; + const ADestIndex: Integer = -1); +var + LByteLength: Integer; + LIndex : Integer; +begin + LByteLength := IndyLength(ABytes, ALength, AOffset); + if LByteLength = 0 then begin + Exit; + end; + LIndex := IndyMax(ADestIndex, 0); + CheckAdd(LByteLength, LIndex); + if Size = 0 then begin + FHeadIndex := 0; + if ADestIndex < 0 then + begin + FBytes := ToBytes(ABytes, LByteLength, AOffset); + FSize := LByteLength; + end else + begin + FSize := ADestIndex + LByteLength; + SetLength(FBytes, FSize); + CopyTIdBytes(ABytes, AOffset, FBytes, ADestIndex, LByteLength); + end; + end + else if ADestIndex < 0 then + begin + CompactHead(False); + if (Capacity - Size - FHeadIndex) < LByteLength then begin + SetLength(FBytes, Size + LByteLength + GrowthFactor); + end; + CopyTIdBytes(ABytes, AOffset, FBytes, FHeadIndex + Size, LByteLength); + Inc(FSize, LByteLength); + end else + begin + CopyTIdBytes(ABytes, AOffset, FBytes, LIndex, LByteLength); + if LIndex >= FSize then begin + FSize := LIndex + LByteLength; + end; + end; +end; + +function TIdBuffer.GetAsString: string; +begin + Result := BytesToString(FBytes, FByteEncoding + {$IFDEF STRING_IS_ANSI}, FAnsiEncoding{$ENDIF} + ); +end; + +end. + + diff --git a/indy/Core/IdCmdTCPClient.pas b/indy/Core/IdCmdTCPClient.pas new file mode 100644 index 0000000..1c1226c --- /dev/null +++ b/indy/Core/IdCmdTCPClient.pas @@ -0,0 +1,305 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.18 2/8/05 5:24:48 PM RLebeau + Updated Disconnect() to not wait for the listening thread to terminate until + after the inherited Disconnect() is called, so that the socket is actually + disconnected and the thread can terminate properly. + + Rev 1.17 2/1/05 12:38:30 AM RLebeau + Removed unused CommandHandlersEnabled property + + Rev 1.16 6/11/2004 8:48:16 AM DSiders + Added "Do not Localize" comments. + + Rev 1.15 5/18/04 9:12:26 AM RLebeau + Bug fix for SetExceptionReply() property setter + + Rev 1.14 5/16/04 5:18:04 PM RLebeau + Added setter method to ExceptionReply property + + Rev 1.13 5/10/2004 6:10:38 PM DSiders + Removed unused member var FCommandHandlersInitialized. + + Rev 1.12 2004.03.06 1:33:00 PM czhower + -Change to disconnect + -Addition of DisconnectNotifyPeer + -WriteHeader now write bufers + + Rev 1.11 2004.03.01 5:12:24 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.10 2004.02.03 4:17:10 PM czhower + For unit name changes. + + Rev 1.9 2004.01.20 10:03:22 PM czhower + InitComponent + + Rev 1.8 1/4/04 8:46:16 PM RLebeau + Added OnBeforeCommandHandler and OnAfterCommandHandler events + + Rev 1.7 11/4/2003 10:25:40 PM DSiders + Removed duplicate FReplyClass member in TIdCmdTCPClient (See + TIdTCPConnection). + + Rev 1.6 10/21/2003 10:54:20 AM JPMugaas + Fix for new API change. + + Rev 1.5 2003.10.18 9:33:24 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.4 2003.10.02 10:16:26 AM czhower + .Net + + Rev 1.3 2003.09.19 11:54:26 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.2 7/9/2003 10:55:24 PM BGooijen + Restored all features + + Rev 1.1 7/9/2003 04:36:06 PM JPMugaas + You now can override the TIdReply with your own type. This should illiminate + some warnings about some serious issues. TIdReply is ONLY a base class with + virtual methods. + + Rev 1.0 7/7/2003 7:06:40 PM SPerry + Component that uses command handlers + + Rev 1.0 7/6/2003 4:47:26 PM SPerry + Units that use Command handlers +} + +unit IdCmdTCPClient; + +{ + Original author: Sergio Perry + Description: TCP client that uses CommandHandlers +} + +interface + +{$I IdCompilerDefines.inc} + +uses + IdContext, + IdException, + IdGlobal, + IdReply, + IdResourceStringsCore, + IdThread, + IdTCPClient, + IdCommandHandlers; + +type + TIdCmdTCPClient = class; + + { Events } + TIdCmdTCPClientAfterCommandHandlerEvent = procedure(ASender: TIdCmdTCPClient; + AContext: TIdContext) of object; + TIdCmdTCPClientBeforeCommandHandlerEvent = procedure(ASender: TIdCmdTCPClient; + var AData: string; AContext: TIdContext) of object; + + { Listening Thread } + + TIdCmdClientContext = class(TIdContext) + protected + FClient: TIdCmdTCPClient; + public + property Client: TIdCmdTCPClient read FClient; + end; + + TIdCmdTCPClientListeningThread = class(TIdThread) + protected + FContext: TIdCmdClientContext; + FClient: TIdCmdTCPClient; + FRecvData: String; + // + procedure Run; override; + public + constructor Create(AClient: TIdCmdTCPClient); reintroduce; + destructor Destroy; override; + // + property Client: TIdCmdTCPClient read FClient; + property RecvData: String read FRecvData write FRecvData; + end; + + { TIdCmdTCPClient } + TIdCmdTCPClient = class(TIdTCPClient) + protected + FExceptionReply: TIdReply; + FListeningThread: TIdCmdTCPClientListeningThread; + FCommandHandlers: TIdCommandHandlers; + FOnAfterCommandHandler: TIdCmdTCPClientAfterCommandHandlerEvent; + FOnBeforeCommandHandler: TIdCmdTCPClientBeforeCommandHandlerEvent; + // + procedure DoAfterCommandHandler(ASender: TIdCommandHandlers; AContext: TIdContext); + procedure DoBeforeCommandHandler(ASender: TIdCommandHandlers; var AData: string; + AContext: TIdContext); + procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); virtual; + function GetCmdHandlerClass: TIdCommandHandlerClass; virtual; + procedure InitComponent; override; + procedure SetCommandHandlers(AValue: TIdCommandHandlers); + procedure SetExceptionReply(AValue: TIdReply); + public + procedure Connect; override; + destructor Destroy; override; + procedure Disconnect(ANotifyPeer: Boolean); override; + published + property CommandHandlers: TIdCommandHandlers read FCommandHandlers write SetCommandHandlers; + property ExceptionReply: TIdReply read FExceptionReply write SetExceptionReply; + // + property OnAfterCommandHandler: TIdCmdTCPClientAfterCommandHandlerEvent + read FOnAfterCommandHandler write FOnAfterCommandHandler; + property OnBeforeCommandHandler: TIdCmdTCPClientBeforeCommandHandlerEvent + read FOnBeforeCommandHandler write FOnBeforeCommandHandler; + end; + + EIdCmdTCPClientError = class(EIdException); + EIdCmdTCPClientConnectError = class(EIdCmdTCPClientError); + +implementation + +uses + IdReplyRFC, SysUtils; + +type + TIdCmdClientContextAccess = class(TIdCmdClientContext) + end; + +{ Listening Thread } + +constructor TIdCmdTCPClientListeningThread.Create(AClient: TIdCmdTCPClient); +begin + FClient := AClient; + FContext := TIdCmdClientContext.Create(AClient, nil, nil); + FContext.FClient := AClient; + TIdCmdClientContextAccess(FContext).FOwnsConnection := False; + // + inherited Create(False); +end; + +destructor TIdCmdTCPClientListeningThread.Destroy; +begin + inherited Destroy; + FreeAndNil(FContext); +end; + +procedure TIdCmdTCPClientListeningThread.Run; +begin + FRecvData := FClient.IOHandler.ReadLn; + if not FClient.CommandHandlers.HandleCommand(FContext, FRecvData) then begin + FClient.DoReplyUnknownCommand(FContext, FRecvData); + end; + //Synchronize(?); + if not Terminated then begin + FClient.IOHandler.CheckForDisconnect; + end; +end; + +{ TIdCmdTCPClient } + +destructor TIdCmdTCPClient.Destroy; +begin + Disconnect; + FreeAndNil(FExceptionReply); + FreeAndNil(FCommandHandlers); + inherited Destroy; +end; + +procedure TIdCmdTCPClient.Connect; +begin + inherited Connect; + // + try + FListeningThread := TIdCmdTCPClientListeningThread.Create(Self); + except + Disconnect(True); + IndyRaiseOuterException(EIdCmdTCPClientConnectError.Create(RSNoCreateListeningThread)); + end; +end; + +procedure TIdCmdTCPClient.Disconnect(ANotifyPeer: Boolean); +begin + if Assigned(FListeningThread) then begin + FListeningThread.Terminate; + end; + try + inherited Disconnect(ANotifyPeer); + finally + if Assigned(FListeningThread) and not IsCurrentThread(FListeningThread) then begin + FListeningThread.WaitFor; + FreeAndNil(FListeningThread); + end; + end; +end; + +procedure TIdCmdTCPClient.DoAfterCommandHandler(ASender: TIdCommandHandlers; + AContext: TIdContext); +begin + if Assigned(OnAfterCommandHandler) then begin + OnAfterCommandHandler(Self, AContext); + end; +end; + +procedure TIdCmdTCPClient.DoBeforeCommandHandler(ASender: TIdCommandHandlers; + var AData: string; AContext: TIdContext); +begin + if Assigned(OnBeforeCommandHandler) then begin + OnBeforeCommandHandler(Self, AData, AContext); + end; +end; + +procedure TIdCmdTCPClient.DoReplyUnknownCommand(AContext: TIdContext; ALine: string); +begin +end; + +function TIdCmdTCPClient.GetCmdHandlerClass: TIdCommandHandlerClass; +begin + Result := TIdCommandHandler; +end; + +procedure TIdCmdTCPClient.InitComponent; +var + LHandlerClass: TIdCommandHandlerClass; +begin + inherited InitComponent; + + FExceptionReply := FReplyClass.Create(nil); + ExceptionReply.SetReply(500, 'Unknown Internal Error'); {do not localize} + + LHandlerClass := GetCmdHandlerClass; + FCommandHandlers := TIdCommandHandlers.Create(Self, FReplyClass, nil, ExceptionReply, LHandlerClass); + FCommandHandlers.OnAfterCommandHandler := DoAfterCommandHandler; + FCommandHandlers.OnBeforeCommandHandler := DoBeforeCommandHandler; +end; + +procedure TIdCmdTCPClient.SetCommandHandlers(AValue: TIdCommandHandlers); +begin + FCommandHandlers.Assign(AValue); +end; + +procedure TIdCmdTCPClient.SetExceptionReply(AValue: TIdReply); +begin + FExceptionReply.Assign(AValue); +end; + +end. diff --git a/indy/Core/IdCmdTCPServer.pas b/indy/Core/IdCmdTCPServer.pas new file mode 100644 index 0000000..1bd03ab --- /dev/null +++ b/indy/Core/IdCmdTCPServer.pas @@ -0,0 +1,535 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.42 2/1/05 12:36:36 AM RLebeau + Removed CommandHandlersEnabled property, no longer used + + Rev 1.41 12/2/2004 9:26:42 PM JPMugaas + Bug fix. + + Rev 1.40 2004.10.27 9:20:04 AM czhower + For TIdStrings + + Rev 1.39 10/26/2004 8:42:58 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.38 6/21/04 10:07:14 PM RLebeau + Updated .DoConnect() to make sure the connection is still connected before + then sending the Greeting + + Rev 1.37 6/20/2004 12:01:44 AM DSiders + Added "Do Not Localize" comments. + + Rev 1.36 6/16/04 12:37:06 PM RLebeau + more compiler errors + + Rev 1.35 6/16/04 12:30:32 PM RLebeau + compiler errors + + Rev 1.34 6/16/04 12:12:26 PM RLebeau + Updated ExceptionReply, Greeting, HelpReply, MaxConnectionReply, and + ReplyUnknownCommand properties to use getter methods that call virtual Create + methods which descendants can override for class-specific initializations + + Rev 1.33 5/16/04 5:16:52 PM RLebeau + Added setter methods to ExceptionReply, HelpReply, and ReplyTexts properties + + Rev 1.32 4/19/2004 5:39:58 PM BGooijen + Added comment + + Rev 1.31 4/18/2004 11:58:44 PM BGooijen + Wasn't thread safe + + Rev 1.30 3/3/2004 4:59:38 AM JPMugaas + Updated for new properties. + + Rev 1.29 2004.03.01 5:12:24 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.28 2004.02.29 9:43:08 PM czhower + Added ReadCommandLine. + + Rev 1.27 2004.02.29 8:17:18 PM czhower + Minor cosmetic changes to code. + + Rev 1.26 2004.02.03 4:17:08 PM czhower + For unit name changes. + + Rev 1.25 03/02/2004 01:49:22 CCostelloe + Added DoReplyUnknownCommand to allow TIdIMAP4Server set a correct reply for + unknown commands + + Rev 1.24 1/29/04 9:43:16 PM RLebeau + Added setter methods to various TIdReply properties + + Rev 1.23 2004.01.20 10:03:22 PM czhower + InitComponent + + Rev 1.22 1/5/2004 2:35:36 PM JPMugaas + Removed of object in method declarations. + + Rev 1.21 1/5/04 10:12:58 AM RLebeau + Fixed Typos in OnBeforeCommandHandler and OnAfterCommandHandler events + + Rev 1.20 1/4/04 8:45:34 PM RLebeau + Added OnBeforeCommandHandler and OnAfterCommandHandler events + + Rev 1.19 1/1/2004 9:33:22 PM BGooijen + the abstract class TIdReply was created sometimes, fixed that + + Rev 1.18 2003.10.18 9:33:26 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.17 2003.10.18 8:03:58 PM czhower + Defaults for codes + + Rev 1.16 8/31/2003 11:49:40 AM BGooijen + removed FReplyClass, this was also in TIdTCPServer + + Rev 1.15 7/9/2003 10:55:24 PM BGooijen + Restored all features + + Rev 1.14 7/9/2003 04:36:08 PM JPMugaas + You now can override the TIdReply with your own type. This should illiminate + some warnings about some serious issues. TIdReply is ONLY a base class with + virtual methods. + + Rev 1.13 2003.07.08 2:26:02 PM czhower + Sergio's update + + Rev 1.0 7/7/2003 7:06:44 PM SPerry + Component that uses command handlers + + Rev 1.0 7/6/2003 4:47:32 PM SPerry + Units that use Command handlers + + Adapted to IdCommandHandlers.pas SPerry + + Rev 1.7 4/4/2003 8:08:00 PM BGooijen + moved some consts from tidtcpserver here + + Rev 1.6 3/23/2003 11:22:24 PM BGooijen + Moved some code to HandleCommand + + Rev 1.5 3/22/2003 1:46:36 PM BGooijen + Removed unused variables + + Rev 1.4 3/20/2003 12:18:30 PM BGooijen + Moved ReplyExceptionCode from TIdTCPServer to TIdCmdTCPServer + + Rev 1.3 3/20/2003 12:14:18 PM BGooijen + Re-enabled Server.ReplyException + + Rev 1.2 2/24/2003 07:21:50 PM JPMugaas + Now compiles with new core code restructures. + + Rev 1.1 1/23/2003 11:06:10 AM BGooijen + + Rev 1.0 1/20/2003 12:48:40 PM BGooijen + Tcpserver with command handlers, these were originally in TIdTcpServer, but + are now moved here +} + +unit IdCmdTCPServer; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdCommandHandlers, + IdContext, + IdIOHandler, + IdReply, + IdTCPServer, + SysUtils; + +type + TIdCmdTCPServer = class; + + { Events } + TIdCmdTCPServerAfterCommandHandlerEvent = procedure(ASender: TIdCmdTCPServer; + AContext: TIdContext) of object; + TIdCmdTCPServerBeforeCommandHandlerEvent = procedure(ASender: TIdCmdTCPServer; + var AData: string; AContext: TIdContext) of object; + + TIdCmdTCPServer = class(TIdTCPServer) + protected + FCommandHandlers: TIdCommandHandlers; + FCommandHandlersInitialized: Boolean; + FExceptionReply: TIdReply; + FHelpReply: TIdReply; + FGreeting: TIdReply; + FMaxConnectionReply: TIdReply; + FOnAfterCommandHandler: TIdCmdTCPServerAfterCommandHandlerEvent; + FOnBeforeCommandHandler: TIdCmdTCPServerBeforeCommandHandlerEvent; + FReplyClass: TIdReplyClass; + FReplyTexts: TIdReplies; + FReplyUnknownCommand: TIdReply; + // + procedure CheckOkToBeActive; override; + function CreateExceptionReply: TIdReply; virtual; + function CreateGreeting: TIdReply; virtual; + function CreateHelpReply: TIdReply; virtual; + function CreateMaxConnectionReply: TIdReply; virtual; + function CreateReplyUnknownCommand: TIdReply; virtual; + procedure DoAfterCommandHandler(ASender: TIdCommandHandlers; AContext: TIdContext); + procedure DoBeforeCommandHandler(ASender: TIdCommandHandlers; var AData: string; + AContext: TIdContext); + procedure DoConnect(AContext: TIdContext); override; + function DoExecute(AContext: TIdContext): Boolean; override; + procedure DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler); override; + // This is here to allow servers to override this functionality, such as IMAP4 server + procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); virtual; + function GetExceptionReply: TIdReply; + function GetGreeting: TIdReply; + function GetHelpReply: TIdReply; + function GetMaxConnectionReply: TIdReply; + function GetRepliesClass: TIdRepliesClass; virtual; + function GetReplyClass: TIdReplyClass; virtual; + function GetReplyUnknownCommand: TIdReply; + procedure InitializeCommandHandlers; virtual; + procedure InitComponent; override; + // This is used by command handlers as the only input. This can be overriden to filter, modify, + // or preparse the input. + function ReadCommandLine(AContext: TIdContext): string; virtual; + procedure Startup; override; + procedure SetCommandHandlers(AValue: TIdCommandHandlers); + procedure SetExceptionReply(AValue: TIdReply); + procedure SetGreeting(AValue: TIdReply); + procedure SetHelpReply(AValue: TIdReply); + procedure SetMaxConnectionReply(AValue: TIdReply); + procedure SetReplyUnknownCommand(AValue: TIdReply); + procedure SetReplyTexts(AValue: TIdReplies); + public + destructor Destroy; override; + published + property CommandHandlers: TIdCommandHandlers read FCommandHandlers + write SetCommandHandlers; + property ExceptionReply: TIdReply read GetExceptionReply write SetExceptionReply; + property Greeting: TIdReply read GetGreeting write SetGreeting; + property HelpReply: TIdReply read GetHelpReply write SetHelpReply; + property MaxConnectionReply: TIdReply read GetMaxConnectionReply + write SetMaxConnectionReply; + property ReplyTexts: TIdReplies read FReplyTexts write SetReplyTexts; + property ReplyUnknownCommand: TIdReply read GetReplyUnknownCommand + write SetReplyUnknownCommand; + // + property OnAfterCommandHandler: TIdCmdTCPServerAfterCommandHandlerEvent + read FOnAfterCommandHandler write FOnAfterCommandHandler; + property OnBeforeCommandHandler: TIdCmdTCPServerBeforeCommandHandlerEvent + read FOnBeforeCommandHandler write FOnBeforeCommandHandler; + end; + +implementation + +uses + IdGlobal, + IdResourceStringsCore, + IdReplyRFC; + +function TIdCmdTCPServer.GetReplyClass: TIdReplyClass; +begin + Result := TIdReplyRFC; +end; + +function TIdCmdTCPServer.GetRepliesClass: TIdRepliesClass; +begin + Result := TIdRepliesRFC; +end; + +destructor TIdCmdTCPServer.Destroy; +begin + inherited Destroy; + FreeAndNil(FReplyUnknownCommand); + FreeAndNil(FReplyTexts); + FreeAndNil(FMaxConnectionReply); + FreeAndNil(FHelpReply); + FreeAndNil(FGreeting); + FreeAndNil(FExceptionReply); + FreeAndNil(FCommandHandlers); +end; + +procedure TIdCmdTCPServer.DoAfterCommandHandler(ASender: TIdCommandHandlers; + AContext: TIdContext); +begin + if Assigned(OnAfterCommandHandler) then begin + OnAfterCommandHandler(Self, AContext); + end; +end; + +procedure TIdCmdTCPServer.DoBeforeCommandHandler(ASender: TIdCommandHandlers; + var AData: string; AContext: TIdContext); +begin + if Assigned(OnBeforeCommandHandler) then begin + OnBeforeCommandHandler(Self, AData, AContext); + end; +end; + +function TIdCmdTCPServer.DoExecute(AContext: TIdContext): Boolean; +var + LLine: string; +begin + if CommandHandlers.Count > 0 then begin + Result := True; + if AContext.Connection.Connected then begin + LLine := ReadCommandLine(AContext); + // OLX sends blank lines during reset groups (NNTP) and expects no response. + // Not sure what the RFCs say about blank lines. + // I telnetted to some newsservers, and they dont respond to blank lines. + // This unit is core and not NNTP, but we should be consistent. + if LLine <> '' then begin + if not FCommandHandlers.HandleCommand(AContext, LLine) then begin + DoReplyUnknownCommand(AContext, LLine); + end; + end; + end; + end else begin + Result := inherited DoExecute(AContext); + end; + if Result and Assigned(AContext.Connection) then begin + Result := AContext.Connection.Connected; + end; + // the return value is used to determine if the DoExecute needs to be called again by the thread +end; + +procedure TIdCmdTCPServer.DoReplyUnknownCommand(AContext: TIdContext; ALine: string); +var + LReply: TIdReply; +begin + if CommandHandlers.PerformReplies then begin + LReply := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); try + LReply.Assign(ReplyUnknownCommand); + LReply.Text.Add(ALine); + AContext.Connection.IOHandler.Write(LReply.FormattedReply); + finally + FreeAndNil(LReply); + end; + end; +end; + +procedure TIdCmdTCPServer.InitializeCommandHandlers; +begin +end; + +procedure TIdCmdTCPServer.DoConnect(AContext: TIdContext); +var + LGreeting: TIdReply; +begin + inherited DoConnect(AContext); + // RLebeau - check the connection first in case the application + // chose to disconnect the connection in the OnConnect event handler. + if AContext.Connection.Connected then begin + if Greeting.ReplyExists then begin + ReplyTexts.UpdateText(Greeting); + LGreeting := FReplyClass.Create(nil); try // SendGreeting calls TIdReply.GetFormattedReply + LGreeting.Assign(Greeting); // and that changes the reply object, so we have to + SendGreeting(AContext, LGreeting); // clone it to make it thread-safe + finally + FreeAndNil(LGreeting); + end; + end; + end; +end; + +procedure TIdCmdTCPServer.DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler); +begin + inherited DoMaxConnectionsExceeded(AIOHandler); + //Do not UpdateText here - in thread. Is done in constructor + AIOHandler.Write(MaxConnectionReply.FormattedReply); +end; + +procedure TIdCmdTCPServer.Startup; +var + i, j: Integer; + LDescr: TStrings; + LHelpList: TStringList; + LHandler, LAddedHandler: TIdCommandHandler; +begin + inherited Startup; + if not FCommandHandlersInitialized then begin + // InitializeCommandHandlers must be called only at runtime, and only after streaming + // has occured. This used to be in .Loaded and that worked for forms. It failed + // for dynamically created instances and also for descendant classes. + FCommandHandlersInitialized := True; + InitializeCommandHandlers; + if HelpReply.Code <> '' then begin + LAddedHandler := CommandHandlers.Add; + LAddedHandler.Command := 'Help'; {do not localize} + LAddedHandler.Description.Text := 'Displays commands that the servers supports.'; {do not localize} + LAddedHandler.NormalReply.Assign(HelpReply); + LHelpList := TStringList.Create; + try + for i := 0 to CommandHandlers.Count - 1 do begin + LHandler := CommandHandlers.Items[i]; + if LHandler.HelpVisible then begin + LHelpList.AddObject(LHandler.Command+LHandler.HelpSuperScript, LHandler); + end; + end; + LHelpList.Sort; + for i := 0 to LHelpList.Count - 1 do begin + LAddedHandler.Response.Add(LHelpList[i]); + LDescr := TIdCommandHandler(LHelpList.Objects[i]).Description; + for j := 0 to LDescr.Count - 1 do begin + LAddedHandler.Response.Add(' ' + LDescr[j]); {do not localize} + end; + LAddedHandler.Response.Add(''); {do not localize} + end; + finally + FreeAndNil(LHelpList); + end; + end; + end; +end; + +procedure TIdCmdTCPServer.SetCommandHandlers(AValue: TIdCommandHandlers); +begin + FCommandHandlers.Assign(AValue); +end; + +function TIdCmdTCPServer.CreateExceptionReply: TIdReply; +begin + Result := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(500, 'Unknown Internal Error'); {do not localize} +end; + +function TIdCmdTCPServer.GetExceptionReply: TIdReply; +begin + if FExceptionReply = nil then begin + FExceptionReply := CreateExceptionReply; + end; + Result := FExceptionReply; +end; + +procedure TIdCmdTCPServer.SetExceptionReply(AValue: TIdReply); +begin + ExceptionReply.Assign(AValue); +end; + +function TIdCmdTCPServer.CreateGreeting: TIdReply; +begin + Result := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(200, 'Welcome'); {do not localize} +end; + +function TIdCmdTCPServer.GetGreeting: TIdReply; +begin + if FGreeting = nil then begin + FGreeting := CreateGreeting; + end; + Result := FGreeting; +end; + +procedure TIdCmdTCPServer.SetGreeting(AValue: TIdReply); +begin + Greeting.Assign(AValue); +end; + +function TIdCmdTCPServer.CreateHelpReply: TIdReply; +begin + Result := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(100, 'Help follows'); {do not localize} +end; + +function TIdCmdTCPServer.GetHelpReply: TIdReply; +begin + if FHelpReply = nil then begin + FHelpReply := CreateHelpReply; + end; + Result := FHelpReply; +end; + +procedure TIdCmdTCPServer.SetHelpReply(AValue: TIdReply); +begin + HelpReply.Assign(AValue); +end; + +function TIdCmdTCPServer.CreateMaxConnectionReply: TIdReply; +begin + Result := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(300, 'Too many connections. Try again later.'); {do not localize} +end; + +function TIdCmdTCPServer.GetMaxConnectionReply: TIdReply; +begin + if FMaxConnectionReply = nil then begin + FMaxConnectionReply := CreateMaxConnectionReply; + end; + Result := FMaxConnectionReply; +end; + +procedure TIdCmdTCPServer.SetMaxConnectionReply(AValue: TIdReply); +begin + MaxConnectionReply.Assign(AValue); +end; + +function TIdCmdTCPServer.CreateReplyUnknownCommand: TIdReply; +begin + Result := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(400, 'Unknown Command'); {do not localize} +end; + +function TIdCmdTCPServer.GetReplyUnknownCommand: TIdReply; +begin + if FReplyUnknownCommand = nil then begin + FReplyUnknownCommand := CreateReplyUnknownCommand; + end; + Result := FReplyUnknownCommand; +end; + +procedure TIdCmdTCPServer.SetReplyUnknownCommand(AValue: TIdReply); +begin + ReplyUnknownCommand.Assign(AValue); +end; + +procedure TIdCmdTCPServer.SetReplyTexts(AValue: TIdReplies); +begin + FReplyTexts.Assign(AValue); +end; + +procedure TIdCmdTCPServer.InitComponent; +begin + inherited InitComponent; + FReplyClass := GetReplyClass; + + // Before Command handlers as they need FReplyTexts, but after FReplyClass is set + FReplyTexts := GetRepliesClass.Create(Self, FReplyClass); + + FCommandHandlers := TIdCommandHandlers.Create(Self, FReplyClass, ReplyTexts, ExceptionReply); + FCommandHandlers.OnAfterCommandHandler := DoAfterCommandHandler; + FCommandHandlers.OnBeforeCommandHandler := DoBeforeCommandHandler; +end; + +function TIdCmdTCPServer.ReadCommandLine(AContext: TIdContext): string; +begin + Result := AContext.Connection.IOHandler.ReadLn; +end; + +procedure TIdCmdTCPServer.CheckOkToBeActive; +begin + if (CommandHandlers.Count = 0) and FCommandHandlersInitialized then begin + inherited CheckOkToBeActive; + end; +end; + +end. diff --git a/indy/Core/IdCommandHandlers.pas b/indy/Core/IdCommandHandlers.pas new file mode 100644 index 0000000..fb05d3b --- /dev/null +++ b/indy/Core/IdCommandHandlers.pas @@ -0,0 +1,682 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.36 2/1/05 12:37:48 AM RLebeau + Removed IdCommandHandlersEnabledDefault variable, no longer used. + + Rev 1.35 1/3/05 4:43:20 PM RLebeau + Changed use of AnsiSameText() to use TextIsSame() instead + + Rev 1.34 12/17/04 12:54:04 PM RLebeau + Updated TIdCommandHandler.Check() to not match misspelled commands when a + CmdDelimiter is specified. + + Rev 1.33 12/10/04 1:48:04 PM RLebeau + Bug fix for TIdCommandHandler.DoCommand() + + Rev 1.32 10/26/2004 8:42:58 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.31 6/17/2004 2:19:50 AM JPMugaas + Problem with unparsed parameters. The initial deliniator between the command + and reply was being added to Unparsed Params leading some strange results and + command failures. + + Rev 1.30 6/6/2004 11:44:34 AM JPMugaas + Removed a temporary workaround for a Telnet Sequences issue in the + TIdFTPServer. That workaround is no longer needed as we fixed the issue + another way. + + Rev 1.29 5/16/04 5:20:22 PM RLebeau + Removed local variable from TIdCommandHandler constructor, no longer used + + Rev 1.28 2004.03.03 3:19:52 PM czhower + sorted + + Rev 1.27 3/3/2004 4:59:40 AM JPMugaas + Updated for new properties. + + Rev 1.26 3/2/2004 8:10:36 AM JPMugaas + HelpHide renamed to HelpVisable. + + Rev 1.25 3/2/2004 6:37:36 AM JPMugaas + Updated with properties for more comprehensive help systems. + + Rev 1.24 2004.03.01 7:13:40 PM czhower + Comaptibilty fix. + + Rev 1.23 2004.03.01 5:12:26 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.22 2004.02.29 9:49:06 PM czhower + Bug fix, and now responses are also write buffered. + + Rev 1.21 2004.02.03 4:17:10 PM czhower + For unit name changes. + + Rev 1.20 1/29/04 10:00:40 PM RLebeau + Added setter methods to various TIdReply properties + + Rev 1.19 2003.12.31 7:31:58 PM czhower + AnsiSameText --> TextIsSame + + Rev 1.18 10/19/2003 11:36:52 AM DSiders + Added localization comments where setting response codes. + + Rev 1.17 2003.10.18 9:33:26 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.16 2003.10.18 8:07:12 PM czhower + Fixed bug with defaults. + + Rev 1.15 2003.10.18 8:03:58 PM czhower + Defaults for codes + + Rev 1.14 10/5/2003 03:06:18 AM JPMugaas + Should compile. + + Rev 1.13 8/9/2003 3:52:44 PM BGooijen + TIdCommandHandlers can now create any TIdCommandHandler descendant. this + makes it possible to override TIdCommandHandler.check and check for the + command a different way ( binary commands, protocols where the string doesn't + start with the command ) + + Rev 1.12 8/2/2003 2:22:54 PM SPerry + Fixed OnCommandHandlersException problem + + Rev 1.11 8/2/2003 1:43:08 PM SPerry + Modifications to get command handlers to work + + Rev 1.9 7/30/2003 10:18:30 PM SPerry + Fixed AV when creating commandhandler (again) -- for some reason the bug + fixed in Rev. 1.7 was still there. + + Rev 1.8 7/30/2003 8:31:58 PM SPerry + Fixed AV with LFReplyClass. + + Rev 1.4 7/9/2003 10:55:26 PM BGooijen + Restored all features + + Rev 1.3 7/9/2003 04:36:10 PM JPMugaas + You now can override the TIdReply with your own type. This should illiminate + some warnings about some serious issues. TIdReply is ONLY a base class with + virtual methods. + + Rev 1.2 7/9/2003 01:43:22 PM JPMugaas + Should now compile. + + Rev 1.1 7/9/2003 2:56:44 PM SPerry + Added OnException event + + + Rev 1.0 7/6/2003 4:47:38 PM SPerry + Units that use Command handlers +} +unit IdCommandHandlers; + +{ + Original author: Chad Z. Hower + Separate Unit : Sergio Perry +} + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdBaseComponent, IdComponent, IdReply, IdGlobal, + IdContext, IdReplyRFC; + +const + IdEnabledDefault = True; + // DO NOT change this default (ParseParams). Many servers rely on this + IdParseParamsDefault = True; + IdHelpVisibleDef = True; +type + TIdCommandHandlers = class; + TIdCommandHandler = class; + TIdCommand = class; + + { Events } + TIdCommandEvent = procedure(ASender: TIdCommand) of object; + TIdAfterCommandHandlerEvent = procedure(ASender: TIdCommandHandlers; + AContext: TIdContext) of object; + TIdBeforeCommandHandlerEvent = procedure(ASender: TIdCommandHandlers; + var AData: string; AContext: TIdContext) of object; + TIdCommandHandlersExceptionEvent = procedure(ACommand: String; AContext: TIdContext) of object; + + { TIdCommandHandler } + TIdCommandHandler = class(TCollectionItem) + protected + FCmdDelimiter: Char; + FCommand: string; + {$IFDEF USE_OBJECT_ARC} + // When ARC is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so + // we will provide separate properties for those purposes + // + // TODO; use TValue instead of separating them + // + FDataObject: TObject; + FDataValue: PtrInt; + {$ELSE} + FData: TObject; + {$ENDIF} + FDescription: TStrings; + FDisconnect: boolean; + FEnabled: boolean; + FExceptionReply: TIdReply; + FHelpSuperScript : String; //may be something like * or + which should appear in help + FHelpVisible : Boolean; + FName: string; + FNormalReply: TIdReply; + FOnCommand: TIdCommandEvent; + FParamDelimiter: Char; + FParseParams: Boolean; + FReplyClass : TIdReplyClass; + FResponse: TStrings; + FTag: integer; + // + function GetDisplayName: string; override; + procedure SetDescription(AValue: TStrings); + procedure SetExceptionReply(AValue: TIdReply); + procedure SetNormalReply(AValue: TIdReply); + procedure SetResponse(AValue: TStrings); + public + function Check(const AData: string; AContext: TIdContext): boolean; virtual; + procedure DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string); virtual; + procedure DoParseParams(AUnparsedParams: string; AParams: TStrings); virtual; + constructor Create(ACollection: TCollection); override; + destructor Destroy; override; +// function GetNamePath: string; override; + function NameIs(const ACommand: string): Boolean; + // + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read FDataObject write FDataObject; + property DataValue: PtrInt read FDataValue write FDataValue; + {$ELSE} + property Data: TObject read FData write FData; + {$ENDIF} + published + property CmdDelimiter: Char read FCmdDelimiter write FCmdDelimiter; + property Command: string read FCommand write FCommand; + property Description: TStrings read FDescription write SetDescription; + property Disconnect: boolean read FDisconnect write FDisconnect; + property Enabled: boolean read FEnabled write FEnabled default IdEnabledDefault; + property ExceptionReply: TIdReply read FExceptionReply write SetExceptionReply; + property Name: string read FName write FName; + property NormalReply: TIdReply read FNormalReply write SetNormalReply; + property ParamDelimiter: Char read FParamDelimiter write FParamDelimiter; + property ParseParams: Boolean read FParseParams write FParseParams; + property Response: TStrings read FResponse write SetResponse; + property Tag: Integer read FTag write FTag; + // + property HelpSuperScript : String read FHelpSuperScript write FHelpSuperScript; //may be something like * or + which should appear in help + property HelpVisible : Boolean read FHelpVisible write FHelpVisible default IdHelpVisibleDef; + + property OnCommand: TIdCommandEvent read FOnCommand write FOnCommand; + end; + + TIdCommandHandlerClass = class of TIdCommandHandler; + + { TIdCommandHandlers } + TIdCommandHandlers = class(TOwnedCollection) + protected + FBase: TIdComponent; + FExceptionReply: TIdReply; + FOnAfterCommandHandler: TIdAfterCommandHandlerEvent; + FOnBeforeCommandHandler: TIdBeforeCommandHandlerEvent; + FOnCommandHandlersException: TIdCommandHandlersExceptionEvent; + FParseParamsDef: Boolean; + FPerformReplies: Boolean; + FReplyClass: TIdReplyClass; + FReplyTexts: TIdReplies; + // + procedure DoAfterCommandHandler(AContext: TIdContext); + procedure DoBeforeCommandHandler(AContext: TIdContext; var VLine: string); + procedure DoOnCommandHandlersException(const ACommand: String; AContext: TIdContext); + function GetItem(AIndex: Integer): TIdCommandHandler; + // This is used instead of the OwnedBy property directly calling GetOwner because + // D5 dies with internal errors and crashes +// function GetOwnedBy: TIdPersistent; + procedure SetItem(AIndex: Integer; const AValue: TIdCommandHandler); + public + function Add: TIdCommandHandler; + constructor Create( + ABase: TIdComponent; + AReplyClass: TIdReplyClass; + AReplyTexts: TIdReplies; + AExceptionReply: TIdReply = nil; + ACommandHandlerClass: TIdCommandHandlerClass = nil + ); reintroduce; + function HandleCommand(AContext: TIdContext; var VCommand: string): Boolean; virtual; + // + property Base: TIdComponent read FBase; + property Items[AIndex: Integer]: TIdCommandHandler read GetItem write SetItem; + // OwnedBy is used so as not to conflict with Owner in D6 + //property OwnedBy: TIdPersistent read GetOwnedBy; + property ParseParamsDefault: Boolean read FParseParamsDef write FParseParamsDef; + property PerformReplies: Boolean read FPerformReplies write FPerformReplies; + property ReplyClass: TIdReplyClass read FReplyClass; + property ReplyTexts: TIdReplies read FReplyTexts; + // + property OnAfterCommandHandler: TIdAfterCommandHandlerEvent read FOnAfterCommandHandler + write FOnAfterCommandHandler; + // Occurs in the context of the peer thread + property OnBeforeCommandHandler: TIdBeforeCommandHandlerEvent read FOnBeforeCommandHandler + write FOnBeforeCommandHandler; + property OnCommandHandlersException: TIdCommandHandlersExceptionEvent read FOnCommandHandlersException + write FOnCommandHandlersException; + end; + + { TIdCommand } + TIdCommand = class(TObject) + protected + FCommandHandler: TIdCommandHandler; + FDisconnect: Boolean; + FParams: TStrings; + FPerformReply: Boolean; + FRawLine: string; + FReply: TIdReply; + FResponse: TStrings; + FContext: TIdContext; + FUnparsedParams: string; + FSendEmptyResponse: Boolean; + // + procedure DoCommand; virtual; + procedure SetReply(AValue: TIdReply); + procedure SetResponse(AValue: TStrings); + public + constructor Create(AOwner: TIdCommandHandler); virtual; + destructor Destroy; override; + procedure SendReply; + // + property CommandHandler: TIdCommandHandler read FCommandHandler; + property Disconnect: Boolean read FDisconnect write FDisconnect; + property PerformReply: Boolean read FPerformReply write FPerformReply; + property Params: TStrings read FParams; + property RawLine: string read FRawLine; + property Reply: TIdReply read FReply write SetReply; + property Response: TStrings read FResponse write SetResponse; + property Context: TIdContext read FContext; + property UnparsedParams: string read FUnparsedParams; + property SendEmptyResponse: Boolean read FSendEmptyResponse write FSendEmptyResponse; + end;//TIdCommand + +implementation + +uses + SysUtils; + +{ TIdCommandHandlers } + +constructor TIdCommandHandlers.Create( + ABase: TIdComponent; + AReplyClass: TIdReplyClass; + AReplyTexts: TIdReplies; + AExceptionReply: TIdReply = nil; + ACommandHandlerClass: TIdCommandHandlerClass = nil + ); +begin + if ACommandHandlerClass = nil then begin + ACommandHandlerClass := TIdCommandHandler; + end; + inherited Create(ABase, ACommandHandlerClass); + FBase := ABase; + FExceptionReply := AExceptionReply; + FParseParamsDef := IdParseParamsDefault; + FPerformReplies := True; // RLebeau: default to legacy behavior + FReplyClass := AReplyClass; + FReplyTexts := AReplyTexts; +end; + +function TIdCommandHandlers.Add: TIdCommandHandler; +begin + Result := TIdCommandHandler(inherited Add); +end; + +function TIdCommandHandlers.HandleCommand(AContext: TIdContext; + var VCommand: string): Boolean; +var + i, j: Integer; +begin + j := Count - 1; + Result := False; + DoBeforeCommandHandler(AContext, VCommand); try + i := 0; + while i <= j do begin + if Items[i].Enabled then begin + Result := Items[i].Check(VCommand, AContext); + if Result then begin + Break; + end; + end; + Inc(i); + end; + finally DoAfterCommandHandler(AContext); end; +end; + +procedure TIdCommandHandlers.DoAfterCommandHandler(AContext: TIdContext); +begin + if Assigned(OnAfterCommandHandler) then begin + OnAfterCommandHandler(Self, AContext); + end; +end; + +procedure TIdCommandHandlers.DoBeforeCommandHandler(AContext: TIdContext; + var VLine: string); +begin + if Assigned(OnBeforeCommandHandler) then begin + OnBeforeCommandHandler(Self, VLine, AContext); + end; +end; + +procedure TIdCommandHandlers.DoOnCommandHandlersException(const ACommand: String; + AContext: TIdContext); +begin + if Assigned(FOnCommandHandlersException) then begin + OnCommandHandlersException(ACommand, AContext); + end; +end; + +function TIdCommandHandlers.GetItem(AIndex: Integer): TIdCommandHandler; +begin + Result := TIdCommandHandler(inherited Items[AIndex]); +end; + +{ +function TIdCommandHandlers.GetOwnedBy: TIdPersistent; +begin + Result := GetOwner; +end; + } + +procedure TIdCommandHandlers.SetItem(AIndex: Integer; const AValue: TIdCommandHandler); +begin + inherited SetItem(AIndex, AValue); +end; + +{ TIdCommandHandler } + +procedure TIdCommandHandler.DoCommand(const AData: string; AContext: TIdContext; AUnparsedParams: string); +var + LCommand: TIdCommand; +begin + LCommand := TIdCommand.Create(Self); + try + LCommand.FRawLine := AData; + LCommand.FContext := AContext; + LCommand.FUnparsedParams := AUnparsedParams; + + if ParseParams then begin + DoParseParams(AUnparsedParams, LCommand.Params); + end; + + // RLebeau 2/21/08: for the IRC protocol, RFC 2812 section 2.4 says that + // clients are not allowed to issue numeric replies for server-issued + // commands. Added the PerformReplies property so TIdIRC can specify + // that behavior. + if Collection is TIdCommandHandlers then begin + LCommand.PerformReply := TIdCommandHandlers(Collection).PerformReplies; + end; + + try + if (LCommand.Reply.Code = '') and (NormalReply.Code <> '') then begin + LCommand.Reply.Assign(NormalReply); + end; + + //if code<>'' before DoCommand, then it breaks exception handling + Assert(LCommand.Reply.Code <> ''); + LCommand.DoCommand; + + if LCommand.Reply.Code = '' then begin + LCommand.Reply.Assign(NormalReply); + end; + // UpdateText here in case user wants to add to it. SendReply also gets it in case + // a different reply is sent (ie exception, etc), or the user changes the code in the event + LCommand.Reply.UpdateText; + except + on E: Exception do begin + // If there is an unhandled exception, we override all replies + // If nothing specified to override with, we throw the exception again. + // If the user wants a custom value on exception other, its their responsibility + // to catch it before it reaches us + LCommand.Reply.Clear; + if LCommand.PerformReply then begin + // Try from command handler first + if ExceptionReply.Code <> '' then begin + LCommand.Reply.Assign(ExceptionReply); + // If still no go, from server + // Can be nil though. Typically only servers pass it in + end else if (Collection is TIdCommandHandlers) and (TIdCommandHandlers(Collection).FExceptionReply <> nil) then begin + LCommand.Reply.Assign(TIdCommandHandlers(Collection).FExceptionReply); + end; + if LCommand.Reply.Code <> '' then begin + //done this way in case an exception message has more than one line. + //otherwise you could get something like this: + // + // 550 System Error. Code: 2 + // The system cannot find the file specified + // + //and the second line would throw off some clients. + LCommand.Reply.Text.Text := E.Message; + //Reply.Text.Add(E.Message); + LCommand.SendReply; + end else begin + raise; + end; + end else begin + raise; + end; + end else begin + raise; + end; + end; + + if LCommand.PerformReply then begin + LCommand.SendReply; + end; + + if (LCommand.Response.Count > 0) or LCommand.SendEmptyResponse then begin + AContext.Connection.WriteRFCStrings(LCommand.Response); + end else if Response.Count > 0 then begin + AContext.Connection.WriteRFCStrings(Response); + end; + finally + try + if LCommand.Disconnect then begin + AContext.Connection.Disconnect; + end; + finally + LCommand.Free; + end; + end; +end; + +procedure TIdCommandHandler.DoParseParams(AUnparsedParams: string; AParams: TStrings); +// AUnparsedParams is not preparsed and is completely left up to the command handler. This will +// allow for future expansion such as multiple delimiters etc, and allow the logic to properly +// remain in each of the command handler implementations. In the future there may be a base type +// and multiple descendants +begin + AParams.Clear; + SplitDelimitedString(AUnparsedParams, AParams, FParamDelimiter <> #32, FParamDelimiter); +end; + +function TIdCommandHandler.Check(const AData: string; AContext: TIdContext): boolean; +// AData is not preparsed and is completely left up to the command handler. This will allow for +// future expansion such as wild cards etc, and allow the logic to properly remain in each of the +// command handler implementations. In the future there may be a base type and multiple descendants +var + LUnparsedParams: string; +begin + LUnparsedParams := ''; + Result := TextIsSame(AData, Command); // Command by itself + + if not Result then begin + if CmdDelimiter <> #0 then begin + Result := TextStartsWith(AData, Command + CmdDelimiter); + if Result then begin + LUnparsedParams := Copy(AData, Length(Command) + 2, MaxInt); + end; + end else begin + // Dont strip any part of the params out.. - just remove the command purely on length and + // no delim + Result := TextStartsWith(AData, Command); + if Result then begin + LUnparsedParams := Copy(AData, Length(Command) + 1, MaxInt); + end; + end; + end; + + if Result then begin + DoCommand(AData, AContext, LUnparsedParams); + end; +end; + +constructor TIdCommandHandler.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + + FReplyClass := TIdCommandHandlers(ACollection).ReplyClass; + if FReplyClass = nil then begin + FReplyClass := TIdReplyRFC; + end; + + FCmdDelimiter := #32; + FEnabled := IdEnabledDefault; + FName := ClassName + IntToStr(ID); + FParamDelimiter := #32; + FParseParams := TIdCommandHandlers(ACollection).ParseParamsDefault; + FResponse := TStringList.Create; + FDescription := TStringList.Create; + + FNormalReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts); + if FNormalReply is TIdReplyRFC then begin + FNormalReply.Code := '200'; {do not localize} + end; + FHelpVisible := IdHelpVisibleDef; + // Dont initialize, pulls from CmdTCPServer for defaults + FExceptionReply := FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(ACollection).ReplyTexts); +end; + +destructor TIdCommandHandler.Destroy; +begin + FreeAndNil(FResponse); + FreeAndNil(FNormalReply); + FreeAndNil(FDescription); + FreeAndNil(FExceptionReply); + inherited Destroy; +end; + +function TIdCommandHandler.GetDisplayName: string; +begin + if Command = '' then begin + Result := Name; + end else begin + Result := Command; + end; +end; + +{ +function TIdCommandHandler.GetNamePath: string; +begin + if Collection <> nil then begin + // OwnedBy is used because D4/D5 dont expose Owner on TOwnedCollection but D6 does + Result := TIdCommandHandlers(Collection).OwnedBy.GetNamePath + '.' + Name; + end else begin + Result := inherited GetNamePath; + end; +end; +} + +function TIdCommandHandler.NameIs(const ACommand: string): Boolean; +begin + Result := TextIsSame(ACommand, FName); +end; + +procedure TIdCommandHandler.SetExceptionReply(AValue: TIdReply); +begin + FExceptionReply.Assign(AValue); +end; + +procedure TIdCommandHandler.SetNormalReply(AValue: TIdReply); +begin + FNormalReply.Assign(AValue); +end; + +procedure TIdCommandHandler.SetResponse(AValue: TStrings); +begin + FResponse.Assign(AValue); +end; + +procedure TIdCommandHandler.SetDescription(AValue: TStrings); +begin + FDescription.Assign(AValue); +end; + +{ TIdCommand } + +constructor TIdCommand.Create(AOwner: TIdCommandHandler); +begin + inherited Create; + FParams := TStringList.Create; + FReply := AOwner.FReplyClass.CreateWithReplyTexts(nil, TIdCommandHandlers(AOwner.Collection).ReplyTexts); + FResponse := TStringList.Create; + FCommandHandler := AOwner; + FDisconnect := AOwner.Disconnect; +end; + +destructor TIdCommand.Destroy; +begin + FreeAndNil(FReply); + FreeAndNil(FResponse); + FreeAndNil(FParams); + inherited Destroy; +end; + +procedure TIdCommand.DoCommand; +begin + if Assigned(CommandHandler.OnCommand) then begin + CommandHandler.OnCommand(Self); + end; +end; + +procedure TIdCommand.SendReply; +begin + PerformReply := False; + Reply.UpdateText; + Context.Connection.IOHandler.Write(Reply.FormattedReply); +end; + +procedure TIdCommand.SetReply(AValue: TIdReply); +begin + FReply.Assign(AValue); +end; + +procedure TIdCommand.SetResponse(AValue: TStrings); +begin + FResponse.Assign(AValue); +end; + +end. diff --git a/indy/Core/IdCompilerDefines.inc b/indy/Core/IdCompilerDefines.inc new file mode 100644 index 0000000..fc64532 --- /dev/null +++ b/indy/Core/IdCompilerDefines.inc @@ -0,0 +1,1687 @@ +{$IFDEF CONDITIONALEXPRESSIONS} + // Must be at the top... + {$IF CompilerVersion >= 24.0} + {$LEGACYIFEND ON} + {$IFEND} +{$ENDIF} + +// General + +// Make this $DEFINE to use the 16 color icons required by Borland +// or DEFINE to use the 256 color Indy versions +{.$DEFINE Borland} + +// S.G. 4/9/2002: IPv4/IPv6 general switch (for defaults only) +{$DEFINE IdIPv4} + +{$DEFINE INDY100} +{$DEFINE 10_6_2} //so developers can IFDEF for this specific version + +// When invoking DCC on the command-line, use the -DBCB +// parameter when generating C++Builder output files! +{$IFDEF BCB} + {$DEFINE CBUILDER} +{$ELSE} + {$DEFINE DELPHI} +{$ENDIF} + +{$UNDEF USE_OPENSSL} +{$UNDEF STATICLOAD_OPENSSL} + +{$UNDEF USE_ZLIB_UNIT} +{$UNDEF USE_SSPI} + +// $DEFINE the following if the global objects in the IdStack and IdThread +// units should be freed on finalization +{.$DEFINE FREE_ON_FINAL} +{$UNDEF FREE_ON_FINAL} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. This works in conjunction with the +// FREE_ON_FINAL define above. +{$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + +// FastMM is natively available in BDS 2006 and higher. $DEFINE the +// following if FastMM has been installed manually in earlier versions +{.$DEFINE USE_FASTMM4} +{$UNDEF USE_FASTMM4} + +// $DEFINE the following if MadExcept has been installed manually in +// BDS 2005 or earlier (System.RegisterExpectedMemoryLeak() was introduced +// in BDS 2006) +{.$DEFINE USE_MADEXCEPT} +{$UNDEF USE_MADEXCEPT} + +// Make sure the following are $DEFINE'd only for Delphi/C++Builder 2009 onwards +// as specified further below. The VCL is fully Unicode, where the 'String' +// type maps to System.UnicodeString, not System.AnsiString anymore +{$UNDEF STRING_IS_UNICODE} +{$UNDEF STRING_IS_ANSI} +{$UNDEF STRING_UNICODE_MISMATCH} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. Delphi/C++Builder Mobile/NextGen compilers +// do not support Ansi data types anymore, and is moving away from raw +// pointers as well. +{$DEFINE HAS_AnsiString} +{$DEFINE HAS_AnsiChar} +{$DEFINE HAS_PAnsiChar} +{$UNDEF HAS_PPAnsiChar} +{$UNDEF NO_ANSI_TYPES} +{$UNDEF USE_MARSHALLED_PTRS} +{$UNDEF HAS_MarshaledAString} +{$UNDEF USE_OBJECT_ARC} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF STRING_IS_IMMUTABLE} +{$UNDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF HAS_TEncoding} +{$UNDEF HAS_TEncoding_GetEncoding_ByEncodingName} +{$UNDEF HAS_Exception_RaiseOuterException} +{$UNDEF HAS_System_ReturnAddress} +{$UNDEF HAS_TCharacter} +{$UNDEF HAS_TInterlocked} +{$UNDEF HAS_TNetEncoding} + +// Make sure that this is defined only for environments where we are using +// the iconv library to charactor conversions. +{.$UNDEF USE_ICONV} + +//Define for Delphi cross-compiler targetting Posix +{$UNDEF USE_VCL_POSIX} +{$UNDEF HAS_ComponentPlatformsAttribute} +{$UNDEF HAS_ComponentPlatformsAttribute_Win32} +{$UNDEF HAS_ComponentPlatformsAttribute_Win64} +{$UNDEF HAS_ComponentPlatformsAttribute_OSX32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Simulator} +{$UNDEF HAS_ComponentPlatformsAttribute_Android} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device32} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux64} +{$UNDEF HAS_ComponentPlatformsAttribute_WinNX32} +{$UNDEF HAS_ComponentPlatformsAttribute_WinIoT32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device64} +{$UNDEF HAS_DIRECTIVE_WARN_DEFAULT} + +// Define for Delphi to auto-generate platform-appropriate '#pragma link' statements in HPP files +{$UNDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + +// detect compiler versions + +// TODO: to detect features in Delphi/C++Builder v6 and later, use CompilerVersion +// and RTLVersion constants instead of VERXXX defines. We still support v5, which +// does not have such constants. + +// Delphi 4 +{$IFDEF VER120} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE DELPHI_4} +{$ENDIF} + +// C++Builder 4 +{$IFDEF VER125} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE CBUILDER_4} +{$ENDIF} + +// Delphi & C++Builder 5 +{$IFDEF VER130} + {$DEFINE DCC} + {$DEFINE VCL_50} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_5} + {$ELSE} + {$DEFINE DELPHI_5} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 6 +{$IFDEF VER140} + {$DEFINE DCC} + {$DEFINE VCL_60} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_6} + {$ELSE} + {$DEFINE DELPHI_6} + {$ENDIF} +{$ENDIF} + +//Delphi 7 +{$IFDEF VER150} + {$DEFINE DCC} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} // there was no C++ Builder 7 +{$ENDIF} + +//Delphi 8 +{$IFDEF VER160} + {$DEFINE DCC} + {$DEFINE VCL_80} + {$DEFINE DELPHI_8} // there was no C++ Builder 8 +{$ENDIF} + +//Delphi 2005 +{$IFDEF VER170} + {$DEFINE DCC} + {$DEFINE VCL_2005} + {$DEFINE DELPHI_2005} // there was no C++Builder 2005 +{$ENDIF} + +// NOTE: CodeGear decided to make Highlander be a non-breaking release +// (no interface changes, thus fully backwards compatible without any +// end user code changes), so VER180 applies to both BDS 2006 and +// Highlander prior to the release of RAD Studio 2007. Use VER185 to +// identify Highlanger specifically. + +//Delphi & C++Builder 2006 +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER180} + {$DEFINE DCC} + {$DEFINE VCL_2006} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2006} + {$ELSE} + {$DEFINE DELPHI_2006} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER185} + {$DEFINE DCC} + {$UNDEF VCL_2006} + {$DEFINE VCL_2007} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_2006} + {$DEFINE CBUILDER_2007} + {$ELSE} + {$UNDEF DELPHI_2006} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +// BDS 2007 NET personality uses VER190 instead of 185. +//Delphi .NET 2007 +{$IFDEF VER190} + {$DEFINE DCC} + {$IFDEF CIL} + //Delphi 2007 + {$DEFINE VCL_2007} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2009 (Tiburon) +{$IFDEF VER200} + {$DEFINE DCC} + {$DEFINE VCL_2009} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2009} + {$ELSE} + {$DEFINE DELPHI_2009} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2010 (Weaver) +{$IFDEF VER210} + {$DEFINE DCC} + {$DEFINE VCL_2010} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2010} + {$ELSE} + {$DEFINE DELPHI_2010} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder XE (Fulcrum) +{$IFDEF VER220} +//REMOVE DCC DEFINE after the next Fulcrum beta. +//It will be defined there. + {$IFNDEF DCC} + {$DEFINE DCC} + {$ENDIF} + {$DEFINE VCL_XE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE} + {$ELSE} + {$DEFINE DELPHI_XE} + {$ENDIF} +{$ENDIF} + +// DCC is now defined by the Delphi compiler starting in XE2 + +//Delphi & CBuilder XE2 (Pulsar) +{$IFDEF VER230} + {$DEFINE VCL_XE2} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE2} + {$ELSE} + {$DEFINE DELPHI_XE2} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE3 (Waterdragon) +//Delphi & CBuilder XE3.5 (Quintessence - early betas only) +{$IFDEF VER240} + {$DEFINE VCL_XE3} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE3} + {$ELSE} + {$DEFINE DELPHI_XE3} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE4 (Quintessence) +{$IFDEF VER250} + {$UNDEF VCL_XE3} + {$DEFINE VCL_XE4} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_XE3} + {$DEFINE CBUILDER_XE4} + {$ELSE} + {$UNDEF DELPHI_XE3} + {$DEFINE DELPHI_XE4} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE5 (Zephyr) +{$IFDEF VER260} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder AppMethod +//AppMethod is just XE5 for mobile only, VCL is removed +{$IFDEF VER265} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE6 (Proteus) +{$IFDEF VER270} + {$DEFINE VCL_XE6} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE6} + {$ELSE} + {$DEFINE DELPHI_XE6} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE7 (Carpathia) +{$IFDEF VER280} + {$DEFINE VCL_XE7} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE7} + {$ELSE} + {$DEFINE DELPHI_XE7} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE8 (Elbrus) +{$IFDEF VER290} + {$DEFINE VCL_XE8} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE8} + {$ELSE} + {$DEFINE DELPHI_XE8} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder 10.0 Seattle (Aitana) +{$IFDEF VER300} + {$DEFINE VCL_SEATTLE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_SEATTLE} + {$ELSE} + {$DEFINE DELPHI_SEATTLE} + {$ENDIF} +{$ENDIF} + +// Delphi.NET +// Covers D8+ +{$IFDEF CIL} + // Platform specific conditional. Used for platform specific code. + {$DEFINE DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE STRING_IS_IMMUTABLE} + {.$DEFINE HAS_Int8} + {.$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + {$DEFINE HAS_UInt64} +{$ENDIF} + +// Kylix +// +//Important: Don't use CompilerVersion here as IF's are evaluated before +//IFDEF's and Kylix 1 does not have CompilerVersion defined at all. +{$IFNDEF FPC} + {$IFDEF LINUX} + {$DEFINE UNIX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF (RTLVersion >= 14.0) and (RTLVersion <= 14.5) } + {$DEFINE KYLIX} + {$IF RTLVersion = 14.5} + {$DEFINE KYLIX_3} + {$ELSEIF RTLVersion >= 14.2} + {$DEFINE KYLIX_2} + {$ELSE} + {$DEFINE KYLIX_1} + {$IFEND} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF KYLIX} + {$DEFINE VCL_60} + {$DEFINE INT_THREAD_PRIORITY} + {$DEFINE CPUI386} + {$UNDEF USE_BASEUNIX} + + {$IFDEF KYLIX_3} + {$DEFINE KYLIX_3_OR_ABOVE} + {$ENDIF} + + {$IFDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_2} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_1} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFNDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIXCOMPAT} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE USE_ZLIB_UNIT} + {$ENDIF} +{$ENDIF} + +// FPC (2+) + +{$IFDEF FPC} + // TODO: In FreePascal 4.2.0+, a Delphi-like UnicodeString type is supported. + // However, String/(P)Char do not map to UnicodeString/(P)WideChar unless + // either {$MODE DelphiUnicode} or {$MODESWITCH UnicodeStrings} is used. + // We should consider enabling one of them so Indy uses the same Unicode logic + // in Delphi 2009+ and FreePascal 4.2.0+ and reduces IFDEFs (in particular, + // STRING_UNICODE_MISMATCH, see further below). However, FreePascal's RTL + // is largely not UnicodeString-enabled yet... + {$MODE Delphi} + //note that we may need further defines for widget types depending on + //what we do and what platforms we support in FPC. + //I'll let Marco think about that one. + {$IFDEF UNIX} + {$DEFINE USE_BASEUNIX} + {$IFDEF LINUX} + //In Linux for I386, you can choose between a Kylix-libc API or + //the standard RTL Unix API. Just pass -dKYLIXCOMPAT to the FPC compiler. + //I will see what I can do about the Makefile. + {$IFDEF KYLIXCOMPAT} + {$IFDEF CPUI386} + {$UNDEF USE_BASEUNIX} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF USE_BASEUNIX} + {$UNDEF KYLIXCOMPAT} + {$ENDIF} + {$ENDIF} + + // FPC_FULLVERSION was added in FPC 2.2.4 + // Have to use Defined() or else Delphi compiler chokes, since it + // evaluates $IF statements before $IFDEF statements... + + {$MACRO ON} // must be on in order to use versioning macros + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20701)} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20604)} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20602)} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20600)} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20404)} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20402)} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20400)} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20204)} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20202)} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20105)} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$IFEND} + + // just in case + {$IFDEF FPC_2_7_1} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_2} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + + {$IFDEF FPC_2_7_1_OR_ABOVE} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_4_OR_ABOVE} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_2} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_2_OR_ABOVE} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_2_OR_ABOVE} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_4_OR_ABOVE} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ELSE} + {$IFDEF VER2_2} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {.$IFDEF FPC_2_7_1_OR_ABOVE} + // support for RawByteString and UnicodeString + {.$DEFINE VCL_2009} + {.$DEFINE DELPHI_2009} + {.$ELSE} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} + {.$ENDIF} +{$ENDIF} + +// end FPC + +{$IFDEF VCL_SEATTLE} + {$DEFINE VCL_SEATTLE_OR_ABOVE} +{$ENDIF} + +{$IFDEF VCL_SEATTLE_OR_ABOVE} + {$DEFINE VCL_XE8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE8} + {$DEFINE VCL_XE8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE VCL_XE7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE7} + {$DEFINE VCL_XE7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE VCL_XE6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE6} + {$DEFINE VCL_XE6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE6_OR_ABOVE} + {$DEFINE VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE5} + {$DEFINE VCL_XE5_OR_ABOVE} + // TODO: figure out how to detect this version + {.$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE4} + {$DEFINE VCL_XE4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE VCL_XE3_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE3} + {$DEFINE VCL_XE3_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE VCL_XE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE2} + {$DEFINE VCL_XE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE VCL_XE_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE} + {$DEFINE VCL_XE_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE VCL_2010_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2010} + {$DEFINE VCL_2010_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE VCL_2009_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2009} + {$DEFINE VCL_2009_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$DEFINE VCL_2007_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2007} + {$DEFINE VCL_2007_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE VCL_2006_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2006} + {$DEFINE VCL_2006_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE VCL_2005_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2005} + {$DEFINE VCL_2005_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2005_OR_ABOVE} + {$DEFINE VCL_8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_80} + {$DEFINE VCL_8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_8_OR_ABOVE} + {$DEFINE VCL_7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_70} + {$DEFINE VCL_7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$DEFINE VCL_6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_60} + {$DEFINE VCL_6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE VCL_5_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_50} + {$DEFINE VCL_5_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$DEFINE VCL_4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_40} + {$DEFINE VCL_4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +// Normalize Delphi compiler defines to match FPC for consistency: +// +// CPU32 - any 32-bit CPU +// CPU64 - any 64-bit CPU +// WINDOWS - any Windows platform (32-bit, 64-bit, CE) +// WIN32 - Windows 32-bit +// WIN64 - Windows 64-bit +// WINCE - Windows CE +// +// Consult the "Free Pascal Programmer's Guide", Appendix G for the complete +// list of defines that are used. Do not work on this unless you understand +// what the FreePascal developers are doing. Not only do you have to +// descriminate with operating systems, but also with chip architectures +// are well. +// +// DCC Pulsar+ define the following values: +// ASSEMBLER +// DCC +// CONDITIONALEXPRESSIONS +// NATIVECODE +// UNICODE +// MACOS +// MACOS32 +// MACOS64 +// MSWINDOWS +// WIN32 +// WIN64 +// LINUX +// POSIX +// POSIX32 +// CPU386 +// CPUX86 +// CPUX64 +// +// Kylix defines the following values: +// LINUX +// (others??) +// + +{$IFNDEF FPC} + // TODO: We need to use ENDIAN_BIG for big endian chip architectures, + // such as 680x0, PowerPC, Sparc, and MIPS, once DCC supports them, + // provided it does not already define its own ENDIAN values by then... + {$DEFINE ENDIAN_LITTLE} + {$IFNDEF VCL_6_OR_ABOVE} + {$DEFINE MSWINDOWS} + {$ENDIF} + {$IFDEF MSWINDOWS} + {$DEFINE WINDOWS} + {$ENDIF} + // TODO: map Pulsar's non-Windows platform defines... + {$IFDEF VCL_XE2_OR_ABOVE} + {$IFDEF CPU386} + //any 32-bit CPU + {$DEFINE CPU32} + //Intel 386 compatible chip architecture + {$DEFINE CPUI386} + {$ENDIF} + {$IFDEF CPUX86} + {$DEFINE CPU32} + {$ENDIF} + {$IFDEF CPUX64} + //any 64-bit CPU + {$DEFINE CPU64} + //AMD64 compatible chip architecture + {$DEFINE CPUX86_64} //historical name for AMD64 + {$DEFINE CPUAMD64} + {$ENDIF} + {$ELSE} + {$IFNDEF DOTNET} + {$IFNDEF KYLIX} + {$DEFINE I386} + {$ENDIF} + {$ENDIF} + {$DEFINE CPU32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + //differences in DotNET Framework versions. + {$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE DOTNET_2} + {$DEFINE DOTNET_2_OR_ABOVE} + {$ELSE} + {$DEFINE DOTNET_1_1} + {$ENDIF} + {$DEFINE DOTNET_1_1_OR_ABOVE} + // Extra include used in D7 for testing. Remove later when all comps are + // ported. Used to selectively exclude non ported parts. Allowed in places + // IFDEFs are otherwise not permitted. + {$DEFINE DOTNET_EXCLUDE} +{$ENDIF} + +// Check for available features + +{$IFDEF CBUILDER} + // When generating a C++ HPP file, if a class has no explicit constructor + // defined and contains compiler-managed members (xxxString, TDateTime, + // Variant, DelphiInterface, etc), the HPP will contain a forwarding + // inline constructor that implicitally initializes those managed members, + // which will overwrite any non-default initializations performed inside + // of InitComponent() overrides! In this situation, the workaround is to + // define an explicit constructor that forwards to the base class constructor + // manually. + {$DEFINE WORKAROUND_INLINE_CONSTRUCTORS} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$IFNDEF FPC} + {$IFNDEF KYLIX} + {$DEFINE HAS_RemoveFreeNotification} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_GetObjectProp} + {$DEFINE HAS_TObjectList} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE HAS_PCardinal} + {$DEFINE HAS_PByte} + {$DEFINE HAS_PWord} + {$DEFINE HAS_PPointer} + {$DEFINE HAS_TList_Assign} + {$DEFINE HAS_sLineBreak} + {$DEFINE HAS_RaiseLastOSError} + {$DEFINE HAS_SysUtils_IncludeExcludeTrailingPathDelimiter} + {$DEFINE HAS_SysUtils_DirectoryExists} + {$DEFINE HAS_UNIT_DateUtils} + {$DEFINE HAS_UNIT_StrUtils} + {$DEFINE HAS_UNIT_Types} + {$DEFINE HAS_TryStrToInt} + {$DEFINE HAS_TryStrToInt64} + {$DEFINE HAS_TryEncodeDate} + {$DEFINE HAS_TryEncodeTime} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFNDEF FPC} + {$DEFINE HAS_IInterface} + {$DEFINE HAS_TSelectionEditor} + {$DEFINE HAS_TStringList_CaseSensitive} + {$IFNDEF KYLIX} + {$DEFINE HAS_DEPRECATED} + {$DEFINE HAS_SYMBOL_PLATFORM} + {$DEFINE HAS_UNIT_PLATFORM} + {$IFNDEF VCL_8_OR_ABOVE} + // Delphi 6 and 7 have an annoying bug that if a class method is declared as + // deprecated, the compiler will emit a "symbol is deprecated" warning + // on the method's implementation! So we will have to wrap implementations + // of deprecated methods with {$WARN SYMBOL_DEPRECATED OFF} directives + // to disable that warning. + {$DEFINE DEPRECATED_IMPL_BUG} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFNDEF DOTNET} + //Widget defines are omitted in .NET + {$DEFINE VCL_60_PLUS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$IFNDEF FPC} + {$DEFINE HAS_UInt64} + {$DEFINE HAS_NAMED_THREADS} + {$DEFINE HAS_TStrings_ValueFromIndex} + {$ENDIF} + {$DEFINE HAS_TFormatSettings} + {$DEFINE HAS_PosEx} + {$IFNDEF VCL_70} + // not implemented in D7 + {$DEFINE HAS_STATIC_TThread_Queue} + {$ENDIF} + {$IFNDEF CIL} + {$IFNDEF VCL_80} + // not implemented in D8 or .NET + {$DEFINE HAS_STATIC_TThread_Synchronize} + {$ENDIF} + {$ENDIF} +{$ELSE} + {$IFDEF CBUILDER_6} + {$DEFINE HAS_NAMED_THREADS} + {$ENDIF} +{$ENDIF} + +{$IFNDEF VCL_2005_OR_ABOVE} + {$IFDEF DCC} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE USE_INLINE} + {$DEFINE HAS_2PARAM_FileAge} + {$DEFINE HAS_System_RegisterExpectedMemoryLeak} + {$IFNDEF FREE_ON_FINAL} + {$IFNDEF DOTNET} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} + // UInt64 is emitted as signed __int64 instead of unsigned __int64 in HPP files + {$IFDEF CBUILDER} + {$DEFINE BROKEN_UINT64_HPPEMIT} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$IFNDEF CBUILDER_2007} + // class properties are broken in C++Builder 2007, causing AVs at compile-time + {$DEFINE HAS_CLASSPROPERTIES} + {$ENDIF} + // Native(U)Int exist but are buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_StrToInt64Def} + {$DEFINE HAS_DWORD_PTR} + {$DEFINE HAS_ULONG_PTR} + {$DEFINE HAS_ULONGLONG} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_CurrentYear} + {$IFNDEF DOTNET} + {$DEFINE HAS_TIMEUNITS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$IFNDEF DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE HAS_UnicodeString} + {$DEFINE HAS_TEncoding} + {$DEFINE HAS_TCharacter} + {$DEFINE HAS_InterlockedCompareExchangePointer} + {$DEFINE HAS_WIDE_TCharArray} + {$DEFINE HAS_UNIT_AnsiStrings} + {$DEFINE HAS_PUInt64} + {$IFDEF VCL_2009} + // TODO: need to differentiate between RTM and Update 1 + // FmtStr() is broken in RTM but was fixed in Update 1 + {$DEFINE BROKEN_FmtStr} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_CLASSVARS} + {$DEFINE HAS_DEPRECATED_MSG} + {$DEFINE HAS_TBytes} + // Native(U)Int are still buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_Int8} + {$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + // UInt64 is now emitted as unsigned __int64 in HPP files + {$IFDEF CBUILDER} + {$UNDEF BROKEN_UINT64_HPPEMIT} + {$ENDIF} + {$IFDEF DCC} + {$IFDEF WINDOWS} + // Exception.RaiseOuterException() is only available on Windows at this time + {$DEFINE HAS_Exception_RaiseOuterException} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE HAS_CLASSCONSTRUCTOR} + {$DEFINE HAS_CLASSDESTRUCTOR} + {$DEFINE HAS_DELAYLOAD} + {$DEFINE HAS_TThread_NameThreadForDebugging} + {$DEFINE DEPRECATED_TThread_SuspendResume} + // Native(U)Int are finally ok to use now + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$DEFINE HAS_USHORT} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE HAS_TFormatSettings_Object} + {$DEFINE HAS_LocaleCharsFromUnicode} + {$DEFINE HAS_UnicodeFromLocaleChars} + {$DEFINE HAS_PVOID} + {$DEFINE HAS_ULONG64} + {$DEFINE HAS_TEncoding_GetEncoding_ByEncodingName} + {$IFDEF DCC} + // Exception.RaiseOuterException() is now available on all platforms + {$DEFINE HAS_Exception_RaiseOuterException} + {$ENDIF} + {$IFNDEF DOTNET} + {$DEFINE HAS_TInterlocked} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$DEFINE HAS_LONG} + {$DEFINE HAS_ComponentPlatformsAttribute} + {$DEFINE HAS_ComponentPlatformsAttribute_Win32} + {$DEFINE HAS_ComponentPlatformsAttribute_Win64} + {$DEFINE HAS_ComponentPlatformsAttribute_OSX32} + {$DEFINE HAS_System_ReturnAddress} + {$DEFINE HAS_DIRECTIVE_WARN_DEFAULT} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$DEFINE HAS_SysUtils_TStringHelper} + {$IFDEF NEXTGEN} + {$DEFINE DCC_NEXTGEN} + {$DEFINE HAS_MarshaledAString} + {$DEFINE USE_MARSHALLED_PTRS} + {$IFDEF AUTOREFCOUNT} + {$DEFINE USE_OBJECT_ARC} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE HAS_AnsiStrings_StrPLCopy} + {$DEFINE HAS_AnsiStrings_StrLen} + {$DEFINE HAS_Character_TCharHelper} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Simulator} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device} + // technically, these are present in XE4, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Android} + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux32} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinNX32} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_Android} +{$ENDIF} + +{$IFDEF VCL_XE5_UPDATE2_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_HPPEMIT_LINKUNIT} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE HAS_TNetEncoding} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device32} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device64} + // technically, these are present in XE8, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux64} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinIoT32} +{$ENDIF} + +// Delphi XE+ cross-compiling +{$IFNDEF FPC} + {$IFDEF POSIX} + {$IF RTLVersion >= 22.0} + {$DEFINE UNIX} + {$UNDEF USE_BASEUNIX} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$IFDEF LINUX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF RTLVersion >= 22.0} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_CROSS_COMPILE} + {$UNDEF KYLIXCOMPAT} +{$ELSE} + {$IFDEF KYLIXCOMPAT} + {$linklib c} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC} + {$DEFINE USE_INLINE} + {$DEFINE USE_CLASSINLINE} + {$DEFINE USE_TBitBtn} //use Bit Buttons instead of Buttons + {$DEFINE FPC_REINTRODUCE_BUG} + {$DEFINE FPC_CIRCULAR_BUG} + {$DEFINE NO_REDECLARE} + {$DEFINE BYTE_COMPARE_SETS} + {$DEFINE HAS_QWord} // TODO: when was QWord introduced? + {$DEFINE HAS_PQWord} // TODO: when was PQWord introduced? + {$IFDEF FPC_2_1_5_OR_ABOVE} + {$DEFINE HAS_UInt64} + {.$DEFINE HAS_PUInt64} // TODO: is this defined? + {$ENDIF} + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE HAS_SharedPrefix} + {$ENDIF} + {$IFDEF FPC_2_2_4_OR_ABOVE} + // size_t and psize_t are only available on Unix systems (FreeBSD, Linux, etc) + {$IFDEF UNIX} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_PtrInt} + {$DEFINE HAS_PtrUInt} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_LPGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFDEF WINDOWS} + {$DEFINE HAS_ULONG_PTR} + {.$DEFINE HAS_ULONGLONG} // TODO: is this defined? + {$ENDIF} + {$DEFINE HAS_UNIT_ctypes} + {$DEFINE HAS_sLineBreak} + {$IFDEF FPC_HAS_UNICODESTRING} + {$DEFINE HAS_UnicodeString} + {$ELSE} + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE HAS_UnicodeString} + {$ENDIF} + {$ENDIF} + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE DEPRECATED_TThread_SuspendResume} + {$DEFINE HAS_DEPRECATED} // TODO: when was deprecated introduced? + {$DEFINE HAS_DEPRECATED_MSG} + {$ENDIF} + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$ENDIF} + {$IFDEF FPC_2_6_2_OR_ABOVE} + {$DEFINE HAS_Int8} + {$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + {$ENDIF} + {$IFDEF FPC_2_6_4_OR_ABOVE} + {$DEFINE HAS_PInt8} + {$DEFINE HAS_PUInt8} + {$DEFINE HAS_PInt16} + {$DEFINE HAS_PUInt16} + {$DEFINE HAS_PInt32} + {$DEFINE HAS_PUInt32} + {$ENDIF} + {$IFDEF FPC_UNICODESTRINGS} + {$DEFINE STRING_IS_UNICODE} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE WIDGET_WINFORMS} +{$ELSE} + {$DEFINE WIDGET_VCL_LIKE} // LCL included. + {$DEFINE WIDGET_VCL_LIKE_OR_KYLIX} + {$IFDEF FPC} + {$DEFINE WIDGET_LCL} + {$ELSE} + {$IFDEF KYLIX} + {$DEFINE WIDGET_KYLIX} + {$ELSE} + {$DEFINE WIDGET_VCL} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// .NET and Delphi 2009+ support UNICODE strings natively! +// +// FreePascal 2.4.0+ supports UnicodeString, but does not map its +// native String type to UnicodeString except when {$MODE DelphiUnicode} +// or {$MODESWITCH UnicodeStrings} is enabled. However, UNICODE is not +// defined in that mode yet until its RTL has been updated to support +// UnicodeString. STRING_UNICODE_MISMATCH is defined when the native +// String/Char types do not map to the same types that APIs are expecting +// based on whether UNICODE is defined or not. +// +// NOTE: Do not define UNICODE here. The compiler defines +// the symbol automatically. +{$IFDEF STRING_IS_UNICODE} + {$IFNDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ELSE} + {$DEFINE STRING_IS_ANSI} + {$IFDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ENDIF} + +{$IFDEF DCC_NEXTGEN} + {$DEFINE NO_ANSI_TYPES} + {.$DEFINE STRING_IS_IMMUTABLE} // Strings are NOT immutable in NEXTGEN yet + {$IFDEF USE_OBJECT_ARC} + // TODO: move these to an appropriate section. Not doing this yet because + // it is a major interface change to switch to Generics and we should + // maintain backwards compatibility with earlier compilers for the time + // being. Defining them only here for now because the non-Generic versions + // of these classes have become deprecated by ARC and so we need to start + // taking advantage of the Generics versions... + {$DEFINE HAS_UNIT_Generics_Collections} + {$DEFINE HAS_UNIT_Generics_Defaults} + {$DEFINE HAS_GENERICS_TDictionary} + {$DEFINE HAS_GENERICS_TList} + {$DEFINE HAS_GENERICS_TObjectList} + {$DEFINE HAS_GENERICS_TThreadList} + // TArray.Copy() was introduced in XE7 but was buggy. It was fixed in XE8: + // + // RSP-9763 TArray.Copy copies from destination to source for unmanaged types + // https://quality.embarcadero.com/browse/RSP-9763 + // + {$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_GENERICS_TArray_Copy} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF NO_ANSI_TYPES} + {$UNDEF HAS_AnsiString} + {$UNDEF HAS_AnsiChar} + {$UNDEF HAS_PAnsiChar} + {$UNDEF HAS_PPAnsiChar} + {$UNDEF HAS_UNIT_AnsiStrings} + {$UNDEF HAS_AnsiStrings_StrPLCopy} +{$ENDIF} + +{$IFDEF WIN32} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} +{$IFDEF WIN64} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} + +{$IFDEF WIN32_OR_WIN64} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} + {$IFNDEF DCC_NEXTGEN} + {$DEFINE USE_SSPI} + {$IFDEF STRING_IS_UNICODE} + {$DEFINE SSPI_UNICODE} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// High-performance counters are not reliable on multi-core systems, and have +// been known to cause problems with TIdIOHandler.ReadLn() timeouts in Windows +// XP SP3, both 32-bit and 64-bit. Refer to these discussions for more info: +// +// http://www.virtualdub.org/blog/pivot/entry.php?id=106 +// http://blogs.msdn.com/oldnewthing/archive/2008/09/08/8931563.aspx +// +// Do not enable thus unless you know it will work correctly on your systems! +{$IFDEF WINDOWS} + {.$DEFINE USE_HI_PERF_COUNTER_FOR_TICKS} +{$ENDIF} + +{$IFDEF UNIX} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} +{$ENDIF} + +{$IFDEF MACOS} + {$DEFINE HAS_getifaddrs} +{$ENDIF} + +{$IFDEF IOS} + {$DEFINE HAS_getifaddrs} + {$DEFINE USE_OPENSSL} + {$IFDEF CPUARM} + // RLebeau: For iOS devices, OpenSSL cannot be used as an external library, + // it must be statically linked into the app. For the iOS simulator, this + // is not true. Users who want to use OpenSSL in iOS device apps will need + // to add the static OpenSSL library to the project and then include the + // IdSSLOpenSSLHeaders_static unit in their uses clause. It hooks up the + // statically linked functions for the IdSSLOpenSSLHeaders unit to use... + {$DEFINE STATICLOAD_OPENSSL} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT} + {$DEFINE REQUIRES_PROPER_ALIGNMENT} +{$ENDIF} + +// +//iconv defines section. +{$DEFINE USE_ICONV_UNIT} +{$DEFINE USE_ICONV_ENC} +{$IFDEF UNIX} + {$DEFINE USE_ICONV} + {$IFDEF USE_BASEUNIX} + {$IFDEF FPC} + {$UNDEF USE_ICONV_UNIT} + {$ELSE} + {$UNDEF USE_ICONV_ENC} + {$ENDIF} + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + //important!! Iconv functions are defined in the libc.pas Kylix compatible unit. + {$UNDEF USE_ICONV_ENC} + {$UNDEF USE_ICONV_UNIT} + {$ENDIF} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE USE_ICONV} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONV_UNIT + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +{$UNDEF USE_SAFELOADLIBRARY} +{$IFDEF WINDOWS} + {$UNDEF USE_ICONV_ENC} + {$DEFINE USE_SAFELOADLIBRARY} +{$ENDIF} + +{$UNDEF USE_INVALIDATE_MOD_CACHE} +{$UNDEF USE_SAFELOADLIBRARY} +//This must come after the iconv defines because this compiler targets a Unix-like +//operating system. One key difference is that it does have a TEncoding class. +//If this comes before the ICONV defines, it creates problems. +//This also must go before the THandle size calculations. +{$IFDEF VCL_CROSS_COMPILE} + {$IFDEF POSIX} + {$DEFINE BSD} + {$DEFINE USE_SAFELOADLIBRARY} + {$DEFINE USE_INVALIDATE_MOD_CACHE} + {$ENDIF} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONVUNIT + {$UNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} + {$DEFINE INT_THREAD_PRIORITY} +{$ENDIF} + +{$IFNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +//IMPORTANT!!!! +// +//Do not remove this!!! This is to work around a conflict. In DCC, MACOS +//will mean OS X. In FreePascal, the DEFINE MACOS means MacIntosh System OS Classic. +{$IFDEF DCC} + // DCC defines MACOS for both iOS and OS X platforms, need to differentiate + {$IFDEF MACOS} + {$IFNDEF IOS} + {$DEFINE DARWIN} + {$ENDIF} + {$ENDIF} +{$ENDIF} +{$IFDEF FPC} + {$IFDEF MACOS} + {$DEFINE MACOS_CLASSIC} + {$ENDIF} +{$ENDIF} + +{ +BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit +word to an 8 bit byte and an 8 bit byte field named sa_len was added. +} +//Place this only after DARWIN has been defined for Delphi MACOS +{$IFDEF FREEBSD} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF DARWIN} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} + +// Do NOT remove these IFDEF's. They are here because InterlockedExchange +// only handles 32bit values. Some Operating Systems may have 64bit +// THandles. This is not always tied to the platform architecture. + +{$IFDEF AMIGA} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF ATARI} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BEOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BSD} + //I think BSD might handle FreeBSD, NetBSD, OpenBSD, and Darwin + {$IFDEF IOS} + {$IFDEF CPUARM32} + {$DEFINE CPU32} + {$DEFINE THANDLE_32} + {$ELSE} + {$IFDEF CPUARM64} + {$DEFINE CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} + {$ENDIF} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} +{$IFDEF EMBEDDED} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF EMX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GBA} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GO32} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF MACOS_CLASSIC} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NATIVENT} //Native NT for kernel level drivers + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NDS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF PALMOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SYMBIAN} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WII} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WATCOM} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} + +// end platform specific stuff for THandle size + +{$IFDEF THANDLE_CPUBITS} + {$IFDEF CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} +{$IFDEF USE_ICONV} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} + +{$UNDEF STREAM_SIZE_64} +{$IFDEF FPC} + {$DEFINE STREAM_SIZE_64} +{$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE STREAM_SIZE_64} + {$ENDIF} +{$ENDIF} + +{$IFNDEF FREE_ON_FINAL} + {$IFNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF USE_FASTMM4} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF USE_MADEXCEPT} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF DOTNET} + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF VCL_CROSS_COMPILE} + // RLebeau: should this be enabled for Windows, at least? + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} +{$ENDIF} + +{ +We must determine what the SocketType parameter is for the Socket function. +In DotNET, it's SocketType. In Kylix and the libc.pas Kylix-compatibility +library, it's a __socket_type. In BaseUnix, it's a C-type Integer. In Windows, +it's a LongInt. + +} +{$UNDEF SOCKETTYPE_IS_SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_CINT} +{$UNDEF SOCKETTYPE_IS___SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_LONGINT} +{$UNDEF SOCKETTYPE_IS_NUMERIC} +{$UNDEF SOCKET_LEN_IS_socklen_t} +{$IFDEF DOTNET} + {$DEFINE SOCKETTYPE_IS_SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_BASEUNIX} + {$DEFINE SOCKETTYPE_IS_CINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF KYLIXCOMPAT} + {$DEFINE SOCKETTYPE_IS___SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$DEFINE SOCKETTYPE_IS_NUMERIC} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKET_LEN_IS_socklen_t} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} + +{Take advantage of some TCP features specific to some stacks. +They work somewhat similarly but there's a key difference. +In Linux, TCP_CORK is turned on to send fixed packet sizes and +when turned-off (uncorked), any remaining data is sent. With +TCP_NOPUSH, this might not happen and remaining data is only sent +before disconnect. TCP_KEEPIDLE and TCP_KEEPINTVL so the IFDEF LINUX and IFDEF +SOLARIS instead of IFDEF UNIX is not an error, it's deliberate.} +{$UNDEF HAS_TCP_NOPUSH} +{$UNDEF HAS_TCP_CORK} +{$UNDEF HAS_TCP_KEEPIDLE} +{$UNDEF HAS_TCP_KEEPINTVL} +{$UNDEF HAS_SOCKET_NOSIGPIPE} +{$IFDEF BSD} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE HAS_TCP_CORK} +{$ENDIF} +{$IFDEF NETBSD} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$IFNDEF ANDROID} + {$DEFINE HAS_SOCKET_NOSIGPIPE} + {$ENDIF} +{$ENDIF} +{end Unix OS specific stuff} +{$IFDEF DEBUG} + {$UNDEF USE_INLINE} +{$ENDIF} + +// RLebeau 5/24/2015: In C++Builder 2006 and 2007, UInt64 is emitted as +// signed __int64 in HPP files instead of as unsigned __int64. This causes +// conflicts in overloaded routines that have (U)Int64 parameters. This +// was fixed in C++Builder 2009. For compilers that do not have a native +// UInt64 type, or for C++Builder 2006/2007, let's define a record type +// that can hold UInt64 values... +{$IFDEF HAS_UInt64} + {$IFDEF BROKEN_UINT64_HPPEMIT} + {$DEFINE TIdUInt64_IS_NOT_NATIVE} + {$ENDIF} +{$ELSE} + {$IFNDEF HAS_QWord} + {$DEFINE TIdUInt64_IS_NOT_NATIVE} + {$ENDIF} +{$ENDIF} + +// RLebeau 9/5/2013: it would take a lot of work to re-write Indy to support +// both 0-based and 1-based string indexing, so we'll just turn off 0-based +// indexing for now... +{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$ZEROBASEDSTRINGS OFF} +{$ENDIF} \ No newline at end of file diff --git a/indy/Core/IdContext.pas b/indy/Core/IdContext.pas new file mode 100644 index 0000000..7204ab8 --- /dev/null +++ b/indy/Core/IdContext.pas @@ -0,0 +1,220 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.14 6/16/2004 2:08:48 PM JPMugaas + Binding made public for the FTP Server. + + Rev 1.13 6/4/2004 1:34:24 PM DSiders + Removed unused TIdContextDoRun, TIdContextMethod types. + + Rev 1.12 2004.02.03 4:17:08 PM czhower + For unit name changes. + + Rev 1.11 21.1.2004 . 12:31:04 DBondzhev + Fix for Indy source. Workaround for dccil bug + now it can be compiled using Compile instead of build + + Rev 1.10 2003.10.21 12:18:58 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.9 2003.10.11 5:47:18 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.8 2003.09.19 11:54:28 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.7 3/22/2003 09:45:26 PM JPMugaas + Now should compile under D4. + + Rev 1.6 3/13/2003 10:18:38 AM BGooijen + Server side fibers, bug fixes + + Rev 1.5 1/31/2003 7:24:18 PM BGooijen + Added a .Binding function + + Rev 1.4 1/23/2003 8:33:20 PM BGooijen + + Rev 1.3 1/23/2003 11:06:06 AM BGooijen + + + Rev 1.2 1-17-2003 23:58:30 BGooijen + removed OnCreate/OnDestroy again, they had no use + + Rev 1.0 1-17-2003 22:28:58 BGooijen +} + +unit IdContext; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdSocketHandle, IdTCPConnection, IdTask, IdYarn, IdThreadSafe, + {$IFDEF HAS_GENERICS_TThreadList} + System.Generics.Collections, + {$ENDIF} + SysUtils; + +type + TIdContext = class; + TIdContextClass = class of TIdContext; + TIdContextRun = function(AContext: TIdContext): Boolean of object; + TIdContextEvent = procedure(AContext: TIdContext) of object; + TIdContextExceptionEvent = procedure(AContext: TIdContext; AException: Exception) of object; + + {$IFDEF HAS_GENERICS_TThreadList} + TIdContextThreadList = TIdThreadSafeObjectList; + TIdContextList = TList; + {$ELSE} + // TODO: flesh out to match TThreadList and TList for non-Generics compilers + TIdContextThreadList = TIdThreadSafeObjectList; + TIdContextList = TList; + {$ENDIF} + + TIdContext = class(TIdTask) + protected + // A list in which this context is registered, this can be nil, and should + // therefore not be used + FContextList: TIdContextThreadList; + FConnection: TIdTCPConnection; // TODO: should this be [Weak] on ARC systems? + FOwnsConnection: Boolean; + FOnRun: TIdContextRun; + FOnBeforeRun: TIdContextEvent; + FOnAfterRun: TIdContextEvent; + FOnException: TIdContextExceptionEvent; + // + procedure BeforeRun; override; + function Run: Boolean; override; + procedure AfterRun; override; + procedure HandleException(AException: Exception); override; + function GetBinding: TIdSocketHandle; + public + constructor Create( + AConnection: TIdTCPConnection; + AYarn: TIdYarn; + AList: TIdContextThreadList = nil + ); reintroduce; virtual; + destructor Destroy; override; + procedure RemoveFromList; + // + property Binding: TIdSocketHandle read GetBinding; + property Connection: TIdTCPConnection read FConnection; + // + property OnAfterRun: TIdContextEvent read FOnAfterRun write FOnAfterRun; + property OnBeforeRun: TIdContextEvent read FOnBeforeRun write FOnBeforeRun; + property OnRun: TIdContextRun read FOnRun write FOnRun; + property OnException: TIdContextExceptionEvent read FOnException write FOnException; + end; + +implementation + +{ TIdContext } + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.Types, + {$ENDIF} + IdGlobal, + IdIOHandlerSocket; + +constructor TIdContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; + AList: TIdContextThreadList = nil); +begin + inherited Create(AYarn); + FConnection := AConnection; + FOwnsConnection := True; + FContextList := AList; +end; + +destructor TIdContext.Destroy; +begin + if Assigned(FContextList) then begin + FContextList.Remove(Self); + end; + + if FOwnsConnection then begin + IdDisposeAndNil(FConnection); + end; + + inherited Destroy; +end; + +procedure TIdContext.RemoveFromList; +begin + FContextList := nil; +end; + +procedure TIdContext.BeforeRun; +begin + //Context must be added to ContextList outside of create. This avoids + //the possibility of another thread accessing a context (specifically + //a subclass) that is still creating. similar logic for remove/destroy. + if Assigned(FContextList) then begin + FContextList.Add(Self); + end; + + if Assigned(OnBeforeRun) then begin + OnBeforeRun(Self); + end; +end; + +function TIdContext.Run: Boolean; +begin + if Assigned(OnRun) then begin + Result := OnRun(Self); + end else begin + Result := True; + end; +end; + +procedure TIdContext.AfterRun; +begin + if Assigned(OnAfterRun) then begin + OnAfterRun(Self); + end; + + if FContextList <> nil then begin + FContextList.Remove(Self); + end; +end; + +procedure TIdContext.HandleException(AException: Exception); +begin + if Assigned(OnException) then begin + OnException(Self, AException); + end; +end; + +function TIdContext.GetBinding: TIdSocketHandle; +begin + Result := nil; + if Connection <> nil then begin + if Connection.Socket <> nil then begin + Result := Connection.Socket.Binding; + end; + end; +end; + +end. + diff --git a/indy/Core/IdCore90ASM90.inc b/indy/Core/IdCore90ASM90.inc new file mode 100644 index 0000000..9b07a32 --- /dev/null +++ b/indy/Core/IdCore90ASM90.inc @@ -0,0 +1,12 @@ +[assembly: AssemblyDescription('Internet Direct (Indy) 10.6.2 Core Run-Time Package for Borland Developer Studio')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew')] +[assembly: AssemblyProduct('Indy for Microsoft .NET Framework')] +[assembly: AssemblyCopyright('Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyTitle('Indy .NET Core Run-Time Package')] +[assembly: AssemblyVersion('10.6.2.*')] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] diff --git a/indy/Core/IdCoreDsnRegister.pas b/indy/Core/IdCoreDsnRegister.pas new file mode 100644 index 0000000..491901f --- /dev/null +++ b/indy/Core/IdCoreDsnRegister.pas @@ -0,0 +1,244 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 12/10/2004 15:36:40 HHariri + Fix so it works with D8 too + + Rev 1.3 9/5/2004 2:08:14 PM JPMugaas + Should work in D9 NET. + + Rev 1.2 2/3/2004 11:42:52 AM JPMugaas + Fixed for new design. + + Rev 1.1 2/1/2004 2:44:20 AM JPMugaas + Bindings editor should be fully functional including IPv6 support. + + Rev 1.0 11/13/2002 08:41:18 AM JPMugaas +} + +unit IdCoreDsnRegister; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF DOTNET} + Borland.Vcl.Design.DesignIntF, + Borland.Vcl.Design.DesignEditors + {$ELSE} + {$IFDEF FPC} + PropEdits, + ComponentEditors + {$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + DesignIntf, + DesignEditors + {$ELSE} + Dsgnintf + {$ENDIF} + {$ENDIF} + {$ENDIF} + ; + +type + {$IFDEF FPC} + TIdBaseComponentEditor = class(TDefaultComponentEditor) + {$ELSE} + TIdBaseComponentEditor = class(TDefaultEditor) + {$ENDIF} + public + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; + + {$IFDEF FPC} + TIdPropEdBinding = class(TPropertyEditor) + protected + FValue : String; + property Value : String read FValue write FValue; + {$ELSE} + TIdPropEdBinding = class(TClassProperty) + {$ENDIF} + public + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + function GetValue: string; override; + procedure SetValue(const Value: string); override; + end; + +// Procs + procedure Register; + +implementation + +uses + Classes, + {$IFDEF WIDGET_WINFORMS} + IdDsnPropEdBindingNET, + IdAboutDotNET, + {$ELSE} + IdDsnPropEdBindingVCL, + IdAboutVCL, + {$ENDIF} + IdDsnCoreResourceStrings, + IdBaseComponent, + IdComponent, + IdGlobal, + IdStack, + IdSocketHandle; + +{ + Design Note: It turns out that in DotNET, there are no services file functions and + IdPorts does not work as expected in DotNET. It is probably possible to read the + services file ourselves but that creates some portability problems as the placement + is different in every operating system. + + e.g. + + Linux and Unix-like systems - /etc + Windows 95, 98, and ME - c:\windows + Windows NT systems - c:\winnt\system32\drivers\etc + + Thus, it will undercut whatever benefit we could get with DotNET. + + About the best I could think of is to use an edit control because + we can't offer anything from the services file in DotNET. + + TODO: Maybe there might be a way to find the location in a more elegant + manner than what I described. +} + +type + {$IFDEF WIDGET_WINFORMS} + TIdPropEdBindingEntry = TIdDsnPropEdBindingNET; + {$ELSE} + TIdPropEdBindingEntry = TIdDsnPropEdBindingVCL; + {$ENDIF} + +procedure TIdPropEdBinding.Edit; +var + pSockets: TIdSocketHandles; + pEntry: TIdPropEdBindingEntry; +begin + inherited Edit; + + {$IFNDEF DOTNET} + pSockets := TIdSocketHandles( + {$IFDEF CPU64} + GetInt64Value + {$ELSE} + GetOrdValue + {$ENDIF} + ); + {$ELSE} + pSockets := GetObjValue as TIdSocketHandles; + {$ENDIF} + + pEntry := TIdPropEdBindingEntry.Create; + try + pEntry.Caption := TComponent(GetComponent(0)).Name; + pEntry.DefaultPort := pSockets.DefaultPort; + Value := GetListValues(pSockets); + pEntry.SetList(Value); + if pEntry.Execute then + begin + Value := pEntry.GetList; + FillHandleList(Value, pSockets); + end; + finally + pEntry.Free; + end; +end; + +function TIdPropEdBinding.GetAttributes: TPropertyAttributes; +begin + Result := inherited GetAttributes + [paDialog]; +end; + +function TIdPropEdBinding.GetValue: string; +var + pSockets: TIdSocketHandles; +begin + {$IFNDEF DOTNET} + pSockets := TIdSocketHandles( + {$IFDEF CPU64} + GetInt64Value + {$ELSE} + GetOrdValue + {$ENDIF} + ); + {$ELSE} + pSockets := GetObjValue as TIdSocketHandles; + {$ENDIF} + Result := GetListValues(pSockets); +end; + +procedure TIdPropEdBinding.SetValue(const Value: string); +var + pSockets: TIdSocketHandles; +begin + inherited SetValue(Value); + {$IFNDEF DOTNET} + pSockets := TIdSocketHandles( + {$IFDEF CPU64} + GetInt64Value + {$ELSE} + GetOrdValue + {$ENDIF} + ); + {$ELSE} + pSockets := GetObjValue as TIdSocketHandles; + {$ENDIF} + pSockets.BeginUpdate; + try + FillHandleList(Value, pSockets); + finally + pSockets.EndUpdate; + end; +end; + +{ TIdBaseComponentEditor } + +procedure TIdBaseComponentEditor.ExecuteVerb(Index: Integer); +begin + case Index of + 0 : TfrmAbout.ShowDlg; + end; +end; + +function TIdBaseComponentEditor.GetVerb(Index: Integer): string; +begin + case Index of + 0: Result := IndyFormat(RSAAboutMenuItemName, [gsIdVersion]); + end; +end; + +function TIdBaseComponentEditor.GetVerbCount: Integer; +begin + Result := 1; +end; + +procedure Register; +begin + RegisterPropertyEditor(TypeInfo(TIdSocketHandles), nil, '', TIdPropEdBinding); {Do not Localize} + RegisterComponentEditor(TIdBaseComponent, TIdBaseComponentEditor); +end; + +end. diff --git a/indy/Core/IdCoreRegister.dcr b/indy/Core/IdCoreRegister.dcr new file mode 100644 index 0000000..bcee1ad Binary files /dev/null and b/indy/Core/IdCoreRegister.dcr differ diff --git a/indy/Core/IdCoreRegisterCool.dcr b/indy/Core/IdCoreRegisterCool.dcr new file mode 100644 index 0000000..df6d318 Binary files /dev/null and b/indy/Core/IdCoreRegisterCool.dcr differ diff --git a/indy/Core/IdCreditsBitmap.res b/indy/Core/IdCreditsBitmap.res new file mode 100644 index 0000000..40a5a3a Binary files /dev/null and b/indy/Core/IdCreditsBitmap.res differ diff --git a/indy/Core/IdCreditsBitmap.resources b/indy/Core/IdCreditsBitmap.resources new file mode 100644 index 0000000..980384d Binary files /dev/null and b/indy/Core/IdCreditsBitmap.resources differ diff --git a/indy/Core/IdCustomTCPServer.pas b/indy/Core/IdCustomTCPServer.pas new file mode 100644 index 0000000..5d25013 --- /dev/null +++ b/indy/Core/IdCustomTCPServer.pas @@ -0,0 +1,1138 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 1/15/05 2:23:00 PM RLebeau + Comment added to SetScheduler() + + Rev 1.0 12/2/2004 3:26:32 PM JPMugaas + Moved most of TIdTCPServer here so we can use TIdTCPServer as an end point + which requires an OnExecute event. + + Rev 1.68 11/29/04 11:50:26 PM RLebeau + Updated ContextDisconected() to call DoDisconnect() + + Rev 1.67 11/27/04 3:28:36 AM RLebeau + Updated to automatically set up the client IOHandler before calling + DoConnect(), and to tear the IOHandler down before calling OnDisconnect(). + + Rev 1.66 10/8/2004 10:11:02 PM BGooijen + uncommented intercept code + + Rev 1.65 2004.08.13 10:55:38 czhower + Removed IFDEF + + Rev 1.64 08.08.2004 10:43:10 OMonien + temporary Thread.priority fix for Kylix + + Rev 1.63 6/11/2004 12:41:52 PM JPMugaas + Reuse Address now reenabled. + + Rev 1.62 6/1/2004 1:22:28 PM DSiders + Added TODO for TerminateWaitTimeout. + + Rev 1.61 28/04/2004 15:54:40 HHariri + Changed thread priority for scheduler + + Rev 1.60 2004.04.22 11:44:48 PM czhower + Boosted thread priority of listener thread. + + Rev 1.59 2004.03.06 10:40:34 PM czhower + Changed IOHandler management to fix bug in server shutdowns. + + Rev 1.58 2004.03.01 5:12:40 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.57 2004.02.03 4:16:56 PM czhower + For unit name changes. + + Rev 1.56 2004.01.20 10:03:36 PM czhower + InitComponent + + Rev 1.55 1/3/2004 11:49:30 PM BGooijen + the server creates a default binding for IPv6 now too, if IPv6 is supported + + Rev 1.54 2003.12.28 8:04:54 PM czhower + Shutdown fix for .net. + + Rev 1.53 2003.11.29 6:03:46 PM czhower + Active = True now works when set at design time. + + Rev 1.52 2003.10.21 12:19:02 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.51 2003.10.18 9:33:30 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.50 2003.10.18 8:04:28 PM czhower + Fixed bug with setting active at design time. + + Rev 1.49 10/15/2003 11:10:00 PM DSiders + Added localization comments. + Added resource srting for exception raised in TIdTCPServer.SetScheduler. + + Rev 1.48 2003.10.15 4:34:38 PM czhower + Bug fix for shutdown. + + Rev 1.47 2003.10.14 11:18:12 PM czhower + Fix for AV on shutdown and other bugs + + Rev 1.46 2003.10.11 5:51:38 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.45 10/5/2003 9:55:26 PM BGooijen + TIdTCPServer works on D7 and DotNet now + + Rev 1.44 10/5/2003 03:07:48 AM JPMugaas + Should compile. + + Rev 1.43 2003.10.01 9:11:28 PM czhower + .Net + + Rev 1.42 2003.09.30 1:23:08 PM czhower + Stack split for DotNet + + Rev 1.41 2003.09.19 10:11:22 PM czhower + Next stage of fiber support in servers. + + Rev 1.40 2003.09.19 11:54:34 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.39 2003.09.18 4:43:18 PM czhower + -Removed IdBaseThread + -Threads now have default names + + Rev 1.37 7/6/2003 8:04:10 PM BGooijen + Renamed IdScheduler* to IdSchedulerOf* + + Rev 1.36 2003.06.30 9:41:06 PM czhower + Fix for AV during server shut down. + + Rev 1.35 6/25/2003 3:57:58 PM BGooijen + Disconnecting the context is now inside try...except + + Rev 1.34 6/8/2003 2:13:02 PM BGooijen + Made ContextClass public + + Rev 1.33 6/5/2003 12:43:26 PM BGooijen + changed short circuit fix code + + Rev 1.32 2003.06.04 10:14:08 AM czhower + Removed short circuit dependency and fixed some older irrelevant code. + + Rev 1.31 6/3/2003 11:49:38 PM BGooijen + removed AV in TIdTCPServer.DoExecute (hopefully) + + Rev 1.30 5/26/2003 04:29:58 PM JPMugaas + Removed GenerateReply and ParseReply. Those are now obsolete duplicate + functions in the new design. + + Rev 1.29 2003.05.26 10:35:26 PM czhower + Fixed spelling typo. + + Rev 1.28 5/26/2003 12:20:00 PM JPMugaas + + Rev 1.27 2003.05.26 11:38:22 AM czhower + + Rev 1.26 5/25/2003 03:38:04 AM JPMugaas + + Rev 1.25 5/25/2003 03:26:38 AM JPMugaas + + Rev 1.24 5/20/2003 12:43:52 AM BGooijen + changeable reply types + + Rev 1.23 5/13/2003 2:56:40 PM BGooijen + changed GetGreating to SendGreeting + + Rev 1.21 4/4/2003 8:09:46 PM BGooijen + moved some consts tidcmdtcpserver, changed DoExecute to return + .connection.connected + + Rev 1.20 3/25/2003 9:04:06 PM BGooijen + Scheduler in IOHandler is now updated when the scheduler is removed + + Rev 1.19 3/23/2003 11:33:34 PM BGooijen + Updates the scheduler in the iohandler when scheduler/iohandler is changed + + Rev 1.18 3/22/2003 11:44:08 PM BGooijen + ServerIntercept now logs connects/disconnects + + Rev 1.17 3/22/2003 1:46:02 PM BGooijen + Better handling of exceptions in TIdListenerThread.Run (could cause mem leaks + first (in non-paged-memory)) + + Rev 1.16 3/21/2003 5:55:54 PM BGooijen + Added code for serverIntercept + + Rev 1.15 3/21/2003 11:44:00 AM JPMugaas + Updated with a OnBeforeConnect event for the TIdMappedPort components. + + Rev 1.14 3/20/2003 12:18:32 PM BGooijen + Moved ReplyExceptionCode from TIdTCPServer to TIdCmdTCPServer + + Rev 1.13 3/13/2003 10:18:26 AM BGooijen + Server side fibers, bug fixes + + Rev 1.12 2003.02.18 5:52:16 PM czhower + Fix for warnings and logic error. + + Rev 1.11 1/23/2003 8:33:16 PM BGooijen + + Rev 1.10 1/23/2003 11:05:48 AM BGooijen + + Rev 1.9 1/20/2003 12:50:44 PM BGooijen + Added a Contexts propperty, which contains all contexts for that server + Moved the commandhandlers to TIdCmdTCPServer + + Rev 1.8 1-18-2003 0:00:30 BGooijen + Removed TIdContext.OnCreate + Added ContextCreated + + Rev 1.7 1-17-2003 23:44:32 BGooijen + added support code for TIdContext.OnCreate + + Rev 1.6 1-17-2003 22:22:10 BGooijen + new design + + Rev 1.5 1-10-2003 23:59:22 BGooijen + Connection is now freed in destructor of TIdContext + + Rev 1.4 1-10-2003 19:46:22 BGooijen + The context was not freed, now it is + + Rev 1.3 1-9-2003 11:52:28 BGooijen + changed construction of TIdContext to Create(AServer: TIdTCPServer) + added TIdContext property .Server + + Rev 1.2 1-3-2003 19:05:56 BGooijen + added FContextClass:TIdContextClass to TIdTcpServer + added Data:TObject to TIdContext + + Rev 1.1 1-1-2003 16:42:10 BGooijen + Changed TIdThread to TIdYarn + Added TIdContext + + Rev 1.0 11/13/2002 09:00:42 AM JPMugaas + +2002-01-01 - Andrew P.Rybin + - bug fix (MaxConnections, SetActive(FALSE)), TerminateListenerThreads, DoExecute + +2002-04-17 - Andrew P.Rybin + - bug fix: if exception raised in OnConnect, Threads.Remove and ThreadMgr.ReleaseThread are not called +} + +unit IdCustomTCPServer; + +{ + Original Author and Maintainer: + - Chad Z. Hower a.k.a Kudzu +} + +interface + +{$I IdCompilerDefines.inc} +//here to flip FPC into Delphi mode + +uses + Classes, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdBaseComponent, + IdComponent,IdContext, IdGlobal, IdException, + IdIntercept, IdIOHandler, IdIOHandlerStack, + IdReply, IdScheduler, IdSchedulerOfThread, IdServerIOHandler, + IdServerIOHandlerStack, IdSocketHandle, IdStackConsts, IdTCPConnection, + IdThread, IdYarn, SysUtils; + +const + IdListenQueueDefault = 15; + +type + TIdCustomTCPServer = class; + + // This is the thread that listens for incoming connections and spawns + // new ones to handle each one + TIdListenerThread = class(TIdThread) + protected + FBinding: TIdSocketHandle; + FServer: TIdCustomTCPServer; + FOnBeforeRun: TIdNotifyThreadEvent; + // + procedure AfterRun; override; + procedure BeforeRun; override; + procedure Run; override; + public + constructor Create(AServer: TIdCustomTCPServer; ABinding: TIdSocketHandle); reintroduce; + // + property Binding: TIdSocketHandle read FBinding; + property Server: TIdCustomTCPServer read FServer; + property OnBeforeRun: TIdNotifyThreadEvent read FOnBeforeRun write FOnBeforeRun; + End; + + {$IFDEF HAS_GENERICS_TThreadList} + TIdListenerThreadList = TThreadList; + TIdListenerList = TList; + {$ELSE} + // TODO: flesh out to match TThreadList and TList for non-Generics compilers + TIdListenerThreadList = TThreadList; + TIdListenerList = TList; + {$ENDIF} + + TIdListenExceptionEvent = procedure(AThread: TIdListenerThread; AException: Exception) of object; + TIdServerThreadExceptionEvent = procedure(AContext: TIdContext; AException: Exception) of object; + TIdServerThreadEvent = procedure(AContext: TIdContext) of object; + + TIdServerContext = class(TIdContext) + protected + FServer: TIdCustomTCPServer; + public + property Server: TIdCustomTCPServer read FServer; + end; + + TIdServerContextClass = class of TIdServerContext; + + TIdCustomTCPServer = class(TIdComponent) + protected + FActive: Boolean; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FScheduler: TIdScheduler; + FBindings: TIdSocketHandles; + FContextClass: TIdServerContextClass; + FImplicitScheduler: Boolean; + FImplicitIOHandler: Boolean; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIntercept: TIdServerIntercept; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIOHandler: TIdServerIOHandler; + FListenerThreads: TIdListenerThreadList; + FListenQueue: integer; + FMaxConnections: Integer; + FReuseSocket: TIdReuseSocket; + FTerminateWaitTime: Integer; + FContexts: TIdContextThreadList; + FOnContextCreated: TIdServerThreadEvent; + FOnConnect: TIdServerThreadEvent; + FOnDisconnect: TIdServerThreadEvent; + FOnException: TIdServerThreadExceptionEvent; + FOnExecute: TIdServerThreadEvent; + FOnListenException: TIdListenExceptionEvent; + FOnBeforeBind: TIdSocketHandleEvent; + FOnAfterBind: TNotifyEvent; + FOnBeforeListenerRun: TIdNotifyThreadEvent; + FUseNagle : Boolean; + // + procedure CheckActive; + procedure CheckOkToBeActive; virtual; + procedure ContextCreated(AContext: TIdContext); virtual; + procedure ContextConnected(AContext: TIdContext); virtual; + procedure ContextDisconnected(AContext: TIdContext); virtual; + function CreateConnection: TIdTCPConnection; virtual; + procedure DoBeforeBind(AHandle: TIdSocketHandle); virtual; + procedure DoAfterBind; virtual; + procedure DoBeforeListenerRun(AThread: TIdThread); virtual; + procedure DoConnect(AContext: TIdContext); virtual; + procedure DoDisconnect(AContext: TIdContext); virtual; + procedure DoException(AContext: TIdContext; AException: Exception); virtual; + function DoExecute(AContext: TIdContext): Boolean; virtual; + procedure DoListenException(AThread: TIdListenerThread; AException: Exception); virtual; + procedure DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler); virtual; + procedure DoTerminateContext(AContext: TIdContext); virtual; + function GetDefaultPort: TIdPort; + procedure InitComponent; override; + procedure Loaded; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + // This is needed for POP3's APOP authentication. For that, + // you send a unique challenge to the client dynamically. + procedure SendGreeting(AContext: TIdContext; AGreeting: TIdReply); virtual; + procedure SetActive(AValue: Boolean); virtual; + procedure SetBindings(const AValue: TIdSocketHandles); virtual; + procedure SetDefaultPort(const AValue: TIdPort); virtual; + procedure SetIntercept(const AValue: TIdServerIntercept); virtual; + procedure SetIOHandler(const AValue: TIdServerIOHandler); virtual; + procedure SetScheduler(const AValue: TIdScheduler); virtual; + procedure Startup; virtual; + procedure Shutdown; virtual; + procedure TerminateAllThreads; + // Occurs in the context of the peer thread + property OnExecute: TIdServerThreadEvent read FOnExecute write FOnExecute; + + public + destructor Destroy; override; + // + procedure StartListening; + procedure StopListening; + // + property Contexts: TIdContextThreadList read FContexts; + property ContextClass: TIdServerContextClass read FContextClass write FContextClass; + property ImplicitIOHandler: Boolean read FImplicitIOHandler; + property ImplicitScheduler: Boolean read FImplicitScheduler; + published + property Active: Boolean read FActive write SetActive default False; + property Bindings: TIdSocketHandles read FBindings write SetBindings; + property DefaultPort: TIdPort read GetDefaultPort write SetDefaultPort; + property Intercept: TIdServerIntercept read FIntercept write SetIntercept; + property IOHandler: TIdServerIOHandler read FIOHandler write SetIOHandler; + property ListenQueue: integer read FListenQueue write FListenQueue default IdListenQueueDefault; + property MaxConnections: Integer read FMaxConnections write FMaxConnections default 0; + // right before/after binding sockets + property OnBeforeBind: TIdSocketHandleEvent read FOnBeforeBind write FOnBeforeBind; + property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind; + property OnBeforeListenerRun: TIdNotifyThreadEvent read FOnBeforeListenerRun write FOnBeforeListenerRun; + property OnContextCreated: TIdServerThreadEvent read FOnContextCreated write FOnContextCreated; + // Occurs in the context of the peer thread + property OnConnect: TIdServerThreadEvent read FOnConnect write FOnConnect; + // Occurs in the context of the peer thread + property OnDisconnect: TIdServerThreadEvent read FOnDisconnect write FOnDisconnect; + // Occurs in the context of the peer thread + property OnException: TIdServerThreadExceptionEvent read FOnException write FOnException; + property OnListenException: TIdListenExceptionEvent read FOnListenException write FOnListenException; + property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent; // {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use TIdSocketHandle.ReuseSocket'{$ENDIF};{$ENDIF} +//UseNagle should be set to true in most cases. +//See: http://tangentsoft.net/wskfaq/intermediate.html#disable-nagle and +// http://tangentsoft.net/wskfaq/articles/lame-list.html#item19 +//The Nagle algorithm reduces the amount of needless traffic. Disabling Nagle +//programs throughput to degrade. + property UseNagle: boolean read FUseNagle write FUseNagle default true; + property TerminateWaitTime: Integer read FTerminateWaitTime write FTerminateWaitTime default 5000; + property Scheduler: TIdScheduler read FScheduler write SetScheduler; + end; + + EIdTCPServerError = class(EIdException); + EIdNoExecuteSpecified = class(EIdTCPServerError); + EIdTerminateThreadTimeout = class(EIdTCPServerError); + +implementation + +uses + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + IdGlobalCore, + IdResourceStringsCore, IdReplyRFC, + IdSchedulerOfThreadDefault, IdStack, + IdThreadSafe; + +{ TIdCustomTCPServer } + +procedure TIdCustomTCPServer.CheckActive; +begin + if Active and (not IsDesignTime) and (not IsLoading) then begin + raise EIdTCPServerError.Create(RSCannotPerformTaskWhileServerIsActive); + end; +end; + +procedure TIdCustomTCPServer.ContextCreated(AContext: TIdContext); +begin + if Assigned(FOnContextCreated) then begin + FOnContextCreated(AContext); + end; +end; + +destructor TIdCustomTCPServer.Destroy; +begin + Active := False; + + SetIOHandler(nil); + + // Destroy bindings first + FreeAndNil(FBindings); + // + FreeAndNil(FContexts); + FreeAndNil(FListenerThreads); + // + inherited Destroy; +end; + +procedure TIdCustomTCPServer.DoBeforeBind(AHandle: TIdSocketHandle); +begin + if Assigned(FOnBeforeBind) then begin + FOnBeforeBind(AHandle); + end; +end; + +procedure TIdCustomTCPServer.DoAfterBind; +begin + if Assigned(FOnAfterBind) then begin + FOnAfterBind(Self); + end; +end; + +procedure TIdCustomTCPServer.SendGreeting(AContext: TIdContext; AGreeting: TIdReply); +begin + AContext.Connection.IOHandler.Write(AGreeting.FormattedReply); +end; + +procedure TIdCustomTCPServer.ContextConnected(AContext: TIdContext); +var + // under ARC, convert weak references to strong references before working with them + LServerIntercept: TIdServerIntercept; + LConnIntercept: TIdConnectionIntercept; +begin + LServerIntercept := Intercept; + if Assigned(LServerIntercept) then begin + LConnIntercept := LServerIntercept.Accept(AContext.Connection); + AContext.Connection.IOHandler.Intercept := LConnIntercept; + if Assigned(LConnIntercept) then begin + LConnIntercept.Connect(AContext.Connection); + end; + end; + DoConnect(AContext); +end; + +procedure TIdCustomTCPServer.ContextDisconnected(AContext: TIdContext); +var + // under ARC, convert weak references to strong references before working with them + LIOHandler: TIdIOHandler; + LIntercept: TIdConnectionIntercept; +begin + DoDisconnect(AContext); + LIOHandler := AContext.Connection.IOHandler; + if Assigned(LIOHandler) then begin + LIntercept := LIOHandler.Intercept; + if Assigned(LIntercept) then begin + LIntercept.Disconnect; + FreeAndNil(LIntercept); + LIOHandler.Intercept := nil; + end; + end; +end; + +function TIdCustomTCPServer.CreateConnection: TIdTCPConnection; +begin + Result := TIdTCPConnection.Create(nil); +end; + +procedure TIdCustomTCPServer.DoConnect(AContext: TIdContext); +begin + if Assigned(OnConnect) then begin + OnConnect(AContext); + end; +end; + +procedure TIdCustomTCPServer.DoDisconnect(AContext: TIdContext); +begin + if Assigned(OnDisconnect) then begin + OnDisconnect(AContext); + end; +end; + +procedure TIdCustomTCPServer.DoException(AContext: TIdContext; AException: Exception); +begin + if Assigned(OnException) then begin + OnException(AContext, AException); + end; +end; + +function TIdCustomTCPServer.DoExecute(AContext: TIdContext): Boolean; +begin + if Assigned(OnExecute) then begin + OnExecute(AContext); + end; + Result := False; + if AContext <> nil then begin + if AContext.Connection <> nil then begin + Result := AContext.Connection.Connected; + end; + end; +end; + +procedure TIdCustomTCPServer.DoListenException(AThread: TIdListenerThread; AException: Exception); +begin + if Assigned(FOnListenException) then begin + FOnListenException(AThread, AException); + end; +end; + +function TIdCustomTCPServer.GetDefaultPort: TIdPort; +begin + Result := FBindings.DefaultPort; +end; + +procedure TIdCustomTCPServer.Loaded; +begin + inherited Loaded; + // Active = True must not be performed before all other props are loaded + if Active then begin + FActive := False; + Active := True; + end; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdCustomTCPServer.Notification(AComponent: TComponent; Operation: TOperation); +begin + // Remove the reference to the linked components if they are deleted + if (Operation = opRemove) then begin + if (AComponent = FScheduler) then begin + FScheduler := nil; + FImplicitScheduler := False; + end + else if (AComponent = FIntercept) then begin + FIntercept := nil; + end + else if (AComponent = FIOHandler) then begin + FIOHandler := nil; + FImplicitIOHandler := False; + end; + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdCustomTCPServer.SetActive(AValue: Boolean); +begin + // At design time we just set the value and save it for run time. + // During loading we ignore it till all other properties are set. + // Loaded will recall it to toggle it + if IsDesignTime or IsLoading then begin + FActive := AValue; + end + else if FActive <> AValue then begin + if AValue then begin + CheckOkToBeActive; + try + Startup; + except + FActive := True; + SetActive(False); // allow descendants to clean up + raise; + end; + FActive := True; + end else begin + // Must set to False here. Shutdown() implementations call property setters that check this + FActive := False; + Shutdown; + end; + end; +end; + +procedure TIdCustomTCPServer.SetBindings(const AValue: TIdSocketHandles); +begin + FBindings.Assign(AValue); +end; + +procedure TIdCustomTCPServer.SetDefaultPort(const AValue: TIdPort); +begin + FBindings.DefaultPort := AValue; +end; + +procedure TIdCustomTCPServer.SetIntercept(const AValue: TIdServerIntercept); +begin + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FIntercept := AValue; + {$ELSE} + if FIntercept <> AValue then + begin + // Remove self from the intercept's notification list + if Assigned(FIntercept) then begin + FIntercept.RemoveFreeNotification(Self); + end; + FIntercept := AValue; + // Add self to the intercept's notification list + if Assigned(FIntercept) then begin + FIntercept.FreeNotification(Self); + end; + end; + {$ENDIF} +end; + +procedure TIdCustomTCPServer.SetScheduler(const AValue: TIdScheduler); +var + // under ARC, convert weak references to strong references before working with them + LScheduler: TIdScheduler; + LIOHandler: TIdServerIOHandler; +begin + LScheduler := FScheduler; + + if LScheduler <> AValue then + begin + // RLebeau - is this really needed? What should happen if this + // gets called by Notification() if the Scheduler is freed while + // the server is still Active? + if Active then begin + raise EIdException.Create(RSTCPServerSchedulerAlreadyActive); + end; + + // under ARC, all weak references to a freed object get nil'ed automatically + + // If implicit one already exists free it + // Free the default Thread manager + if FImplicitScheduler then begin + // Under D8 notification gets called after .Free of FreeAndNil, but before + // its set to nil with a side effect of IDisposable. To counteract this we + // set it to nil first. + // -Kudzu + FScheduler := nil; + FImplicitScheduler := False; + IdDisposeAndNil(LScheduler); + end; + + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will no longer be notified when the component is freed + if LScheduler <> nil then begin + LScheduler.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FScheduler := AValue; + + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will be notified when the component is freed, even is it's on + // another form + if AValue <> nil then begin + AValue.FreeNotification(Self); + end; + {$ENDIF} + + LIOHandler := FIOHandler; + if LIOHandler <> nil then begin + LIOHandler.SetScheduler(AValue); + end; + end; +end; + +procedure TIdCustomTCPServer.SetIOHandler(const AValue: TIdServerIOHandler); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdServerIOHandler; +begin + LIOHandler := FIOHandler; + + if LIOHandler <> AValue then begin + + // RLebeau - is this needed? SetScheduler() does it, so should SetIOHandler() + // also do it? What should happen if this gets called by Notification() if the + // IOHandler is freed while the server is still Active? + { + if Active then begin + raise EIdException.Create(RSTCPServerIOHandlerAlreadyActive); + end; + } + + if FImplicitIOHandler then begin + FIOHandler := nil; + FImplicitIOHandler := False; + IdDisposeAndNil(LIOHandler); + end; + + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will no longer be notified when the component is freed + if Assigned(LIOHandler) then begin + LIOHandler.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FIOHandler := AValue; + + if AValue <> nil then begin + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will be notified when the component is freed, even is it's on + // another form + AValue.FreeNotification(Self); + {$ENDIF} + AValue.SetScheduler(FScheduler); + end; + end; +end; + +procedure TIdCustomTCPServer.StartListening; +var + LListenerThreads: TIdListenerList; + LListenerThread: TIdListenerThread; + I: Integer; + LBinding: TIdSocketHandle; +begin + LListenerThreads := FListenerThreads.LockList; + try + // Set up any sockets that are not already listening + I := LListenerThreads.Count; + try + while I < Bindings.Count do begin + LBinding := Bindings[I]; + LBinding.AllocateSocket; + // do not overwrite if the default. This allows ReuseSocket to be set per binding + if FReuseSocket <> rsOSDependent then begin + LBinding.ReuseSocket := FReuseSocket; + end; + DoBeforeBind(LBinding); + LBinding.Bind; + LBinding.UseNagle := FUseNagle; + Inc(I); + end; + except + Dec(I); // the one that failed doesn't need to be closed + while I >= 0 do begin + Bindings[I].CloseSocket; + Dec(I); + end; + raise; + end; + + if I > LListenerThreads.Count then begin + DoAfterBind; + end; + + // Set up any threads that are not already running + for I := LListenerThreads.Count to Bindings.Count - 1 do + begin + LBinding := Bindings[I]; + LBinding.Listen(FListenQueue); + LListenerThread := TIdListenerThread.Create(Self, LBinding); + try + LListenerThread.Name := Name + ' Listener #' + IntToStr(I + 1); {do not localize} + LListenerThread.OnBeforeRun := DoBeforeListenerRun; + //Todo: Implement proper priority handling for Linux + //http://www.midnightbeach.com/jon/pubs/2002/BorCon.London/Sidebar.3.html + LListenerThread.Priority := tpListener; + LListenerThreads.Add(LListenerThread); + except + LBinding.CloseSocket; + FreeAndNil(LListenerThread); + raise; + end; + LListenerThread.Start; + end; + finally + FListenerThreads.UnlockList; + end; +end; + +//APR-011207: for safe-close Ex: SQL Server ShutDown 1) stop listen 2) wait until all clients go out +procedure TIdCustomTCPServer.StopListening; +var + LListenerThreads: TIdListenerList; + LListener: TIdListenerThread; +begin + LListenerThreads := FListenerThreads.LockList; + try + while LListenerThreads.Count > 0 do begin + LListener := {$IFDEF HAS_GENERICS_TThreadList}LListenerThreads[0]{$ELSE}TIdListenerThread(LListenerThreads[0]){$ENDIF}; + // Stop listening + LListener.Terminate; + LListener.Binding.CloseSocket; + // Tear down Listener thread + LListener.WaitFor; + LListener.Free; + LListenerThreads.Delete(0); // RLebeau 2/17/2006 + end; + finally + FListenerThreads.UnlockList; + end; +end; + +{$IFDEF STRING_IS_UNICODE} +//This is an ugly hack that's required because a ShortString does not seem +//to be acceptable to D2009's Assert function. +procedure AssertClassName(const ABool : Boolean; const AString : String); inline; +begin + Assert(ABool, AString); +end; +{$ENDIF} + +procedure TIdCustomTCPServer.TerminateAllThreads; +var + i: Integer; + LContext: TIdContext; + LList: TIdContextList; + + // under ARC, convert a weak reference to a strong reference before working with it + LScheduler: TIdScheduler; +begin + // TODO: reimplement support for TerminateWaitTimeout + + //BGO: find out why TerminateAllThreads is sometimes called multiple times + //Kudzu: Its because of notifications. It calls shutdown when the Scheduler is + // set to nil and then again on destroy. + if Contexts <> nil then begin + LList := Contexts.LockList; + try + for i := 0 to LList.Count - 1 do begin + LContext := {$IFDEF HAS_GENERICS_TList}LList.Items[i]{$ELSE}TIdContext(LList.Items[i]){$ENDIF}; + Assert(LContext<>nil); + {$IFDEF STRING_IS_UNICODE} + AssertClassName(LContext.Connection<>nil, LContext.ClassName); + {$ELSE} + Assert(LContext.Connection<>nil, LContext.ClassName); + {$ENDIF} + // RLebeau: allow descendants to perform their own cleanups before + // closing the connection. FTP, for example, needs to abort an + // active data transfer on a separate asociated connection + DoTerminateContext(LContext); + end; + finally + Contexts.UnLockList; + end; + end; + + // Scheduler may be nil during destroy which calls TerminateAllThreads + // This happens with explicit schedulers + + LScheduler := FScheduler; + if Assigned(LScheduler) then begin + LScheduler.TerminateAllYarns; + end; +end; + +procedure TIdCustomTCPServer.DoBeforeListenerRun(AThread: TIdThread); +begin + if Assigned(OnBeforeListenerRun) then begin + OnBeforeListenerRun(AThread); + end; +end; + +procedure TIdCustomTCPServer.DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler); +begin +// +end; + +procedure TIdCustomTCPServer.DoTerminateContext(AContext: TIdContext); +begin + // Dont call disconnect with true. Otherwise it frees the IOHandler and the thread + // is still running which often causes AVs and other. + AContext.Connection.Disconnect(False); +end; + +procedure TIdCustomTCPServer.InitComponent; +begin + inherited InitComponent; + FBindings := TIdSocketHandles.Create(Self); + FContexts := TIdContextThreadList.Create; + FContextClass := TIdServerContext; + // + FTerminateWaitTime := 5000; + FListenQueue := IdListenQueueDefault; + FListenerThreads := TIdListenerThreadList.Create; + //TODO: When reestablished, use a sleeping thread instead +// fSessionTimer := TTimer.Create(self); + FUseNagle := true; // default +end; + +procedure TIdCustomTCPServer.Shutdown; +var + // under ARC, convert the weak reference to a strong reference before working with it + LIOHandler: TIdServerIOHandler; +begin + // tear down listening threads + StopListening; + + // Tear down ThreadMgr + try + TerminateAllThreads; + finally + {//bgo TODO: fix this: and Threads.IsCountLessThan(1)} + // DONE -oAPR: BUG! Threads still live, Mgr dead ;-( + if ImplicitScheduler then begin + SetScheduler(nil); + end; + end; + + LIOHandler := IOHandler; + if LIOHandler <> nil then begin + LIOHandler.Shutdown; + end; +end; + +// Linux/Unix does not allow an IPv4 socket and an IPv6 socket +// to listen on the same port at the same time! Windows does not +// have that problem... +{$IFNDEF IdIPv6} + {$DEFINE CanCreateTwoBindings} + {$IFDEF LINUX} // should this be UNIX instead? + {$UNDEF CanCreateTwoBindings} + {$ENDIF} + {$IFDEF ANDROID} + {$UNDEF CanCreateTwoBindings} + {$ENDIF} +{$ENDIF} + +procedure TIdCustomTCPServer.Startup; +var + LScheduler: TIdScheduler; + LIOHandler: TIdServerIOHandler; +begin + // Set up bindings + if Bindings.Count = 0 then begin + // TODO: on systems that support dual-stack sockets, create a single + // Binding object that supports both IPv4 and IPv6 on the same socket... + Bindings.Add; // IPv4 or IPv6 by default + {$IFNDEF IdIPv6} + {$IFDEF CanCreateTwoBindings} + if GStack.SupportsIPv6 then begin + // maybe add a property too, so the developer can switch it on/off + Bindings.Add.IPVersion := Id_IPv6; + end; + {$ENDIF} + {$ENDIF} + end; + + // Setup IOHandler + LIOHandler := FIOHandler; + if not Assigned(LIOHandler) then begin + LIOHandler := TIdServerIOHandlerStack.Create(Self); + SetIOHandler(LIOHandler); + FImplicitIOHandler := True; + end; + LIOHandler.Init; + + // Set up scheduler + LScheduler := FScheduler; + if not Assigned(FScheduler) then begin + LScheduler := TIdSchedulerOfThreadDefault.Create(Self); + SetScheduler(LScheduler); + FImplicitScheduler := True; + // Useful in debugging and for thread names + LScheduler.Name := Name + 'Scheduler'; {do not localize} + end; + LScheduler.Init; + + StartListening; +end; + +procedure TIdCustomTCPServer.CheckOkToBeActive; +begin + //nothing here. Override in a descendant for an end-point +end; + +{ TIdListenerThread } + +procedure TIdListenerThread.AfterRun; +begin + inherited AfterRun; + // Close just own binding. The rest will be closed from their coresponding + // threads + FBinding.CloseSocket; +end; + +procedure TIdListenerThread.BeforeRun; +begin + inherited BeforeRun; + if Assigned(FOnBeforeRun) then begin + FOnBeforeRun(Self); + end; +end; + +constructor TIdListenerThread.Create(AServer: TIdCustomTCPServer; ABinding: TIdSocketHandle); +begin + inherited Create; + FBinding := ABinding; + FServer := AServer; +end; + +type + TIdServerContextAccess = class(TIdServerContext) + end; + +procedure TIdListenerThread.Run; +var + LContext: TIdServerContext; + LIOHandler: TIdIOHandler; + LPeer: TIdTCPConnection; + LYarn: TIdYarn; +begin + Assert(Server<>nil); + Assert(Server.IOHandler<>nil); + + LContext := nil; + LPeer := nil; + LYarn := nil; + try + // GetYarn can raise exceptions + LYarn := Server.Scheduler.AcquireYarn; + + // TODO: under Windows at least, use SO_CONDITIONAL_ACCEPT to allow + // the user to reject connections before they are accepted. Somehow + // expose an event here for the user to decide with... + + LIOHandler := Server.IOHandler.Accept(Binding, Self, LYarn); + if LIOHandler = nil then begin + // Listening has finished + Stop; + Abort; + end else begin + // We have accepted the connection and need to handle it + LPeer := TIdTCPConnection.Create(nil); + {$IFDEF USE_OBJECT_ARC} + // under ARC, the TIdTCPConnection.IOHandler property is a weak reference. + // TIdServerIOHandler.Accept() returns an IOHandler with no Owner assigned, + // so lets make the TIdTCPConnection become the Owner in order to keep the + // IOHandler alive whic this method exits. + // + // TODO: should we assign Ownership unconditionally on all platforms? + // + LPeer.InsertComponent(LIOHandler); + {$ENDIF} + LPeer.IOHandler := LIOHandler; + LPeer.ManagedIOHandler := True; + end; + + // LastRcvTimeStamp := Now; // Added for session timeout support + // ProcessingTimeout := False; + + // Check MaxConnections + if (Server.MaxConnections > 0) and (not Server.Contexts.IsCountLessThan(Server.MaxConnections)) then begin + FServer.DoMaxConnectionsExceeded(LIOHandler); + LPeer.Disconnect; + Abort; + end; + + // Create and init context + LContext := Server.FContextClass.Create(LPeer, LYarn, Server.Contexts); + LContext.FServer := Server; + // We set these instead of having the context call them directly + // because they are protected methods. Also its good to keep + // Context indepent of the server as well. + LContext.OnBeforeRun := Server.ContextConnected; + LContext.OnRun := Server.DoExecute; + LContext.OnAfterRun := Server.ContextDisconnected; + LContext.OnException := Server.DoException; + // + Server.ContextCreated(LContext); + // + // If all ok, lets start the yarn + Server.Scheduler.StartYarn(LYarn, LContext); + except + on E: Exception do begin + // RLebeau 1/11/07: TIdContext owns the Peer by default so + // take away ownership here so the Peer is not freed twice + if LContext <> nil then begin + TIdServerContextAccess(LContext).FOwnsConnection := False; + end; + FreeAndNil(LContext); + FreeAndNil(LPeer); + // Must terminate - likely has not started yet + if LYarn <> nil then begin + Server.Scheduler.TerminateYarn(LYarn); + end; + // EAbort is used to kick out above and destroy yarns and other, but + // we dont want to show the user + if not (E is EAbort) then begin + Server.DoListenException(Self, E); + end; + end; + end; +end; + +end. + diff --git a/indy/Core/IdCustomTransparentProxy.pas b/indy/Core/IdCustomTransparentProxy.pas new file mode 100644 index 0000000..1e0aac0 --- /dev/null +++ b/indy/Core/IdCustomTransparentProxy.pas @@ -0,0 +1,253 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 11/12/2004 11:30:16 AM JPMugaas + Expansions for IPv6. + + Rev 1.9 11/11/2004 10:25:22 PM JPMugaas + Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions + from the UDP client with SOCKS. You must call OpenProxy before using + RecvFrom or SendTo. When you are finished, you must use CloseProxy to close + any connection to the Proxy. Connect and disconnect also call OpenProxy and + CloseProxy. + + Rev 1.8 11/11/2004 3:42:52 AM JPMugaas + Moved strings into RS. Socks will now raise an exception if you attempt to + use SOCKS4 and SOCKS4A with UDP. Those protocol versions do not support UDP + at all. + + Rev 1.7 11/9/2004 8:18:00 PM JPMugaas + Attempt to add SOCKS support in UDP. + + Rev 1.6 6/6/2004 11:51:56 AM JPMugaas + Fixed TODO with an exception + + Rev 1.5 2004.02.03 4:17:04 PM czhower + For unit name changes. + + Rev 1.4 10/15/2003 10:59:06 PM DSiders + Corrected spelling error in resource string name. + Added resource string for circular links exception in transparent proxy. + + Rev 1.3 10/15/2003 10:10:18 PM DSiders + Added localization comments. + + Rev 1.2 5/16/2003 9:22:38 AM BGooijen + Added Listen(...) + + Rev 1.1 5/14/2003 6:41:00 PM BGooijen + Added Bind(...) + + Rev 1.0 12/2/2002 05:01:26 PM JPMugaas + Rechecked in due to file corruption. +} + +unit IdCustomTransparentProxy; + +interface + +{$I IdCompilerDefines.inc} +//we need to put this in Delphi mode to work + +uses + Classes, + IdComponent, + IdException, + IdGlobal, + IdIOHandler, + IdSocketHandle, + IdBaseComponent; + +type + EIdTransparentProxyCircularLink = class(EIdException); + EIdTransparentProxyUDPNotSupported = class(EIdException); + + TIdCustomTransparentProxyClass = class of TIdCustomTransparentProxy; + + TIdCustomTransparentProxy = class(TIdComponent) + protected + FHost: String; + FPassword: String; + FPort: TIdPort; + FIPVersion : TIdIPVersion; + FUsername: String; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FChainedProxy: TIdCustomTransparentProxy; + // + function GetEnabled: Boolean; virtual; abstract; + procedure SetEnabled(AValue: Boolean); virtual; + procedure MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); virtual; abstract; + {$IFNDEF USE_OBJECT_ARC} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + {$ENDIF} + procedure SetChainedProxy(const AValue: TIdCustomTransparentProxy); + public + procedure Assign(ASource: TPersistent); override; + procedure OpenUDP(AHandle : TIdSocketHandle; const AHost: string = ''; const APort: TIdPort = 0; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); virtual; + procedure CloseUDP(AHandle: TIdSocketHandle); virtual; + function RecvFromUDP(AHandle: TIdSocketHandle; var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion; + AMSec: Integer = IdTimeoutDefault): Integer; virtual; + procedure SendToUDP(AHandle: TIdSocketHandle; + const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion; + const ABuffer : TIdBytes); virtual; + procedure Connect(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); + // + procedure Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);overload;virtual; + procedure Bind(AIOHandler: TIdIOHandler; const APort: TIdPort); overload; + function Listen(AIOHandler: TIdIOHandler; const ATimeOut: Integer): Boolean; virtual; + // + property Enabled: Boolean read GetEnabled write SetEnabled; + property Host: String read FHost write FHost; + property Password: String read FPassword write FPassword; + property Port: TIdPort read FPort write FPort; + property IPVersion : TIdIPVersion read FIPVersion write FIPVersion default ID_DEFAULT_IP_VERSION; + property Username: String read FUsername write FUsername; + property ChainedProxy: TIdCustomTransparentProxy read FChainedProxy write SetChainedProxy; + end; + +implementation + +uses + IdResourceStringsCore, IdExceptionCore; + +{ TIdCustomTransparentProxy } + +procedure TIdCustomTransparentProxy.Assign(ASource: TPersistent); +var + LSource: TIdCustomTransparentProxy; +Begin + if ASource is TIdCustomTransparentProxy then begin + LSource := TIdCustomTransparentProxy(ASource); + FHost := LSource.Host; + FPassword := LSource.Password; + FPort := LSource.Port; + FIPVersion := LSource.IPVersion; + FUsername := LSource.Username; + end else begin + inherited Assign(ASource); + end; +End;// + +procedure TIdCustomTransparentProxy.Connect(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +var + // under ARC, convert a weak reference to a strong reference before working with it + LChainedProxy: TIdCustomTransparentProxy; +begin + LChainedProxy := FChainedProxy; + if Assigned(LChainedProxy) and LChainedProxy.Enabled then begin + MakeConnection(AIOHandler, LChainedProxy.Host, LChainedProxy.Port); + LChainedProxy.Connect(AIOHandler, AHost, APort, AIPVersion); + end else begin + MakeConnection(AIOHandler, AHost, APort, AIPVersion); + end; +end; + +function TIdCustomTransparentProxy.Listen(AIOHandler: TIdIOHandler; const ATimeOut: integer):boolean; +begin + raise EIdTransparentProxyCantBind.Create(RSTransparentProxyCannotBind); +end; + +procedure TIdCustomTransparentProxy.Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + raise EIdTransparentProxyCantBind.Create(RSTransparentProxyCannotBind); +end; + +procedure TIdCustomTransparentProxy.Bind(AIOHandler: TIdIOHandler; const APort: TIdPort); +begin + Bind(AIOHandler, '0.0.0.0', APort); {do not localize} +end; + +procedure TIdCustomTransparentProxy.SetEnabled(AValue: Boolean); +Begin +End; + +// under ARC, all weak references to a freed object get nil'ed automatically +{$IFNDEF USE_OBJECT_ARC} +procedure TIdCustomTransparentProxy.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FChainedProxy) then begin + FChainedProxy := nil; + end; + inherited Notification(AComponent,Operation); +end; +{$ENDIF} + +procedure TIdCustomTransparentProxy.SetChainedProxy(const AValue: TIdCustomTransparentProxy); +var + LNextValue: TIdCustomTransparentProxy; + // under ARC, convert a weak reference to a strong reference before working with it + LChainedProxy: TIdCustomTransparentProxy; +begin + LChainedProxy := FChainedProxy; + + if LChainedProxy <> AValue then + begin + LNextValue := AValue; + while Assigned(LNextValue) do begin + if LNextValue = Self then begin + raise EIdTransparentProxyCircularLink.CreateFmt(RSInterceptCircularLink, [ClassName]);// -> One EIDCircularLink exception + end; + LNextValue := LNextValue.ChainedProxy; + end; + + // under ARC, all weak references to a freed object get nil'ed automatically + + {$IFNDEF USE_OBJECT_ARC} + if Assigned(LChainedProxy) then begin + LChainedProxy.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FChainedProxy := AValue; + + {$IFNDEF USE_OBJECT_ARC} + if Assigned(AValue) then begin + AValue.FreeNotification(Self); + end; + {$ENDIF} + end; +end; + +procedure TIdCustomTransparentProxy.CloseUDP(AHandle: TIdSocketHandle); +begin + raise EIdTransparentProxyUDPNotSupported.Create(RSTransparentProxyCanNotSupportUDP); +end; + +procedure TIdCustomTransparentProxy.OpenUDP(AHandle: TIdSocketHandle; + const AHost: string = ''; const APort: TIdPort = 0; + const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + raise EIdTransparentProxyUDPNotSupported.Create(RSTransparentProxyCanNotSupportUDP); +end; + +function TIdCustomTransparentProxy.RecvFromUDP(AHandle: TIdSocketHandle; + var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort; + var VIPVersion: TIdIPVersion; AMSec: Integer = IdTimeoutDefault): Integer; +begin + raise EIdTransparentProxyUDPNotSupported.Create(RSTransparentProxyCanNotSupportUDP); +end; + +procedure TIdCustomTransparentProxy.SendToUDP(AHandle: TIdSocketHandle; + const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion; + const ABuffer : TIdBytes); +begin + raise EIdTransparentProxyUDPNotSupported.Create(RSTransparentProxyCanNotSupportUDP); +end; + +end. + diff --git a/indy/Core/IdDeprecatedImplBugOff.inc b/indy/Core/IdDeprecatedImplBugOff.inc new file mode 100644 index 0000000..1e8eee8 --- /dev/null +++ b/indy/Core/IdDeprecatedImplBugOff.inc @@ -0,0 +1,4 @@ +{$IFDEF DEPRECATED_IMPL_BUG} + {$WARN SYMBOL_DEPRECATED OFF} +{$ENDIF} + diff --git a/indy/Core/IdDeprecatedImplBugOn.inc b/indy/Core/IdDeprecatedImplBugOn.inc new file mode 100644 index 0000000..43d7f77 --- /dev/null +++ b/indy/Core/IdDeprecatedImplBugOn.inc @@ -0,0 +1,8 @@ +{$IFDEF DEPRECATED_IMPL_BUG} + {$IFDEF HAS_DIRECTIVE_WARN_DEFAULT} + {$WARN SYMBOL_DEPRECATED DEFAULT} + {$ELSE} + {$WARN SYMBOL_DEPRECATED ON} + {$ENDIF} +{$ENDIF} + diff --git a/indy/Core/IdDsnBaseCmpEdt.pas b/indy/Core/IdDsnBaseCmpEdt.pas new file mode 100644 index 0000000..cbafa57 --- /dev/null +++ b/indy/Core/IdDsnBaseCmpEdt.pas @@ -0,0 +1,94 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 9/5/2004 2:08:16 PM JPMugaas + Should work in D9 NET. + + Rev 1.1 2/3/2004 11:42:50 AM JPMugaas + Fixed for new design. + + Rev 1.0 11/13/2002 08:43:16 AM JPMugaas +} + +unit IdDsnBaseCmpEdt; + +{$I IdCompilerDefines.inc} + +interface + +uses + {$IFDEF DOTNET} + Borland.Vcl.Design.DesignIntF, + Borland.Vcl.Design.DesignEditors + {$ELSE} + {$IFDEF FPC} + ComponentEditors + {$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + DesignIntf, + DesignEditors + {$ELSE} + Dsgnintf + {$ENDIF} + {$ENDIF} + {$ENDIF} + ; + +type + {$IFDEF FPC} + TIdBaseComponentEditor = class(TDefaultComponentEditor) + {$ELSE} + TIdBaseComponentEditor = class(TDefaultEditor) + {$ENDIF} + public + procedure ExecuteVerb(Index: Integer); override; + function GetVerb(Index: Integer): string; override; + function GetVerbCount: Integer; override; + end; + +implementation + +uses + IdAbout, + IdGlobal, + IdDsnCoreResourceStrings, + SysUtils; + +{ TIdBaseComponentEditor } + +procedure TIdBaseComponentEditor.ExecuteVerb(Index: Integer); +begin + case Index of + 0 : ShowAboutBox(RSAAboutBoxCompName, gsIdVersion); + end; +end; + +function TIdBaseComponentEditor.GetVerb(Index: Integer): string; +begin + case Index of + 0: Result := IndyFormat(RSAAboutMenuItemName, [gsIdVersion]); + end; +end; + +function TIdBaseComponentEditor.GetVerbCount: Integer; +begin + Result := 1; +end; + +end. + diff --git a/indy/Core/IdDsnCoreResourceStrings.pas b/indy/Core/IdDsnCoreResourceStrings.pas new file mode 100644 index 0000000..888d34c --- /dev/null +++ b/indy/Core/IdDsnCoreResourceStrings.pas @@ -0,0 +1,134 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 1/29/2004 8:54:30 AM JPMugaas + Removed myself from the distribution Team Chairperson entry as I am resigning + from that role. + + Rev 1.4 10/15/2003 10:11:46 PM DSiders + Added resource string for About box credits. + Corrected spelling error in comments. + + Rev 1.3 6/16/2003 12:01:44 PM JPMugaas + Updated copyright to say 2003. + + Rev 1.2 6/8/2003 05:46:58 AM JPMugaas + The kitchen sink has now been implemented. + + Rev 1.1 1/15/2003 08:03:56 AM JPMugaas + Updated with new website address. + + Rev 1.0 11/13/2002 08:43:24 AM JPMugaas +} + +unit IdDsnCoreResourceStrings; + +{ + Note: This unit is for resource strings that are used in the Core Design-Time + package and NOT any design-time packages. This is to prevent design-time + resource strings from being linked into the Run-Time only package. +} + +interface +{$I IdCompilerDefines.inc} + +const + IndyPitCrew = 'Kudzu (Chad Z. Hower)'#13#10 + + 'and the Indy Pit Crew'; + +resourcestring + { About Box stuff } + RSAAboutFormCaption = 'About'; + RSAAboutBoxCompName = 'Internet Direct (Indy)'; + RSAAboutMenuItemName = 'About Internet &Direct (Indy) %s...'; + + RSAAboutBoxVersion = 'Version %s'; + RSAAboutBoxCopyright = 'Copyright (c) 1993 - 2015'#13#10 + + IndyPitCrew; + RSAAboutBoxTitle1 = 'INDY'; + RSAAboutBoxTitle2 = 'Internet Direct'; + RSAAboutBoxLicences = 'Indy Modified BSD License'+#13#10+'Indy MPL License'; + RSAAboutBoxBuiltFor = 'Indy.Sockets (%s)'; + + RSAAboutBoxPleaseVisit = 'For the latest updates and information please visit:'; + RSAAboutBoxIndyWebsite = 'http://www.indyproject.org/'; {Do not Localize} + + RSAAboutCreditsCoordinator = 'Project Coordinator'; + RSAAboutCreditsCoCordinator = 'Project Co-Coordinator'; + RSAAboutCreditsDocumentation = 'Documentation Coordinator'; + RSAAboutCreditsDemos = 'Demos Coordinator'; + RSAAboutCreditsRetiredPast = 'Retired/Past Contributors'; + RSAAboutCreditsIndyCrew = 'The Indy Pit Crew'; + + RSAAboutKitchenSink = IndyPitCrew+#10#13+'present the'#10#13'Kitchen Sink'; + + {Binding Editor stuff} + { + Note to translators - Please Read!!! + + For all the constants except RSBindingFormCaption, there may be an + & symbol before a letter or number. This is rendered as that character being + underlined. In addition, the character after the & symbol along with the ALT + key enables a user to move to that control. Since these are on one form, be + careful to ensure that the same letter or number does not have a & before it + in more than one string, otherwise an ALT key sequence will be broken. + } + RSBindingFormCaption = 'Binding Editor'; + RSBindingNewCaption = '&New'; + RSBindingDeleteCaption = '&Delete'; + //RSBindingAddCaption = '&Add'; + RSBindingRemoveCaption = '&Remove'; + RSBindingLabelBindings = '&Bindings'; + RSBindingHostnameLabel = '&IP Address'; + RSBindingPortLabel = '&Port'; + RSBindingIPVerLabel = 'IP &Version'; + RSBindingIPV4Item = 'IPv&4 (127.0.0.1)'; + RSBindingIPV6Item = 'IPv&6 (::1)'; + {Design time SASLList Hints} + RSADlgSLMoveUp = 'Move Up'; + RSADlgSLMoveDown = 'Move Down'; + RSADlgSLAdd = 'Add'; + RSADlgSLRemove = 'Remove'; + //Caption that uses format with component name + RSADlgSLCaption = 'Editing SASL List for %s'; + RSADlgSLAvailable = '&Available'; + RSADlgSLAssigned = 'A&ssigned (tried in order listed)'; + {Note that the Ampersand indicates an ALT keyboard sequence} + RSADlgSLEditList = 'Edit &List'; + //Display item constants + RSBindingAll = 'All'; //all IP addresses + RSBindingAny = 'Any'; //any port + + {Standard dialog stock strings} + RSOk = 'OK'; + RSCancel = 'Cancel'; + RSHelp = '&Help'; + + // IdRegister + RSRegIndyClients = 'Indy Clients'; + RSRegIndyServers = 'Indy Servers'; + RSRegIndyIntercepts = 'Indy Intercepts'; + RSRegIndyIOHandlers = 'Indy I/O Handlers'; + RSRegIndyMisc = 'Indy Misc'; +{$IFDEF FPC} + CoreSuffix = ' - Core'; +{$ENDIF} + +implementation + +end. diff --git a/indy/Core/IdDsnPropEdBinding.pas b/indy/Core/IdDsnPropEdBinding.pas new file mode 100644 index 0000000..f62dc86 --- /dev/null +++ b/indy/Core/IdDsnPropEdBinding.pas @@ -0,0 +1,143 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 10/26/2004 8:45:26 PM JPMugaas + Should compile. + + Rev 1.8 10/26/2004 8:42:58 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.7 5/19/2004 10:44:28 PM DSiders + Corrected spelling for TIdIPAddress.MakeAddressObject method. + + Rev 1.6 2/3/2004 11:34:26 AM JPMugaas + Should compile. + + Rev 1.5.1.0 2/3/2004 11:32:26 AM JPMugaas + Should compile. + + Rev 1.5 2/1/2004 2:44:20 AM JPMugaas + Bindings editor should be fully functional including IPv6 support. + + Rev 1.4 2/1/2004 1:03:34 AM JPMugaas + This now work properly in both Win32 and DotNET. The behavior had to change + in DotNET because of some missing functionality and because implementing that + functionality creates more problems than it would solve. + + Rev 1.3 2003.12.31 10:42:22 PM czhower + Warning removed + + Rev 1.2 10/15/2003 10:12:32 PM DSiders + Added localization comments. + + Rev 1.1 2003.10.11 5:47:46 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.0 11/13/2002 08:43:58 AM JPMugaas +} + +unit IdDsnPropEdBinding; + +{ + Design Note: It turns out that in DotNET, there are no services file functions and + IdPorts does not work as expected in DotNET. It is probably possible to read the + services file ourselves but that creates some portability problems as the placement + is different in every operating system. + + e.g. + + Linux and Unix-like systems - /etc + Windows 95, 98, and ME - c:\windows + Windows NT systems - c:\winnt\system32\drivers\etc + + Thus, it will undercut whatever benefit we could get with DotNET. + + About the best I could think of is to use an edit control because + we can't offer anything from the services file in DotNET. + + TODO: Maybe there might be a way to find the location in a more elegant + manner than what I described. +} + +interface + +{$I IdCompilerDefines.inc} + +{$IFDEF WIDGET_WINFORMS} +{$R 'IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources' 'IdDsnPropEdBindingNET.resx'} +{$ENDIF} + +uses + Classes, + IdSocketHandle; + +{ +Design Note: It turns out that in DotNET, there are no services file functions and IdPorts +does not work as expected in DotNET. It is probably possible to read the services +file ourselves but that creates some portability problems as the placement is different +in every operating system. + +e.g. + +Linux and Unix-like systems - /etc +Windows 95, 98, and ME - c:\windows +Windows NT systems - c:\winnt\system32\drivers\etc + +Thus, it will undercut whatever benefit we could get with DotNET. + +About the best I could think of is to use an edit control because +we can't offer anything from the services file in DotNET. + +TODO: Maybe there might be a way to find the location in a more eligant +manner than what I described. +} + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; + +implementation + +uses + {$IFDEF WIDGET_WINFORMS} + IdDsnPropEdBindingNET; + {$ELSE} + IdDsnPropEdBindingVCL; + {$ENDIF} + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +begin + {$IFDEF WIDGET_WINFORMS} + IdDsnPropEdBindingNET.FillHandleList(AList,ADest); + {$ELSE} + IdDsnPropEdBindingVCL.FillHandleList(AList,ADest); + {$ENDIF} +end; + +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; +begin + {$IFDEF WIDGET_WINFORMS} + Result := IdDsnPropEdBindingNET.GetListValues(ASocketHandles); + {$ELSE} + Result := IdDsnPropEdBindingVCL.GetListValues(ASocketHandles); + {$ENDIF} +end; + +end. diff --git a/indy/Core/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources b/indy/Core/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources new file mode 100644 index 0000000..61b5d53 Binary files /dev/null and b/indy/Core/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources differ diff --git a/indy/Core/IdDsnPropEdBindingNET.pas b/indy/Core/IdDsnPropEdBindingNET.pas new file mode 100644 index 0000000..9a7d2a4 --- /dev/null +++ b/indy/Core/IdDsnPropEdBindingNET.pas @@ -0,0 +1,702 @@ +unit IdDsnPropEdBindingNET; + +interface + +uses + Classes, + System.Drawing, System.Collections, System.ComponentModel, + System.Windows.Forms, System.Data, IdSocketHandle; + +type + TIdDsnPropEdBindingNET = class(System.Windows.Forms.Form) + {$REGION 'Designer Managed Code'} + strict private + /// + /// Required designer variable. + /// + Components: System.ComponentModel.Container; + btnOk: System.Windows.Forms.Button; + btnCancel: System.Windows.Forms.Button; + lblBindings: System.Windows.Forms.Label; + lbBindings: System.Windows.Forms.ListBox; + btnNew: System.Windows.Forms.Button; + btnDelete: System.Windows.Forms.Button; + lblIPAddress: System.Windows.Forms.Label; + edtIPAddress: System.Windows.Forms.ComboBox; + lblPort: System.Windows.Forms.Label; + edtPort: System.Windows.Forms.NumericUpDown; + cboIPVersion: System.Windows.Forms.ComboBox; + lblIPVersion: System.Windows.Forms.Label; + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + procedure InitializeComponent; + procedure btnNew_Click(sender: System.Object; e: System.EventArgs); + procedure btnDelete_Click(sender: System.Object; e: System.EventArgs); + procedure edtPort_ValueChanged(sender: System.Object; e: System.EventArgs); + procedure edtIPAddress_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + procedure cboIPVersion_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + procedure lbBindings_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + {$ENDREGION} + strict protected + /// + /// Clean up any resources being used. + /// + procedure Dispose(Disposing: Boolean); override; + private + FHandles : TIdSocketHandles; + FDefaultPort : Integer; + FIPv4Addresses : TStrings; + FIPv6Addresses : TStrings; + FCurrentHandle : TIdSocketHandle; + + { Private Declarations } + procedure SetHandles(const Value: TIdSocketHandles); + procedure SetIPv4Addresses(const Value: TStrings); + procedure SetIPv6Addresses(const Value: TStrings); + procedure UpdateBindingList; + procedure UpdateEditControls; + procedure FillComboBox(ACombo : System.Windows.Forms.ComboBox; AStrings :TStrings); + procedure SetCaption(const AValue : String); + function GetCaption : String; + public + constructor Create; + function Execute : Boolean; + function GetList: string; + procedure SetList(const AList: string); + property Handles : TIdSocketHandles read FHandles write SetHandles; + property DefaultPort : Integer read FDefaultPort write FDefaultPort; + property IPv4Addresses : TStrings read FIPv4Addresses write SetIPv4Addresses; + property IPv6Addresses : TStrings read FIPv6Addresses write SetIPv6Addresses; + property Caption : String read GetCaption write SetCaption; + end; + + [assembly: RuntimeRequiredAttribute(TypeOf(TIdDsnPropEdBindingNET))] + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; + +implementation +uses + IdGlobal, + IdIPAddress, + IdDsnCoreResourceStrings, IdStack, SysUtils; + +const + IPv6Wildcard1 = '::'; {do not localize} + IPv6Wildcard2 = '0:0:0:0:0:0:0:0'; {do not localize} + IPv6Loopback = '::1'; {do not localize} + IPv4Wildcard = '0.0.0.0'; {do not localize} + IPv4Loopback = '127.0.0.1'; {do not localize} + +function IsValidIP(const AAddr : String): Boolean; +var + LIP: TIdIPAddress; +begin + LIP := TIdIPAddress.MakeAddressObject(AAddr); + Result := Assigned(LIP); + if Result then + begin + FreeAndNil(LIP); + end; +end; + +function StripAndSymbol(s : String) : String; +begin + Result := ''; + repeat + if s='' then + begin + Break; + end; + Result := Result + Fetch(s,'&'); + until False; +end; + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +var + LItems: TStringList; + i: integer; + LIPVersion: TIdIPVersion; + LAddr, LText: string; + LPort: integer; +begin + ADest.Clear; + LItems := TStringList.Create; + try + LItems.CommaText := AList; + for i := 0 to LItems.Count-1 do begin + if Length(LItems[i]) > 0 then begin + if TextStartsWith(LItems[i], '[') then begin + // ipv6 + LIPVersion := Id_IPv6; + LText := Copy(LItems[i], 2, MaxInt); + LAddr := Fetch(LText, ']:'); + LPort := IndyStrToInt(LText, -1); + end else begin + // ipv4 + LIPVersion := Id_IPv4; + LText := LItems[i]; + LAddr := Fetch(LText, ':'); + LPort := IndyStrToInt(LText, -1); + //Note that 0 is legal and indicates the server binds to a random port + end; + if IsValidIP(LAddr) and (LPort > -1) and (LPort < 65536) then begin + with ADest.Add do begin + IPVersion := LIPVersion; + IP := LAddr; + Port := LPort; + end; + end; + end; + end; + finally + LItems.Free; + end; +end; + +function NumericOnly(const AText : String) : String; +var + i: Integer; +begin + Result := ''; + for i := 1 to Length(AText) do + begin + if IsNumeric(AText[i]) then + begin + Result := Result + AText[i]; + end + else + begin + Break; + end; + end; + if (Length(Result) = 0) then + begin + Result := '0'; + end; +end; + +function IndexOfNo(const ANo : Integer; AItems : System.Windows.Forms.ComboBox.ObjectCollection) : Integer; +begin + for Result := 0 to AItems.Count -1 do + begin + if ANo = IndyStrToInt( NumericOnly(AItems[Result].ToString )) then + begin + Exit; + end; + end; + Result := -1; +end; + +function GetDisplayString(ASocketHandle: TIdSocketHandle): string; +begin + Result := ''; + case ASocketHandle.IPVersion of + Id_IPv4 : Result := IndyFormat('%s:%d', [ASocketHandle.IP, ASocketHandle.Port]); + Id_IPv6 : Result := IndyFormat('[%s]:%d', [ASocketHandle.IP, ASocketHandle.Port]); + end; +end; + +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; +var + i: Integer; +begin + Result := ''; + for i := 0 to ASocketHandles.Count -1 do begin + Result := Result + ',' + GetDisplayString(ASocketHandles[i]); + end; + Delete(Result,1,1); +end; + +{$AUTOBOX ON} + +{$REGION 'Windows Form Designer generated code'} +/// +/// Required method for Designer support -- do not modify +/// the contents of this method with the code editor. +/// +procedure TIdDsnPropEdBindingNET.InitializeComponent; +type + TArrayOfInteger = array of Integer; +begin + Self.btnOk := System.Windows.Forms.Button.Create; + Self.btnCancel := System.Windows.Forms.Button.Create; + Self.lblBindings := System.Windows.Forms.Label.Create; + Self.lbBindings := System.Windows.Forms.ListBox.Create; + Self.btnNew := System.Windows.Forms.Button.Create; + Self.btnDelete := System.Windows.Forms.Button.Create; + Self.lblIPAddress := System.Windows.Forms.Label.Create; + Self.edtIPAddress := System.Windows.Forms.ComboBox.Create; + Self.lblPort := System.Windows.Forms.Label.Create; + Self.edtPort := System.Windows.Forms.NumericUpDown.Create; + Self.cboIPVersion := System.Windows.Forms.ComboBox.Create; + Self.lblIPVersion := System.Windows.Forms.Label.Create; + (System.ComponentModel.ISupportInitialize(Self.edtPort)).BeginInit; + Self.SuspendLayout; + // + // btnOk + // + Self.btnOk.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnOk.DialogResult := System.Windows.Forms.DialogResult.OK; + Self.btnOk.Location := System.Drawing.Point.Create(312, 160); + Self.btnOk.Name := 'btnOk'; + Self.btnOk.TabIndex := 0; + // + // btnCancel + // + Self.btnCancel.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnCancel.DialogResult := System.Windows.Forms.DialogResult.Cancel; + Self.btnCancel.Location := System.Drawing.Point.Create(392, 160); + Self.btnCancel.Name := 'btnCancel'; + Self.btnCancel.TabIndex := 1; + // + // lblBindings + // + Self.lblBindings.AutoSize := True; + Self.lblBindings.Location := System.Drawing.Point.Create(8, 8); + Self.lblBindings.Name := 'lblBindings'; + Self.lblBindings.Size := System.Drawing.Size.Create(42, 16); + Self.lblBindings.TabIndex := 2; + Self.lblBindings.Text := '&Binding'; + // + // lbBindings + // + Self.lbBindings.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Bottom) or System.Windows.Forms.AnchorStyles.Left))); + Self.lbBindings.Location := System.Drawing.Point.Create(8, 24); + Self.lbBindings.Name := 'lbBindings'; + Self.lbBindings.Size := System.Drawing.Size.Create(137, 121); + Self.lbBindings.TabIndex := 3; + Include(Self.lbBindings.SelectedValueChanged, Self.lbBindings_SelectedValueChanged); + // + // btnNew + // + Self.btnNew.Location := System.Drawing.Point.Create(152, 56); + Self.btnNew.Name := 'btnNew'; + Self.btnNew.TabIndex := 4; + Include(Self.btnNew.Click, Self.btnNew_Click); + // + // btnDelete + // + Self.btnDelete.Location := System.Drawing.Point.Create(152, 88); + Self.btnDelete.Name := 'btnDelete'; + Self.btnDelete.TabIndex := 5; + Include(Self.btnDelete.Click, Self.btnDelete_Click); + // + // lblIPAddress + // + Self.lblIPAddress.Location := System.Drawing.Point.Create(240, 8); + Self.lblIPAddress.Name := 'lblIPAddress'; + Self.lblIPAddress.Size := System.Drawing.Size.Create(100, 16); + Self.lblIPAddress.TabIndex := 6; + Self.lblIPAddress.Text := 'Label1'; + // + // edtIPAddress + // + Self.edtIPAddress.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.edtIPAddress.Location := System.Drawing.Point.Create(240, 24); + Self.edtIPAddress.Name := 'edtIPAddress'; + Self.edtIPAddress.Size := System.Drawing.Size.Create(224, 21); + Self.edtIPAddress.TabIndex := 7; + Include(Self.edtIPAddress.SelectedValueChanged, Self.edtIPAddress_SelectedValueChanged); + // + // lblPort + // + Self.lblPort.Location := System.Drawing.Point.Create(240, 58); + Self.lblPort.Name := 'lblPort'; + Self.lblPort.Size := System.Drawing.Size.Create(100, 16); + Self.lblPort.TabIndex := 8; + Self.lblPort.Text := 'Label1'; + // + // edtPort + // + Self.edtPort.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.edtPort.Location := System.Drawing.Point.Create(240, 74); + Self.edtPort.Maximum := System.Decimal.Create(TArrayOfInteger.Create(65535, + 0, 0, 0)); + Self.edtPort.Name := 'edtPort'; + Self.edtPort.Size := System.Drawing.Size.Create(224, 20); + Self.edtPort.TabIndex := 9; + Include(Self.edtPort.ValueChanged, Self.edtPort_ValueChanged); + // + // cboIPVersion + // + Self.cboIPVersion.DropDownStyle := System.Windows.Forms.ComboBoxStyle.DropDownList; + Self.cboIPVersion.Location := System.Drawing.Point.Create(240, 124); + Self.cboIPVersion.Name := 'cboIPVersion'; + Self.cboIPVersion.Size := System.Drawing.Size.Create(224, 21); + Self.cboIPVersion.TabIndex := 10; + Include(Self.cboIPVersion.SelectedValueChanged, Self.cboIPVersion_SelectedValueChanged); + // + // lblIPVersion + // + Self.lblIPVersion.Location := System.Drawing.Point.Create(240, 108); + Self.lblIPVersion.Name := 'lblIPVersion'; + Self.lblIPVersion.Size := System.Drawing.Size.Create(100, 16); + Self.lblIPVersion.TabIndex := 11; + Self.lblIPVersion.Text := 'Label1'; + // + // TIdDsnPropEdBindingNET + // + Self.AcceptButton := Self.btnOk; + Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); + Self.CancelButton := Self.btnCancel; + Self.ClientSize := System.Drawing.Size.Create(470, 189); + Self.Controls.Add(Self.lblIPVersion); + Self.Controls.Add(Self.cboIPVersion); + Self.Controls.Add(Self.edtPort); + Self.Controls.Add(Self.lblPort); + Self.Controls.Add(Self.edtIPAddress); + Self.Controls.Add(Self.lblIPAddress); + Self.Controls.Add(Self.btnDelete); + Self.Controls.Add(Self.btnNew); + Self.Controls.Add(Self.lbBindings); + Self.Controls.Add(Self.lblBindings); + Self.Controls.Add(Self.btnCancel); + Self.Controls.Add(Self.btnOk); + Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; + Self.MaximizeBox := False; + Self.MaximumSize := System.Drawing.Size.Create(480, 225); + Self.MinimizeBox := False; + Self.MinimumSize := System.Drawing.Size.Create(480, 225); + Self.Name := 'TIdDsnPropEdBindingNET'; + Self.ShowInTaskbar := False; + Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + Self.Text := 'WinForm'; + (System.ComponentModel.ISupportInitialize(Self.edtPort)).EndInit; + Self.ResumeLayout(False); +end; +{$ENDREGION} + +procedure TIdDsnPropEdBindingNET.Dispose(Disposing: Boolean); +begin + if Disposing then + begin + if Components <> nil then + begin + Components.Dispose(); + FreeAndNil(FHandles); + + FreeAndNil(FIPv4Addresses); + FreeAndNil(FIPv6Addresses); + + //don't free FCurrentHandle; - it's in the handles collection + TIdStack.DecUsage; + end; + end; + inherited Dispose(Disposing); + +end; + +constructor TIdDsnPropEdBindingNET.Create; +var + i: Integer; + LLocalAddresses: TIdStackLocalAddressList; +begin + inherited Create; + // + // Required for Windows Form Designer support + // + InitializeComponent; + // + // TODO: Add any constructor code after InitializeComponent call + // + FHandles := TIdSocketHandles.Create(nil); + FIPv4Addresses := TStringList.Create; + FIPv6Addresses := TStringList.Create; + SetIPv4Addresses(nil); + SetIPv6Addresses(nil); + + TIdStack.IncUsage; + try + LLocalAddresses := TIdStackLocalAddressList.Create; + try + GStack.GetLocalAddressList(LLocalAddresses); + for i := 0 to LLocalAddresses.Count-1 do + begin + case LLocalAddresses[i].IPVersion of + Id_IPv4: FIPv4Addresses.Add(LLocalAddresses[i].IPAddress); + Id_IPv6: FIPv6Addresses.Add(LLocalAddresses[i].IPAddress); + end; + end; + finally + LLocalAddresses.Free; + end; + finally + TIdStack.DecUsage; + end; + + UpdateEditControls; + //captions + btnNew.Text := RSBindingNewCaption; + btnDelete.Text := RSBindingDeleteCaption; + lblIPAddress.Text := RSBindingHostnameLabel; + lblPort.Text := RSBindingPortLabel; + lblIPVersion.Text := RSBindingIPVerLabel; + btnOk.Text := RSOk; + btnCancel.Text := RSCancel; + //IPVersion choices + //we yhave to strip out the & symbol. In Win32, we use this + //in a radio-box so a user could select by pressingg the 4 or 6 + //key. For this, we don't have a radio box and I'm too lazy + //to use two Radio Buttons. + cboIPVersion.Items.Add(StripAndSymbol(RSBindingIPV4Item)); + cboIPVersion.Items.Add(StripAndSymbol(RSBindingIPV6Item)); +end; + +procedure TIdDsnPropEdBindingNET.SetHandles(const Value: TIdSocketHandles); +begin + FHandles.Assign(Value); + UpdateBindingList; +end; + +function TIdDsnPropEdBindingNET.GetList: string; +begin + Result := GetListValues(Handles); +end; + +procedure TIdDsnPropEdBindingNET.SetIPv6Addresses(const Value: TStrings); +begin + if Assigned(Value) then begin + FIPv6Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv6Addresses.IndexOf(IPv6Loopback) = -1 then begin + FIPv6Addresses.Insert(0, IPv6Loopback); + end; + if FIPv6Addresses.IndexOf(IPv6Wildcard1) = -1 then begin + FIPv6Addresses.Insert(0, IPv6Wildcard1); + end; +end; + +procedure TIdDsnPropEdBindingNET.SetIPv4Addresses(const Value: TStrings); +begin + if Assigned(Value) then begin + FIPv4Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv4Addresses.IndexOf(IPv6Loopback) = -1 then begin + FIPv4Addresses.Insert(0, IPv4Loopback); + end; + if FIPv4Addresses.IndexOf(IPv4Wildcard) = -1 then begin + FIPv4Addresses.Insert(0, IPv4Wildcard); + end; +end; + +procedure TIdDsnPropEdBindingNET.SetList(const AList: string); +begin + FCurrentHandle := nil; + FillHandleList(AList, Handles); + UpdateBindingList; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.lbBindings_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + if lbBindings.SelectedIndex >= 0 then begin + btnDelete.Enabled := True; + FCurrentHandle := FHandles[lbBindings.SelectedIndex]; + end else begin + btnDelete.Enabled := False; + FCurrentHandle := nil; + end; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.cboIPVersion_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + case cboIPVersion.SelectedIndex of + 0 : + begin + if FCurrentHandle.IPVersion <> Id_IPv4 then + begin + FCurrentHandle.IPVersion := Id_IPv4; + FillComboBox(edtIPAddress,FIPv4Addresses); + FCurrentHandle.IP := IPv4Wildcard; + end; + end; + 1 : + begin + if FCurrentHandle.IPVersion <> Id_IPv6 then + begin + FCurrentHandle.IPVersion := Id_IPv6; + FillComboBox(edtIPAddress,FIPv6Addresses); + FCurrentHandle.IP := IPv6Wildcard1; + end; + end; + end; + UpdateEditControls; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.edtIPAddress_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + FCurrentHandle.IP := edtIPAddress.SelectedItem.ToString; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.edtPort_ValueChanged(sender: System.Object; + e: System.EventArgs); +begin + if Assigned(FCurrentHandle) then begin + FCurrentHandle.Port := edtPort.Value.ToInt16(edtPort.Value); + end; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.btnDelete_Click(sender: System.Object; e: System.EventArgs); +var LSH : TIdSocketHandle; + i : Integer; +begin + if lbBindings.SelectedIndex >= 0 then + begin + // Delete is not available in D4's collection classes + // This should work just as well. + i := lbBindings.get_SelectedIndex; + LSH := Handles[i]; + FreeAndNil(LSH); + lbBindings.Items.Remove(i); + FCurrentHandle := nil; + UpdateBindingList; + end; + lbBindings_SelectedValueChanged(nil, nil); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.btnNew_Click(sender: System.Object; e: System.EventArgs); +begin + FCurrentHandle := FHandles.Add; + FCurrentHandle.IP := IPv4Wildcard; + FCurrentHandle.Port := FDefaultPort; + UpdateBindingList; + FillComboBox(edtIPAddress, FIPv4Addresses); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.UpdateBindingList; +var + i: integer; + selected: integer; + s: string; +begin + selected := lbBindings.SelectedIndex; + lbBindings.BeginUpdate; + try + if lbBindings.Items.Count = FHandles.Count then begin + for i := 0 to FHandles.Count - 1 do begin + s := GetDisplayString(FHandles[i]); + if s <> lbBindings.Items[i].ToString then begin + lbBindings.Items[i] := s; + end; + end; + end else begin + lbBindings.Items.Clear; + for i := 0 to FHandles.Count-1 do begin + lbBindings.Items.Add(GetDisplayString(FHandles[i])); + end; + end; + finally + lbBindings.EndUpdate; + if Assigned(FCurrentHandle) then begin + lbBindings.SelectedIndex := FCurrentHandle.Index; + end else begin + lbBindings.SelectedIndex := IndyMin(selected, lbBindings.Items.Count-1); + end; + end; +{ selected := lbBindings.SelectedItem; + lbBindings.Items.BeginUpdate; + try + if lbBindings.Items.Count = FHandles.Count then begin + for i := 0 to FHandles.Count - 1 do begin + s := GetDisplayString(FHandles[i]); + if s <> lbBindings.Items[i] then begin + lbBindings.Items[i] := s; + end; + end; + end else begin + lbBindings.Items.Clear; + for i := 0 to FHandles.Count-1 do begin + lbBindings.Items.Add(GetDisplayString(FHandles[i])); + end; + end; + finally + lbBindings.Items.EndUpdate; + if Assigned(FCurrentHandle) then begin + lbBindings.ItemIndex := FCurrentHandle.Index; + end else begin + lbBindings.ItemIndex := IndyMin(selected, lbBindings.Items.Count-1); + end; + end; } +end; + +procedure TIdDsnPropEdBindingNET.UpdateEditControls; +begin + if Assigned(FCurrentHandle) then + begin + edtPort.Text := ''; + edtPort.Value := FCurrentHandle.Port; + case FCurrentHandle.IPVersion of + Id_IPv4 : + begin + FillComboBox(edtIPAddress, FIPv4Addresses); + edtIPAddress.SelectedItem := edtIPAddress.Items[0]; + cboIPVersion.SelectedItem := cboIPVersion.Items[0]; + end; + Id_IPv6 : + begin + FillComboBox(edtIPAddress, FIPv6Addresses); + edtIPAddress.SelectedItem := edtIPAddress.Items[0]; + cboIPVersion.SelectedItem := cboIPVersion.Items[1]; + end; + end; + if edtIPAddress.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown then begin + edtIPAddress.Text := FCurrentHandle.IP; + end else begin + edtIPAddress.SelectedIndex := edtIPAddress.Items.IndexOf(FCurrentHandle.IP); + end; + end; + + lblIPAddress.Enabled := Assigned(FCurrentHandle); + edtIPAddress.Enabled := Assigned(FCurrentHandle); + lblPort.Enabled := Assigned(FCurrentHandle); + edtPort.Enabled := Assigned(FCurrentHandle); + lblIPVersion.Enabled := Assigned(FCurrentHandle); + cboIPVersion.Enabled := Assigned(FCurrentHandle); +end; + +procedure TIdDsnPropEdBindingNET.FillComboBox( + ACombo: System.Windows.Forms.ComboBox; AStrings: TStrings); +var + i : Integer; +begin + ACombo.Items.Clear; + for i := 0 to AStrings.Count-1 do begin + ACombo.Items.Add(AStrings[i]); + end; +end; + +function TIdDsnPropEdBindingNET.Execute: Boolean; +begin + Result := Self.ShowDialog = System.Windows.Forms.DialogResult.OK; +end; + +function TIdDsnPropEdBindingNET.GetCaption: String; +begin + Result := Text; +end; + +procedure TIdDsnPropEdBindingNET.SetCaption(const AValue: String); +begin + Text := AValue; +end; + +end. diff --git a/indy/Core/IdDsnPropEdBindingNET.resx b/indy/Core/IdDsnPropEdBindingNET.resx new file mode 100644 index 0000000..9d6389a --- /dev/null +++ b/indy/Core/IdDsnPropEdBindingNET.resx @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + diff --git a/indy/Core/IdDsnPropEdBindingVCL.pas b/indy/Core/IdDsnPropEdBindingVCL.pas new file mode 100644 index 0000000..ed4c19a --- /dev/null +++ b/indy/Core/IdDsnPropEdBindingVCL.pas @@ -0,0 +1,819 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 10/26/2004 8:45:26 PM JPMugaas + Should compile. + + Rev 1.8 10/26/2004 8:42:58 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.7 5/19/2004 10:44:28 PM DSiders + Corrected spelling for TIdIPAddress.MakeAddressObject method. + + Rev 1.6 2/3/2004 11:34:26 AM JPMugaas + Should compile. + + Rev 1.5.1.0 2/3/2004 11:32:26 AM JPMugaas + Should compile. + + Rev 1.5 2/1/2004 2:44:20 AM JPMugaas + Bindings editor should be fully functional including IPv6 support. + + Rev 1.4 2/1/2004 1:03:34 AM JPMugaas + This now work properly in both Win32 and DotNET. The behavior had to change + in DotNET because of some missing functionality and because implementing that + functionality creates more problems than it would solve. + + Rev 1.3 2003.12.31 10:42:22 PM czhower + Warning removed + + Rev 1.2 10/15/2003 10:12:32 PM DSiders + Added localization comments. + + Rev 1.1 2003.10.11 5:47:46 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.0 11/13/2002 08:43:58 AM JPMugaas +} + +unit IdDsnPropEdBindingVCL; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, +{$IFDEF WIDGET_KYLIX} + QActnList, QStdCtrls, QForms, QExtCtrls, QControls, QComCtrls, QGraphics, Qt, +{$ENDIF} +{$IFDEF WIDGET_VCL_LIKE} + ActnList, StdCtrls, Buttons, ExtCtrls, Graphics, Controls, ComCtrls, Forms, Dialogs, +{$ENDIF} +{$IFDEF HAS_UNIT_Types} + Types, +{$ENDIF} +{$IFDEF WINDOWS} + Windows, +{$ENDIF} +{$IFDEF LCL} + LResources, +{$ENDIF} + IdSocketHandle; +{ + Design Note: It turns out that in DotNET, there are no services file functions and + IdPorts does not work as expected in DotNET. It is probably possible to read the + services file ourselves but that creates some portability problems as the placement + is different in every operating system. + + e.g. + + Linux and Unix-like systems - /etc + Windows 95, 98, and ME - c:\windows + Windows NT systems - c:\winnt\system32\drivers\etc + + Thus, it will undercut whatever benefit we could get with DotNET. + + About the best I could think of is to use an edit control because + we can't offer anything from the services file in DotNET. + + TODO: Maybe there might be a way to find the location in a more elegant + manner than what I described. +} + +type + TIdDsnPropEdBindingVCL = class(TForm) + {$IFDEF USE_TBitBtn} + btnOk: TBitBtn; + btnCancel: TBitBtn; + {$ELSE} + btnOk: TButton; + btnCancel: TButton; + {$ENDIF} + lblBindings: TLabel; + edtPort: TComboBox; + rdoBindingType: TRadioGroup; + lblIPAddress: TLabel; + lblPort: TLabel; + btnNew: TButton; + btnDelete: TButton; + ActionList1: TActionList; + btnBindingsNew: TAction; + btnBindingsDelete: TAction; + edtIPAddress: TComboBox; + lbBindings: TListBox; + procedure btnBindingsNewExecute(Sender: TObject); + procedure btnBindingsDeleteExecute(Sender: TObject); + procedure btnBindingsDeleteUpdate(Sender: TObject); + procedure edtPortKeyPress(Sender: TObject; var Key: Char); + procedure edtIPAddressChange(Sender: TObject); + procedure edtPortChange(Sender: TObject); + procedure rdoBindingTypeClick(Sender: TObject); + procedure lbBindingsClick(Sender: TObject); + private + procedure SetHandles(const Value: TIdSocketHandles); + procedure SetIPv4Addresses(const Value: TStrings); + procedure SetIPv6Addresses(const Value: TStrings); + procedure UpdateBindingList; + protected + FInUpdateRoutine : Boolean; + FHandles : TIdSocketHandles; + FDefaultPort : Integer; + FIPv4Addresses : TStrings; + FIPv6Addresses : TStrings; + fCreatedStack : Boolean; + FCurrentHandle : TIdSocketHandle; + procedure UpdateEditControls; + function PortDescription(const PortNumber: integer): string; + public + Constructor Create(AOwner : TComponent); overload; override; + constructor Create; reintroduce; overload; + Destructor Destroy; override; + function Execute : Boolean; + function GetList: string; + procedure SetList(const AList: string); + property Handles : TIdSocketHandles read FHandles write SetHandles; + property DefaultPort : Integer read FDefaultPort write FDefaultPort; + property IPv4Addresses : TStrings read FIPv4Addresses write SetIPv4Addresses; + property IPv6Addresses : TStrings read FIPv6Addresses write SetIPv6Addresses; + end; + +var + IdPropEdBindingEntry: TIdDsnPropEdBindingVCL; + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; + +implementation + +uses + IdGlobal, + IdIPAddress, + IdDsnCoreResourceStrings, + IdStack, + IdStackBSDBase, + SysUtils; + +const + IPv6Wildcard1 = '::'; {do not localize} + {CH IPv6Wildcard2 = '0:0:0:0:0:0:0:0'; } {do not localize} + IPv6Loopback = '::1'; {do not localize} + IPv4Wildcard = '0.0.0.0'; {do not localize} + IPv4Loopback = '127.0.0.1'; {do not localize} + +function IsValidIP(const AAddr : String): Boolean; +var + LIP : TIdIPAddress; +begin + LIP := TIdIPAddress.MakeAddressObject(AAddr); + Result := Assigned(LIP); + if Result then begin + FreeAndNil(LIP); + end; +end; + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +var + LItems: TStringList; + i: integer; + LIPVersion: TIdIPVersion; + LAddr, LText: string; + LPort: integer; + LSocket: TIdSocketHandle; +begin + ADest.Clear; + LItems := TStringList.Create; + try + LItems.CommaText := AList; + for i := 0 to LItems.Count-1 do begin + if Length(LItems[i]) > 0 then begin + if TextStartsWith(LItems[i], '[') then begin + // ipv6 + LIPVersion := Id_IPv6; + LText := Copy(LItems[i], 2, MaxInt); + LAddr := Fetch(LText, ']:'); + LPort := StrToIntDef(LText, -1); + end else begin + // ipv4 + LIPVersion := Id_IPv4; + LText := LItems[i]; + LAddr := Fetch(LText, ':'); + LPort := StrToIntDef(LText, -1); + //Note that 0 is legal and indicates the server binds to a random port + end; + if IsValidIP(LAddr) and (LPort > -1) and (LPort < 65536) then begin + LSocket := ADest.Add; + LSocket.IPVersion := LIPVersion; + LSocket.IP := LAddr; + LSocket.Port := LPort; + end; + end; + end; + finally + LItems.Free; + end; +end; + +{ TIdDsnPropEdBindingVCL } + +function NumericOnly(const AText : String) : String; +var + i : Integer; +begin + Result := ''; + for i := 1 to Length(AText) do + begin + if IsNumeric(AText[i]) then begin + Result := Result + AText[i]; + end else begin + Break; + end; + end; + if Length(Result) = 0 then begin + Result := '0'; + end; +end; + +function IndexOfNo(const ANo : Integer; AStrings : TStrings) : Integer; +begin + for Result := 0 to AStrings.Count-1 do + begin + if ANo = IndyStrToInt(NumericOnly(AStrings[Result])) then begin + Exit; + end; + end; + Result := -1; +end; + +function GetDisplayString(ASocketHandle: TIdSocketHandle): string; +begin + Result := ''; + case ASocketHandle.IPVersion of + Id_IPv4 : Result := Format('%s:%d',[ASocketHandle.IP, ASocketHandle.Port]); + Id_IPv6 : Result := Format('[%s]:%d',[ASocketHandle.IP, ASocketHandle.Port]); + end; +end; + +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; +var i : Integer; +begin + Result := ''; + for i := 0 to ASocketHandles.Count -1 do begin + Result := Result + ',' + GetDisplayString(ASocketHandles[i]); + end; + Delete(Result,1,1); +end; + +constructor TIdDsnPropEdBindingVCL.Create(AOwner: TComponent); +var + i : Integer; + LLocalAddresses: TIdStackLocalAddressList; +begin + inherited CreateNew(AOwner, 0); + {$IFNDEF WIDGET_KYLIX} + Borderstyle := bsDialog; + {$ENDIF} + BorderIcons := [biSystemMenu]; + // Width := 480; + // Height := 252; + ClientWidth := 472; + {$IFDEF USE_TBitBtn} + ClientHeight := 230; + {$ELSE} + ClientHeight := 225; + {$ENDIF} + Constraints.MaxWidth := Width; + Constraints.MaxHeight := Height; + Constraints.MinWidth := Width; + Constraints.MinHeight := Height; + Position := poScreenCenter; + lblBindings := TLabel.Create(Self); + lbBindings := TListBox.Create(Self); + ActionList1 := TActionList.Create(Self); + btnBindingsNew := TAction.Create(Self); + btnBindingsDelete := TAction.Create(Self); + btnNew := TButton.Create(Self); + btnDelete := TButton.Create(Self); + lblIPAddress := TLabel.Create(Self); + edtIPAddress := TComboBox.Create(Self); + lblPort := TLabel.Create(Self); + + edtPort := TComboBox.Create(Self); + rdoBindingType := TRadioGroup.Create(Self); + + {$IFDEF USE_TBitBtn} + btnOk := TBitBtn.Create(Self); + btnCancel := TBitBtn.Create(Self); + {$ELSE} + btnOk := TButton.Create(Self); + btnCancel := TButton.Create(Self); + {$ENDIF} + + lblBindings.Name := 'lblBindings'; {do not localize} + lblBindings.Parent := Self; + lblBindings.Left := 8; + lblBindings.Top := 8; + lblBindings.Width := 35; + lblBindings.Height := 13; + lblBindings.Caption := '&Binding'; {do not localize} + + lbBindings.Name := 'lbBindings'; {do not localize} + lbBindings.Parent := Self; + lbBindings.Left := 8; + lbBindings.Top := 24; + lbBindings.Width := 137; + lbBindings.Height := 161; + lbBindings.ItemHeight := 13; + lbBindings.TabOrder := 8; + lbBindings.OnClick := lbBindingsClick; + + ActionList1.Name := 'ActionList1'; {do not localize} + { + ActionList1.Left := 152; + ActionList1.Top := 32; + } + + btnBindingsNew.Name := 'btnBindingsNew'; {do not localize} + btnBindingsNew.Caption := RSBindingNewCaption; + btnBindingsNew.OnExecute := btnBindingsNewExecute; + + btnBindingsDelete.Name := 'btnBindingsDelete'; {do not localize} + btnBindingsDelete.Caption := RSBindingDeleteCaption; + btnBindingsDelete.OnExecute := btnBindingsDeleteExecute; + btnBindingsDelete.OnUpdate := btnBindingsDeleteUpdate; + + btnNew.Name := 'btnNew'; {do not localize} + btnNew.Parent := Self; + btnNew.Left := 152; + btnNew.Top := 72; + btnNew.Width := 75; + btnNew.Height := 25; + btnNew.Action := btnBindingsNew; + btnNew.TabOrder := 6; + + btnDelete.Name := 'btnDelete'; {do not localize} + btnDelete.Parent := Self; + btnDelete.Left := 152; + btnDelete.Top := 104; + btnDelete.Width := 75; + btnDelete.Height := 25; + btnDelete.Action := btnBindingsDelete; + btnDelete.TabOrder := 7; + + lblIPAddress.Name := 'lblIPAddress'; {do not localize} + lblIPAddress.Parent := Self; + lblIPAddress.Left := 240; + lblIPAddress.Top := 8; + lblIPAddress.Width := 54; + lblIPAddress.Height := 13; + lblIPAddress.Caption := RSBindingHostnameLabel; + lblIPAddress.Enabled := False; + + edtIPAddress.Name := 'edtIPAddress'; {do not localize} + edtIPAddress.Parent := Self; + edtIPAddress.Left := 240; + edtIPAddress.Top := 24; + edtIPAddress.Width := 221; + edtIPAddress.Height := 21; + edtIPAddress.Enabled := False; + edtIPAddress.ItemHeight := 13; + edtIPAddress.TabOrder := 3; + edtIPAddress.OnChange := edtIPAddressChange; + + lblPort.Name := 'lblPort'; {do not localize} + lblPort.Parent := Self; + lblPort.Left := 240; + lblPort.Top := 56; + lblPort.Width := 22; + lblPort.Height := 13; + lblPort.Caption := RSBindingPortLabel; + lblPort.Enabled := False; + lblPort.FocusControl := edtPort; + + edtPort.Name := 'edtPort'; {do not localize} + edtPort.Parent := Self; + edtPort.Left := 240; + edtPort.Top := 72; + edtPort.Width := 221; + edtPort.Height := 21; + edtPort.Enabled := False; + edtPort.ItemHeight := 13; + edtPort.TabOrder := 4; + edtPort.OnChange := edtPortChange; + edtPort.OnKeyPress := edtPortKeyPress; + + rdoBindingType.Name := 'rdoBindingType'; {do not localize} + rdoBindingType.Parent := Self; + rdoBindingType.Left := 240; + rdoBindingType.Top := 120; + rdoBindingType.Width := 221; + rdoBindingType.Height := 65; + rdoBindingType.Caption := RSBindingIPVerLabel; + rdoBindingType.Enabled := False; + rdoBindingType.Items.Add(RSBindingIPV4Item); + rdoBindingType.Items.Add(RSBindingIPV6Item); + rdoBindingType.TabOrder := 5; + rdoBindingType.OnClick := rdoBindingTypeClick; + + btnOk.Name := 'btnOk'; {do not localize} + btnOk.Parent := Self; + btnOk.Anchors := [akRight, akBottom]; + btnOk.Left := 306; + btnOk.Top := 193; + btnOk.Width := 75; + {$IFDEF USE_TBitBtn} + btnOk.Height := 30; + btnOk.Kind := bkOk; + {$ELSE} + btnOk.Height := 25; + btnOk.Caption := RSOk; + btnOk.Default := True; + btnOk.ModalResult := 1; + {$ENDIF} + btnOk.TabOrder := 0; + + btnCancel.Name := 'btnCancel'; {do not localize} + btnCancel.Parent := Self; + btnCancel.Anchors := [akRight, akBottom]; + btnCancel.Left := 386; + btnCancel.Top := 193; + btnCancel.Width := 75; + {$IFDEF USE_TBitBtn} + btnCancel.Height := 30; + btnCancel.Kind := bkCancel; + {$ELSE} + btnCancel.Height := 25; + btnCancel.Cancel := True; + btnCancel.Caption := RSCancel; + btnCancel.ModalResult := 2; + {$ENDIF} + btnCancel.Anchors := [akRight, akBottom]; + btnCancel.TabOrder := 1; + + FHandles := TIdSocketHandles.Create(nil); + FIPv4Addresses := TStringList.Create; + FIPv6Addresses := TStringList.Create; + SetIPv4Addresses(nil); + SetIPv6Addresses(nil); + + TIdStack.IncUsage; + try + LLocalAddresses := TIdStackLocalAddressList.Create; + try + GStack.GetLocalAddressList(LLocalAddresses); + for i := 0 to LLocalAddresses.Count-1 do + begin + case LLocalAddresses[i].IPVersion of + Id_IPv4: FIPv4Addresses.Add(LLocalAddresses[i].IPAddress); + Id_IPv6: FIPv6Addresses.Add(LLocalAddresses[i].IPAddress); + end; + end; + finally + LLocalAddresses.Free; + end; + finally + TIdStack.DecUsage; + end; + + edtPort.Items.BeginUpdate; + try + edtPort.Items.Add(PortDescription(0)); + for i := 0 to IdPorts.Count - 1 do begin + edtPort.Items.Add( + PortDescription( + {$IFDEF HAS_GENERICS_TList} + IdPorts[i] + {$ELSE} + PtrInt(IdPorts[i]) + {$ENDIF} + ) + ); + end; + finally + edtPort.Items.EndUpdate; + end; + + AutoScroll := False; + Caption := RSBindingFormCaption; + {$IFDEF WIDGET_VCL} + Scaled := False; + {$ENDIF} + Font.Color := clBtnText; + Font.Height := -11; + Font.Name := 'MS Sans Serif'; {Do not Localize} + Font.Style := []; + Position := poScreenCenter; + PixelsPerInch := 96; + FInUpdateRoutine := False; + UpdateEditControls; +end; + +destructor TIdDsnPropEdBindingVCL.Destroy; +begin + FreeAndNil(FIPv4Addresses); + FreeAndNil(FIPv6Addresses); + FreeAndNil(FHandles); + inherited Destroy; +end; + +function TIdDsnPropEdBindingVCL.PortDescription(const PortNumber: integer): string; +var + LList: TStringList; +begin + if PortNumber = 0 then begin + Result := IndyFormat('%d: %s', [PortNumber, RSBindingAny]); + end else begin + Result := ''; {Do not Localize} + LList := TStringList.Create; + try + GBSDStack.AddServByPortToList(PortNumber, LList); + if LList.Count > 0 then begin + Result := Format('%d: %s', [PortNumber, LList.CommaText]); {Do not Localize} + end; + finally + LList.Free; + end; + end; +end; + +procedure TIdDsnPropEdBindingVCL.SetHandles(const Value: TIdSocketHandles); +begin + FHandles.Assign(Value); + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingVCL.btnBindingsNewExecute(Sender: TObject); +begin + FCurrentHandle := FHandles.Add; + FCurrentHandle.IP := IPv4Wildcard; + FCurrentHandle.Port := FDefaultPort; + UpdateBindingList; + edtIPAddress.Items.Assign(FIPv4Addresses); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingVCL.UpdateEditControls; +var + i : Integer; +begin + if Assigned(FCurrentHandle) then + begin + i := IndexOfNo(FCurrentHandle.Port,edtPort.Items); + if i = -1 then begin + edtPort.Text := IntToStr(FCurrentHandle.Port); + end else begin + edtPort.ItemIndex := i; + end; + + case FCurrentHandle.IPVersion of + Id_IPv4 : + begin + rdoBindingType.ItemIndex := 0; + edtIPAddress.Items.Assign(FIPv4Addresses); + end; + Id_IPv6 : + begin + rdoBindingType.ItemIndex := 1; + edtIPAddress.Items.Assign(FIPv6Addresses); + end; + end; + if edtIPAddress.Style = csDropDown then begin + edtIPAddress.Text := FCurrentHandle.IP; + end else begin + edtIPAddress.ItemIndex := edtIPAddress.Items.IndexOf(FCurrentHandle.IP); + end; + end + else + begin + edtIPAddress.Text := ''; + //in LCL, the line below caused an index out of range error. + {$IFDEF WIDGET_VCL} + edtPort.ItemIndex := -1; //-2; + {$ENDIF} + edtPort.Text := ''; + end; + + lblIPAddress.Enabled := Assigned(FCurrentHandle); + edtIPAddress.Enabled := Assigned(FCurrentHandle); + lblPort.Enabled := Assigned(FCurrentHandle); + edtPort.Enabled := Assigned(FCurrentHandle); + rdoBindingType.Enabled := Assigned(FCurrentHandle); + {$IFDEF WIDGET_KYLIX} + //WOrkaround for CLX quirk that might be Kylix 1 + for i := 0 to rdoBindingType.ControlCount -1 do begin + rdoBindingType.Controls[i].Enabled := Assigned(FCurrentHandle); + end; + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE} + //The Win32 VCL does not change the control background to a greyed look + //when controls are disabled. This quirk is not present in CLX. + if Assigned(FCurrentHandle) then + begin + edtIPAddress.Color := clWindow; + edtPort.Color := clWindow; + end else + begin + edtIPAddress.Color := clBtnFace; + edtPort.Color := clBtnFace; + end; + {$ENDIF} +end; + +procedure TIdDsnPropEdBindingVCL.btnBindingsDeleteExecute(Sender: TObject); +var + LSH : TIdSocketHandle; +begin + if lbBindings.ItemIndex >= 0 then + begin + // Delete is not available in D4's collection classes + // This should work just as well. + LSH := Handles[lbBindings.ItemIndex]; + FreeAndNil(LSH); + FCurrentHandle := nil; + UpdateBindingList; + end; + lbBindingsClick(nil); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingVCL.btnBindingsDeleteUpdate(Sender: TObject); +begin + btnBindingsDelete.Enabled := lbBindings.ItemIndex >= 0; +end; + +procedure TIdDsnPropEdBindingVCL.SetIPv4Addresses(const Value: TStrings); +begin + if Assigned(Value) then begin + FIPv4Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv4Addresses.IndexOf(IPv6Loopback) = -1 then begin + FIPv4Addresses.Insert(0, IPv4Loopback); + end; + if FIPv4Addresses.IndexOf(IPv4Wildcard) = -1 then begin + FIPv4Addresses.Insert(0, IPv4Wildcard); + end; +end; + +procedure TIdDsnPropEdBindingVCL.SetIPv6Addresses(const Value: TStrings); +begin + if Assigned(Value) then begin + FIPv6Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv6Addresses.IndexOf(IPv6Loopback) = -1 then begin + FIPv6Addresses.Insert(0, IPv6Loopback); + end; + if FIPv6Addresses.IndexOf(IPv6Wildcard1) = -1 then begin + FIPv6Addresses.Insert(0, IPv6Wildcard1); + end; +end; + +procedure TIdDsnPropEdBindingVCL.edtPortKeyPress(Sender: TObject; var Key: Char); +begin + // RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler + // may change characters >= #128 from their Ansi codepage value to their true + // Unicode codepoint value, depending on the codepage used for the source code. + // For instance, #128 may become #$20AC... + + if (Key > Chr(31)) and (Key < Chr(128)) then begin + if not IsNumeric(Key) then begin + Key := #0; + end; + end; +end; + +procedure TIdDsnPropEdBindingVCL.edtIPAddressChange(Sender: TObject); +begin + FCurrentHandle.IP := edtIPAddress.Text; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingVCL.edtPortChange(Sender: TObject); +begin + if Assigned(FCurrentHandle) then begin + FCurrentHandle.Port := IndyStrToInt(NumericOnly(edtPort.Text), 0); + end; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingVCL.rdoBindingTypeClick(Sender: TObject); +begin + case rdoBindingType.ItemIndex of + 0 : + begin + if FCurrentHandle.IPVersion <> Id_IPv4 then + begin + FCurrentHandle.IPVersion := Id_IPv4; + edtIPAddress.Items.Assign(FIPv4Addresses); + FCurrentHandle.IP := IPv4Wildcard; + end; + end; + 1 : + begin + if FCurrentHandle.IPVersion <> Id_IPv6 then + begin + FCurrentHandle.IPVersion := Id_IPv6; + edtIPAddress.Items.Assign(FIPv6Addresses); + FCurrentHandle.IP := IPv6Wildcard1; + end; + end; + end; + UpdateEditControls; + UpdateBindingList; +end; + +function TIdDsnPropEdBindingVCL.GetList: string; +begin + Result := GetListValues(Handles); +end; + +procedure TIdDsnPropEdBindingVCL.lbBindingsClick(Sender: TObject); +begin + if lbBindings.ItemIndex >= 0 then begin + FCurrentHandle := FHandles[lbBindings.ItemIndex]; + end else begin + FCurrentHandle := nil; + end; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingVCL.SetList(const AList: string); +begin + FCurrentHandle := nil; + FillHandleList(AList, Handles); + UpdateBindingList; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingVCL.UpdateBindingList; +var + i: integer; + selected: integer; + s: string; +begin +//in Lazarus, for some odd reason, if you have more than one binding, +//the routine is called while the items are updated + if FInUpdateRoutine then begin + Exit; + end; + FInUpdateRoutine := True; + try + selected := lbBindings.ItemIndex; + lbBindings.Items.BeginUpdate; + try + if lbBindings.Items.Count = FHandles.Count then begin + for i := 0 to FHandles.Count - 1 do begin + s := GetDisplayString(FHandles[i]); + if s <> lbBindings.Items[i] then begin + lbBindings.Items[i] := s; + end; + end; + end else begin + lbBindings.Items.Clear; + for i := 0 to FHandles.Count-1 do begin + lbBindings.Items.Add(GetDisplayString(FHandles[i])); + end; + end; + finally + lbBindings.Items.EndUpdate; + if Assigned(FCurrentHandle) then begin + lbBindings.ItemIndex := FCurrentHandle.Index; + end else begin + lbBindings.ItemIndex := IndyMin(selected, lbBindings.Items.Count-1); + end; + end; + finally + FInUpdateRoutine := False; + end; +end; + +function TIdDsnPropEdBindingVCL.Execute: Boolean; +begin + Result := ShowModal = mrOk; +end; + +constructor TIdDsnPropEdBindingVCL.Create; +begin + Create(nil); +end; + +end. diff --git a/indy/Core/IdExceptionCore.pas b/indy/Core/IdExceptionCore.pas new file mode 100644 index 0000000..d880b27 --- /dev/null +++ b/indy/Core/IdExceptionCore.pas @@ -0,0 +1,191 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 09/06/2004 09:52:34 CCostelloe + Kylix 3 patch + + Rev 1.2 6/4/2004 5:12:56 PM SGrobety + added EIdMaxCaptureLineExceeded + + Rev 1.1 2/10/2004 7:41:50 PM JPMugaas + I had to move EWrapperException down to the system package because + IdStackDotNET was using it and that would drage IdExceptionCore into the + package. Borland changed some behavior so the warning is now an error. + + Rev 1.0 2004.02.03 4:19:48 PM czhower + Rename + + Rev 1.15 11/4/2003 10:26:58 PM DSiders + Added exceptions moved from IdIOHandler.pas and IdTCPConnection.pas. + + Rev 1.14 2003.10.16 11:24:00 AM czhower + Added IfAssigned + + Rev 1.13 2003.10.11 5:47:58 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.12 10/3/2003 11:38:36 PM GGrieve + Add EIdWrapperException + + Rev 1.11 9/29/2003 02:56:28 PM JPMugaas + Added comment about why IdException.Create is virtual. + + Rev 1.10 9/24/2003 11:42:50 PM JPMugaas + Minor changes to help compile under NET + + Rev 1.9 2003.09.19 10:10:02 PM czhower + IfTrue, IfFalse + + Rev 1.8 2003.09.19 11:54:28 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.7 2003.07.17 4:57:04 PM czhower + Added new exception type so it can be added to debugger list of ignored + exceptions. + + Rev 1.6 7/1/2003 8:33:02 PM BGooijen + Added EIdFibersNotSupported + + Rev 1.5 2003.06.05 10:08:50 AM czhower + Extended reply mechanisms to the exception handling. Only base and RFC + completed, handing off to J Peter. + + Rev 1.4 5/14/2003 2:59:58 PM BGooijen + Added exception for transparant proxy + + Rev 1.3 2003.04.14 10:54:06 AM czhower + Fiber specific exceptions + + Rev 1.2 4/2/2003 7:18:38 PM BGooijen + Added EIdHttpProxyError + + Rev 1.1 1/17/2003 05:06:46 PM JPMugaas + Exceptions for scheduler string. + + Rev 1.0 11/13/2002 08:44:10 AM JPMugaas +} + +unit IdExceptionCore; + +interface +{$I IdCompilerDefines.inc} +//needed to put FCP into Delphi mode +uses + IdException, IdStack; + +type + // IdFiber Exceptions + EIdFiber = class(EIdException); + EIdFiberFinished = class(EIdFiber); + EIdFibersNotSupported = class(EIdFiber); + + EIdAlreadyConnected = class(EIdException); + + // EIdClosedSocket is raised if .Disconnect has been called and an operation is attempted + // or Connect has not been called + EIdClosedSocket = class(EIdException); + EIdResponseError = class(EIdException); + EIdReadTimeout = class(EIdException); + EIdAcceptTimeout = class(EIdException); + EIdReadLnMaxLineLengthExceeded = class(EIdException); + EIdReadLnWaitMaxAttemptsExceeded = class(EIdException); + + // TIdTCPConnection exceptions + EIdPortRequired = class(EIdException); + EIdHostRequired = class(EIdException); + EIdTCPConnectionError = class(EIdException); + EIdObjectTypeNotSupported = class(EIdTCPConnectionError); + EIdInterceptPropIsNil = class(EIdTCPConnectionError); + EIdInterceptPropInvalid = class(EIdTCPConnectionError); + EIdIOHandlerPropInvalid = class(EIdTCPConnectionError); + EIdNoDataToRead = class(EIdTCPConnectionError); + EIdFileNotFound = class(EIdTCPConnectionError); + + EIdNotConnected = class(EIdException); + + EInvalidSyslogMessage = class(EIdException); + EIdSSLProtocolReplyError = class(EIdException); + EIdConnectTimeout = class(EIdException); + EIdConnectException = class(EIdException); + + EIdTransparentProxyCantBind = class(EIdException); + + EIdHttpProxyError = class(EIdException); + + EIdSocksError = class(EIdException); + EIdSocksRequestFailed = class(EIdSocksError); + EIdSocksRequestServerFailed = class(EIdSocksError); + EIdSocksRequestIdentFailed = class(EIdSocksError); + EIdSocksUnknownError = class(EIdSocksError); + EIdSocksServerRespondError = class(EIdSocksError); + EIdSocksAuthMethodError = class(EIdSocksError); + EIdSocksAuthError = class(EIdSocksError); + EIdSocksServerGeneralError = class(EIdSocksError); + EIdSocksServerPermissionError = class (EIdSocksError); + EIdSocksServerNetUnreachableError = class (EIdSocksError); + EIdSocksServerHostUnreachableError = class (EIdSocksError); + EIdSocksServerConnectionRefusedError = class (EIdSocksError); + EIdSocksServerTTLExpiredError = class (EIdSocksError); + EIdSocksServerCommandError = class (EIdSocksError); + EIdSocksServerAddressError = class (EIdSocksError); + + //IdIMAP4 Exception + EIdConnectionStateError = class(EIdException); + + // THE EDnsResolverError is used so the resolver can repond to only resolver execeptions. + EIdDnsResolverError = Class(EIdException); + + {Socket exceptions} + EIdInvalidSocket = class(EIdException); + + EIdThreadMgrError = class(EIdException); + EIdThreadClassNotSpecified = class(EIdThreadMgrError); + + {TIdTrivial FTP Exception } + EIdTFTPException = class(EIdException); + EIdTFTPFileNotFound = class(EIdTFTPException); + EIdTFTPAccessViolation = class(EIdTFTPException); + EIdTFTPAllocationExceeded = class(EIdTFTPException); + EIdTFTPIllegalOperation = class(EIdTFTPException); + EIdTFTPUnknownTransferID = class(EIdTFTPException); + EIdTFTPFileAlreadyExists = class(EIdTFTPException); + EIdTFTPNoSuchUser = class(EIdTFTPException); + EIdTFTPOptionNegotiationFailed = class(EIdTFTPException); // RFC 1782 + + {Icmp exceptions} + EIdIcmpException = class(EIdException); + + EIdSetSizeExceeded = class(EIdException); + + {IdMessage and things use this} + EIdMessageException = class(EIdException); + + //scheduler exception + EIdSchedulerException = class(EIdException); + EIdSchedulerMaxThreadsExceeded = class(EIdSchedulerException); + + { IdIOHandler } + EIdMaxCaptureLineExceeded = class(EIdException); // S.G. 6/4/2004: triggered when a capture command exceeds the maximum number of line allowed + +implementation + +end. diff --git a/indy/Core/IdGlobalCore.pas b/indy/Core/IdGlobalCore.pas new file mode 100644 index 0000000..566a1d1 --- /dev/null +++ b/indy/Core/IdGlobalCore.pas @@ -0,0 +1,48 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 8/16/2004 1:08:46 PM JPMugaas + Failed to compile in some IDE's. + + Rev 1.1 2004.08.13 21:46:20 czhower + Fix for .NET + + Rev 1.0 2004.08.13 10:54:58 czhower + Initial checkin +} + +unit IdGlobalCore; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdGlobal; + +const + {$IFDEF UNIX} + tpListener = tpNormal; + {$ELSE} + tpListener = tpHighest; + {$ENDIF} + +implementation + +end. diff --git a/indy/Core/IdIOHandler.pas b/indy/Core/IdIOHandler.pas new file mode 100644 index 0000000..42f0775 --- /dev/null +++ b/indy/Core/IdIOHandler.pas @@ -0,0 +1,2695 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} + +{ + Rev 1.123 2/8/05 5:27:06 PM RLebeau + Bug fix for ReadLn(). + + Added try..finally block to ReadLnSplit(). + + Rev 1.122 1/27/05 3:09:30 PM RLebeau + Updated AllData() to call ReadFromSource() directly instead of using + CheckForDataOnSource(), since ReadFromSource() can return a disconnect + conditon. When data is in the InputBuffer, Connected() always return True + even if the socket is actually disconnected. + + Rev 1.121 12/21/04 3:21:40 AM RLebeau + Removed compiler warning + + Rev 1.120 17/12/2004 17:11:28 ANeillans + Compiler fix + + Rev 1.119 12/12/04 2:23:52 PM RLebeau + Added WriteRFCStrings() method + + Rev 1.118 12/11/2004 9:04:50 PM DSiders + Fixed comparison error in WaitFor. + + Rev 1.117 12/10/04 2:00:24 PM RLebeau + Updated WaitFor() to not return more data than actually needed. + + Updated AllData() to not concatenate the Result on every iteration of the + loop. + + Rev 1.116 11/29/04 10:37:18 AM RLebeau + Updated write buffering methods to prevent Access Violations when used + incorrectly. + + Rev 1.115 11/4/04 12:41:08 PM RLebeau + Bug fix for ReadLn() + + Rev 1.114 10/26/2004 8:43:00 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.113 27.08.2004 21:58:18 Andreas Hausladen + Speed optimization ("const" for string parameters) + + Rev 1.112 8/2/04 5:49:20 PM RLebeau + Moved ConnectTimeout over from TIdIOHandlerSocket + + Rev 1.111 2004.08.01 19:36:14 czhower + Code optimization to WriteFile + + Rev 1.110 7/24/04 12:53:54 PM RLebeau + Compiler fix for WriteFile() + + Rev 1.109 7/23/04 6:39:14 PM RLebeau + Added extra exception handling to WriteFile() + + Rev 1.108 7/21/2004 5:45:10 PM JPMugaas + Updated with Remy's change. This should work better and fix a problem with + looping with ReadStream and ReadUntilDisconnect. + + Rev 1.107 7/21/2004 12:22:18 PM BGooijen + Reverted back 2 versions + + Rev 1.104 6/29/04 12:16:16 PM RLebeau + Updated ReadChar() to call ReadBytes() directly instead of ReadString() + + Rev 1.103 6/17/04 3:01:56 PM RLebeau + Changed ReadStream() to not extract too many bytes from the InputBuffer when + an error occurs + + Rev 1.102 6/12/04 11:36:44 AM RLebeau + Changed ReadString() to pass the ABytes parameter to ReadBytes() instead of + the LBuf length + + Rev 1.100 6/10/2004 6:52:12 PM JPMugaas + Regeneration to fix a bug in the package generator that I created. OOPS!!! + + Rev 1.99 6/9/04 7:36:26 PM RLebeau + ReadString() bug fix + + Rev 1.98 07/06/2004 20:55:36 CCostelloe + Fix for possible memory leak. + + Rev 1.97 5/29/04 10:46:24 PM RLebeau + Updated AllData() to only append values to the result when there is actual + data in the buffer. + + Rev 1.96 29/05/2004 21:07:40 CCostelloe + Bug fix (may need more investigation) + + Rev 1.95 2004.05.20 1:39:54 PM czhower + Last of the IdStream updates + + Rev 1.94 2004.05.20 12:34:22 PM czhower + Removed more non .NET compatible stream read and writes + + Rev 1.93 2004.05.20 11:39:02 AM czhower + IdStreamVCL + + Rev 1.92 5/3/2004 12:57:00 PM BGooijen + Fixes for 0-based + + Rev 1.91 2004.05.03 11:15:44 AM czhower + Changed Find to IndexOf and made 0 based to be consistent. + + Rev 1.90 4/24/04 12:40:04 PM RLebeau + Added Write() overload for Char type. + + Rev 1.89 4/18/2004 11:58:00 PM BGooijen + ReadBytes with count=-1 reads everything available, ( and waits ReadTimeOut + time for data) + + Rev 1.88 4/18/04 2:44:24 PM RLebeau + Read/write support for Int64 values + + Rev 1.87 2004.04.18 12:51:58 AM czhower + Big bug fix with server disconnect and several other bug fixed that I found + along the way. + + Rev 1.86 2004.04.16 11:30:28 PM czhower + Size fix to IdBuffer, optimizations, and memory leaks + + Rev 1.85 2004.04.08 7:06:46 PM czhower + Peek support. + + Rev 1.84 2004.04.08 3:56:28 PM czhower + Fixed bug with Intercept byte count. Also removed Bytes from Buffer. + + Rev 1.83 2004.04.08 2:08:00 AM czhower + Saved before checkin this time... + + Rev 1.82 7/4/2004 4:08:46 PM SGrobety + Re-introduce the IOHandler.MaxCapturedLines property + + Rev 1.81 2004.04.07 3:59:46 PM czhower + Bug fix for WriteDirect. + + Rev 1.79 2004.03.07 11:48:38 AM czhower + Flushbuffer fix + other minor ones found + + Rev 1.78 2004.03.03 11:54:58 AM czhower + IdStream change + + Rev 1.77 2004.03.02 2:47:08 PM czhower + .Net overloads + + Rev 1.76 2004.03.01 5:12:28 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.75 2004.02.03 4:16:44 PM czhower + For unit name changes. + + Rev 1.74 2004.01.21 9:36:00 PM czhower + .Net overload + + Rev 1.73 2004.01.21 12:19:58 AM czhower + .Readln overload for .net + + Rev 1.72 2004.01.20 10:03:26 PM czhower + InitComponent + + Rev 1.71 1/11/2004 5:51:04 PM BGooijen + Added AApend parameter to ReadBytes + + Rev 1.70 12/30/2003 7:17:56 PM BGooijen + .net + + Rev 1.69 2003.12.28 1:05:54 PM czhower + .Net changes. + + Rev 1.68 2003.12.28 11:53:28 AM czhower + Removed warning in .net. + + Rev 1.67 2003.11.29 10:15:30 AM czhower + InternalBuffer --> InputBuffer for consistency. + + Rev 1.66 11/23/03 1:46:28 PM RLebeau + Removed "var" specifier from TStrings parameter of ReadStrings(). + + Rev 1.65 11/4/2003 10:27:56 PM DSiders + Removed exceptions moved to IdException.pas. + + Rev 1.64 2003.10.24 10:44:52 AM czhower + IdStream implementation, bug fixes. + + Rev 1.63 10/22/03 2:05:40 PM RLebeau + Fix for TIdIOHandler::Write(TStream) where it was not reading the stream into + the TIdBytes correctly. + + Rev 1.62 10/19/2003 5:55:44 PM BGooijen + Fixed todo in PerformCapture + + Rev 1.61 2003.10.18 12:58:50 PM czhower + Added comment + + Rev 1.60 2003.10.18 12:42:04 PM czhower + Intercept.Disconnect is now called + + Rev 1.59 10/15/2003 7:39:28 PM DSiders + Added a formatted resource string for the exception raised in + TIdIOHandler.MakeIOHandler. + + Rev 1.58 2003.10.14 1:26:50 PM czhower + Uupdates + Intercept support + + Rev 1.57 2003.10.11 5:48:22 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.56 9/10/2003 1:50:38 PM SGrobety + Removed all "const" keywords from boolean parameter interfaces. Might trigger + changes in other units. + + Rev 1.55 10/5/2003 10:39:56 PM BGooijen + Write buffering + + Rev 1.54 10/4/2003 11:03:12 PM BGooijen + ReadStream, and functions with network ordering + + Rev 1.53 10/4/2003 7:10:46 PM BGooijen + ReadXXXXX + + Rev 1.52 10/4/2003 3:55:02 PM BGooijen + ReadString, and some Write functions + + Rev 1.51 04/10/2003 13:38:32 HHariri + Write(Integer) support + + Rev 1.50 10/3/2003 12:09:30 AM BGooijen + DotNet + + Rev 1.49 2003.10.02 8:29:14 PM czhower + Changed names of byte conversion routines to be more readily understood and + not to conflict with already in use ones. + + Rev 1.48 2003.10.02 1:18:50 PM czhower + Changed read methods to be overloaded and more consistent. Will break some + code, but nearly all code that uses them is Input. + + Rev 1.47 2003.10.02 10:16:26 AM czhower + .Net + + Rev 1.46 2003.10.01 9:11:16 PM czhower + .Net + + Rev 1.45 2003.10.01 2:46:36 PM czhower + .Net + + Rev 1.42 2003.10.01 11:16:32 AM czhower + .Net + + Rev 1.41 2003.10.01 1:37:34 AM czhower + .Net + + Rev 1.40 2003.10.01 1:12:34 AM czhower + .Net + + Rev 1.39 2003.09.30 1:22:56 PM czhower + Stack split for DotNet + + Rev 1.38 2003.09.18 5:17:58 PM czhower + Implemented OnWork + + Rev 1.37 2003.08.21 10:43:42 PM czhower + Fix to ReadStream from Doychin + + Rev 1.36 08/08/2003 17:32:26 CCostelloe + Removed "virtual" from function ReadLnSplit + + Rev 1.35 07/08/2003 00:25:08 CCostelloe + Function ReadLnSplit added + + Rev 1.34 2003.07.17 1:05:12 PM czhower + More IOCP improvements. + + Rev 1.33 2003.07.14 11:00:50 PM czhower + More IOCP fixes. + + Rev 1.32 2003.07.14 12:54:30 AM czhower + Fixed graceful close detection if it occurs after connect. + + Rev 1.31 2003.07.10 7:40:24 PM czhower + Comments + + Rev 1.30 2003.07.10 4:34:56 PM czhower + Fixed AV, added some new comments + + Rev 1.29 7/1/2003 5:50:44 PM BGooijen + Fixed ReadStream + + Rev 1.28 6/30/2003 10:26:08 AM BGooijen + forgot to remove some code regarding to TIdBuffer.Find + + Rev 1.27 6/29/2003 10:56:26 PM BGooijen + Removed .Memory from the buffer, and added some extra methods + + Rev 1.26 2003.06.25 4:30:00 PM czhower + Temp hack fix for AV problem. Working on real solution now. + + Rev 1.25 23/6/2003 22:33:14 GGrieve + fix CheckForDataOnSource - specify timeout + + Rev 1.24 23/6/2003 06:46:52 GGrieve + allow block on checkForData + + Rev 1.23 6/4/2003 1:07:08 AM BGooijen + changed comment + + Rev 1.22 6/3/2003 10:40:34 PM BGooijen + FRecvBuffer bug fixed, it was freed, but never recreated, resulting in an AV + + Rev 1.21 2003.06.03 6:28:04 PM czhower + Made check for data virtual + + Rev 1.20 2003.06.03 3:43:24 PM czhower + Resolved InputBuffer inconsistency. Added new method and renamed old one. + + Rev 1.19 5/25/2003 03:56:04 AM JPMugaas + Updated for unit rename. + + Rev 1.18 2003.04.17 11:01:12 PM czhower + + Rev 1.17 4/16/2003 3:29:30 PM BGooijen + minor change in ReadBuffer + + Rev 1.16 4/1/2003 7:54:24 PM BGooijen + ReadLn default terminator changed to LF + + Rev 1.15 3/27/2003 3:24:06 PM BGooijen + MaxLine* is now published + + Rev 1.14 2003.03.25 7:42:12 PM czhower + try finally to WriteStrings + + Rev 1.13 3/24/2003 11:01:36 PM BGooijen + WriteStrings is now buffered to increase speed + + Rev 1.12 3/19/2003 1:02:32 PM BGooijen + changed class function ConstructDefaultIOHandler a little (default parameter) + + Rev 1.11 3/13/2003 10:18:16 AM BGooijen + Server side fibers, bug fixes + + Rev 1.10 3/5/2003 11:03:06 PM BGooijen + Added Intercept here + + Rev 1.9 2/25/2003 11:02:12 PM BGooijen + InputBufferToStream now accepts a bytecount + + Rev 1.8 2003.02.25 1:36:00 AM czhower + + Rev 1.7 12-28-2002 22:28:16 BGooijen + removed warning, added initialization and finalization part. + + Rev 1.6 12-16-2002 20:43:28 BGooijen + Added class function ConstructIOHandler(....), and removed some comments + + Rev 1.5 12-15-2002 23:02:38 BGooijen + added SendBufferSize + + Rev 1.4 12-15-2002 20:50:32 BGooijen + FSendBufferSize was not initialized + + Rev 1.3 12-14-2002 22:14:54 BGooijen + improved method to detect timeouts in ReadLn. + + Rev 1.2 12/11/2002 04:09:28 AM JPMugaas + Updated for new API. + + Rev 1.1 2002.12.07 12:25:56 AM czhower + + Rev 1.0 11/13/2002 08:44:50 AM JPMugaas +} + +unit IdIOHandler; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdAntiFreezeBase, IdBuffer, IdBaseComponent, IdComponent, IdGlobal, IdExceptionCore, + IdIntercept, IdResourceStringsCore, IdStream; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Idiohandler@TIdIOHandler@SetPortA$qqri"="@Idiohandler@TIdIOHandler@SetPort$qqri"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Idiohandler@TIdIOHandler@SetPortW$qqri"="@Idiohandler@TIdIOHandler@SetPort$qqri"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +const + GRecvBufferSizeDefault = 32 * 1024; + GSendBufferSizeDefault = 32 * 1024; + IdMaxLineLengthDefault = 16 * 1024; + // S.G. 6/4/2004: Maximum number of lines captured + // S.G. 6/4/2004: Default to "unlimited" + Id_IOHandler_MaxCapturedLines = -1; + +type + + EIdIOHandler = class(EIdException); + EIdIOHandlerRequiresLargeStream = class(EIdIOHandler); + EIdIOHandlerStreamDataTooLarge = class(EIdIOHandler); + + TIdIOHandlerClass = class of TIdIOHandler; + + { + How does this fit in in the hierarchy against TIdIOHandlerSocket + Destination - Socket - otehr file descendats it + + TIdIOHandler should only implement an interface. No default functionality + except very simple read/write functions such as ReadUInt32, etc. Functions + that cannot really be optimized beyond their default implementations. + + Some default implementations offer basic non optmized implementations. + + Yes, I know this comment conflicts. Its being worked on. + } + TIdIOHandler = class(TIdComponent) + private + FLargeStream: Boolean; + protected + FClosedGracefully: Boolean; + FConnectTimeout: Integer; + FDestination: string; + FHost: string; + // IOHandlers typically receive more data than they need to complete each + // request. They store this extra data in InputBuffer for future methods to + // use. InputBuffer is what collects the input and keeps it if the current + // method does not need all of it. + // + FInputBuffer: TIdBuffer; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIntercept: TIdConnectionIntercept; + FMaxCapturedLines: Integer; + FMaxLineAction: TIdMaxLineAction; + FMaxLineLength: Integer; + FOpened: Boolean; + FPort: Integer; + FReadLnSplit: Boolean; + FReadLnTimedOut: Boolean; + FReadTimeOut: Integer; +//TODO: + FRecvBufferSize: Integer; + FSendBufferSize: Integer; + + FWriteBuffer: TIdBuffer; + FWriteBufferThreshold: Integer; + FDefStringEncoding : IIdTextEncoding; + {$IFDEF STRING_IS_ANSI} + FDefAnsiEncoding : IIdTextEncoding; + {$ENDIF} + procedure SetDefStringEncoding(const AEncoding : IIdTextEncoding); + {$IFDEF STRING_IS_ANSI} + procedure SetDefAnsiEncoding(const AEncoding: IIdTextEncoding); + {$ENDIF} + // + procedure BufferRemoveNotify(ASender: TObject; ABytes: Integer); + function GetDestination: string; virtual; + procedure InitComponent; override; + procedure InterceptReceive(var VBuffer: TIdBytes); + {$IFNDEF USE_OBJECT_ARC} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + {$ENDIF} + procedure PerformCapture(const ADest: TObject; out VLineCount: Integer; + const ADelim: string; AUsesDotTransparency: Boolean; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); virtual; + procedure RaiseConnClosedGracefully; + procedure SetDestination(const AValue: string); virtual; + procedure SetHost(const AValue: string); virtual; + procedure SetPort(AValue: Integer); virtual; + procedure SetIntercept(AValue: TIdConnectionIntercept); virtual; + // This is the main Read function which all other default implementations + // use. + function ReadFromSource(ARaiseExceptionIfDisconnected: Boolean = True; + ATimeout: Integer = IdTimeoutDefault; + ARaiseExceptionOnTimeout: Boolean = True): Integer; + function ReadDataFromSource(var VBuffer: TIdBytes): Integer; virtual; abstract; + function WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; virtual; abstract; + function SourceIsAvailable: Boolean; virtual; abstract; + function CheckForError(ALastResult: Integer): Integer; virtual; abstract; + procedure RaiseError(AError: Integer); virtual; abstract; + public + procedure AfterAccept; virtual; + function Connected: Boolean; virtual; + destructor Destroy; override; + // CheckForDisconnect allows the implementation to check the status of the + // connection at the request of the user or this base class. + procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True; + AIgnoreBuffer: Boolean = False); virtual; abstract; + // Does not wait or raise any exceptions. Just reads whatever data is + // available (if any) into the buffer. Must NOT raise closure exceptions. + // It is used to get avialable data, and check connection status. That is + // it can set status flags about the connection. + function CheckForDataOnSource(ATimeout: Integer = 0): Boolean; virtual; + procedure Close; virtual; + procedure CloseGracefully; virtual; + class function MakeDefaultIOHandler(AOwner: TComponent = nil) + : TIdIOHandler; + class function MakeIOHandler(ABaseType: TIdIOHandlerClass; + AOwner: TComponent = nil): TIdIOHandler; + // Variant of MakeIOHandler() which returns nil if it cannot find a registered IOHandler + class function TryMakeIOHandler(ABaseType: TIdIOHandlerClass; + AOwner: TComponent = nil): TIdIOHandler; + class procedure RegisterIOHandler; + class procedure SetDefaultClass; + function WaitFor(const AString: string; ARemoveFromBuffer: Boolean = True; + AInclusive: Boolean = False; AByteEncoding: IIdTextEncoding = nil; + ATimeout: Integer = IdTimeoutDefault + {$IFDEF STRING_IS_ANSI}; AAnsiEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + // This is different than WriteDirect. WriteDirect goes + // directly to the network or next level. WriteBuffer allows for buffering + // using WriteBuffers. This should be the only call to WriteDirect + // unless the calls that bypass this are aware of WriteBuffering or are + // intended to bypass it. + procedure Write(const ABuffer: TIdBytes; const ALength: Integer = -1; const AOffset: Integer = 0); overload; virtual; + // This is the main write function which all other default implementations + // use. If default implementations are used, this must be implemented. + procedure WriteDirect(const ABuffer: TIdBytes; const ALength: Integer = -1; const AOffset: Integer = 0); + // + procedure Open; virtual; + function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; virtual; + // + // Optimal Extra Methods + // + // These methods are based on the core methods. While they can be + // overridden, they are so simple that it is rare a more optimal method can + // be implemented. Because of this they are not overrideable. + // + // + // Write Methods + // + // Only the ones that have a hope of being better optimized in descendants + // have been marked virtual + procedure Write(const AOut: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; virtual; + procedure WriteLn(AEncoding: IIdTextEncoding = nil); overload; + procedure WriteLn(const AOut: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; virtual; + procedure WriteLnRFC(const AOut: string = ''; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); virtual; + procedure Write(AValue: TStrings; AWriteLinesCount: Boolean = False; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; virtual; + procedure Write(AValue: Byte); overload; + procedure Write(AValue: Char; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + + // for iOS64, Delphi's Longint and LongWord are 64bit, so we can't rely on + // Write(Longint) and ReadLongint() being 32bit anymore, for instance when + // sending/reading a TStream with LargeStream=False. So adding new (U)IntX + // methods and deprecating the old ones... + // + procedure Write(AValue: Int16; AConvert: Boolean = True); overload; + procedure Write(AValue: UInt16; AConvert: Boolean = True); overload; + procedure Write(AValue: Int32; AConvert: Boolean = True); overload; + procedure Write(AValue: UInt32; AConvert: Boolean = True); overload; + procedure Write(AValue: Int64; AConvert: Boolean = True); overload; + procedure Write(AValue: TIdUInt64; AConvert: Boolean = True); overload; + // + + procedure Write(AStream: TStream; ASize: TIdStreamSize = 0; + AWriteByteCount: Boolean = False); overload; virtual; + procedure WriteRFCStrings(AStrings: TStrings; AWriteTerminator: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); + // Not overloaded because it does not have a unique type for source + // and could be easily unresolvable with future additions + function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; virtual; + // + // Read methods + // + function AllData(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; virtual; + function InputLn(const AMask: string = ''; AEcho: Boolean = True; + ATabWidth: Integer = 8; AMaxLineLength: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; AAnsiEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; virtual; + // Capture + // Not virtual because each calls PerformCapture which is virtual + procedure Capture(ADest: TStream; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; // .Net overload + procedure Capture(ADest: TStream; ADelim: string; + AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Capture(ADest: TStream; out VLineCount: Integer; + const ADelim: string = '.'; AUsesDotTransparency: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Capture(ADest: TStrings; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; // .Net overload + procedure Capture(ADest: TStrings; const ADelim: string; + AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Capture(ADest: TStrings; out VLineCount: Integer; + const ADelim: string = '.'; AUsesDotTransparency: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + // + // Read___ + // Cannot overload, compiler cannot overload on return values + // + procedure ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); virtual; + // ReadLn + function ReadLn(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; // .Net overload + function ReadLn(ATerminator: string; AByteEncoding: IIdTextEncoding + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function ReadLn(ATerminator: string; ATimeout: Integer = IdTimeoutDefault; + AMaxLineLength: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; virtual; + //RLebeau: added for RFC 822 retrieves + function ReadLnRFC(var VMsgEnd: Boolean; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function ReadLnRFC(var VMsgEnd: Boolean; const ALineTerminator: string; + const ADelim: string = '.'; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function ReadLnWait(AFailCount: Integer = MaxInt; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; virtual; + // Added for retrieving lines over 16K long} + function ReadLnSplit(var AWasSplit: Boolean; ATerminator: string = LF; + ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + // Read - Simple Types + function ReadChar(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): Char; + function ReadByte: Byte; + function ReadString(ABytes: Integer; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + + // for iOS64, Delphi's Longint and LongWord are changed to 64bit, so we can't + // rely on Write(Longint) and ReadLongint() being 32bit anymore, for instance + // when sending/reading a TStream with LargeStream=False. So adding new (U)IntX + // methods and deprecating the old ones... + // + function ReadInt16(AConvert: Boolean = True): Int16; + function ReadUInt16(AConvert: Boolean = True): UInt16; + function ReadInt32(AConvert: Boolean = True): Int32; + function ReadUInt32(AConvert: Boolean = True): UInt32; + function ReadInt64(AConvert: Boolean = True): Int64; + function ReadUInt64(AConvert: Boolean = True): TIdUInt64; + // + function ReadSmallInt(AConvert: Boolean = True): Int16; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ReadInt16()'{$ENDIF};{$ENDIF} + function ReadWord(AConvert: Boolean = True): UInt16; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ReadUInt16()'{$ENDIF};{$ENDIF} + function ReadLongInt(AConvert: Boolean = True): Int32; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ReadInt32()'{$ENDIF};{$ENDIF} + function ReadLongWord(AConvert: Boolean = True): UInt32; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ReadUInt32()'{$ENDIF};{$ENDIF} + // + + procedure ReadStream(AStream: TStream; AByteCount: TIdStreamSize = -1; + AReadUntilDisconnect: Boolean = False); virtual; + procedure ReadStrings(ADest: TStrings; AReadLinesCount: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); + // + procedure Discard(AByteCount: Int64); + procedure DiscardAll; + // + // WriteBuffering Methods + // + procedure WriteBufferCancel; virtual; + procedure WriteBufferClear; virtual; + procedure WriteBufferClose; virtual; + procedure WriteBufferFlush; overload; //.Net overload + procedure WriteBufferFlush(AByteCount: Integer); overload; virtual; + procedure WriteBufferOpen; overload; //.Net overload + procedure WriteBufferOpen(AThreshold: Integer); overload; virtual; + function WriteBufferingActive: Boolean; + // + // InputBuffer Methods + // + function InputBufferIsEmpty: Boolean; + // + // These two are direct access and do no reading of connection + procedure InputBufferToStream(AStream: TStream; AByteCount: Integer = -1); + function InputBufferAsString(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + // + // Properties + // + property ConnectTimeout: Integer read FConnectTimeout write FConnectTimeout default 0; + property ClosedGracefully: Boolean read FClosedGracefully; + // TODO: Need to name this consistent. Originally no access was allowed, + // but new model requires it for writing. Will decide after next set + // of changes are complete what to do with Buffer prop. + // + // Is used by SuperCore + property InputBuffer: TIdBuffer read FInputBuffer; + //currently an option, as LargeFile support changes the data format + property LargeStream: Boolean read FLargeStream write FLargeStream; + property MaxCapturedLines: Integer read FMaxCapturedLines write FMaxCapturedLines default Id_IOHandler_MaxCapturedLines; + property Opened: Boolean read FOpened; + property ReadTimeout: Integer read FReadTimeOut write FReadTimeOut default IdTimeoutDefault; + property ReadLnTimedout: Boolean read FReadLnTimedout ; + property WriteBufferThreshold: Integer read FWriteBufferThreshold; + property DefStringEncoding : IIdTextEncoding read FDefStringEncoding write SetDefStringEncoding; + {$IFDEF STRING_IS_ANSI} + property DefAnsiEncoding : IIdTextEncoding read FDefAnsiEncoding write SetDefAnsiEncoding; + {$ENDIF} + // + // Events + // + property OnWork; + property OnWorkBegin; + property OnWorkEnd; + published + property Destination: string read GetDestination write SetDestination; + property Host: string read FHost write SetHost; + property Intercept: TIdConnectionIntercept read FIntercept write SetIntercept; + property MaxLineLength: Integer read FMaxLineLength write FMaxLineLength default IdMaxLineLengthDefault; + property MaxLineAction: TIdMaxLineAction read FMaxLineAction write FMaxLineAction; + property Port: Integer read FPort write SetPort; + // RecvBufferSize is used by some methods that read large amounts of data. + // RecvBufferSize is the amount of data that will be requested at each read + // cycle. RecvBuffer is used to receive then send to the Intercepts, after + // that it goes to InputBuffer + property RecvBufferSize: Integer read FRecvBufferSize write FRecvBufferSize + default GRecvBufferSizeDefault; + // SendBufferSize is used by some methods that have to break apart large + // amounts of data into smaller pieces. This is the buffer size of the + // chunks that it will create and use. + property SendBufferSize: Integer read FSendBufferSize write FSendBufferSize + default GSendBufferSizeDefault; + end; + +implementation + +uses + //facilitate inlining only. + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF WIN32_OR_WIN64} + Windows, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + {$IFDEF DARWIN} + Macapi.CoreServices, + {$ENDIF} + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdStack, IdStackConsts, IdResourceStrings, + SysUtils; + +type + {$IFDEF HAS_GENERICS_TList} + TIdIOHandlerClassList = TList; + {$ELSE} + // TODO: flesh out to match TList for non-Generics compilers + TIdIOHandlerClassList = TList; + {$ENDIF} + +var + GIOHandlerClassDefault: TIdIOHandlerClass = nil; + GIOHandlerClassList: TIdIOHandlerClassList = nil; + +{$IFDEF DCC} + {$IFNDEF VCL_7_OR_ABOVE} + // RLebeau 5/13/2015: The Write(Int64) and ReadInt64() methods produce an + // "Internal error URW533" compiler error in Delphi 5, and an "Internal + // error URW699" compiler error in Delphi 6, so need to use some workarounds + // for those versions... + {$DEFINE AVOID_URW_ERRORS} + {$ENDIF} +{$ENDIF} + +{ TIdIOHandler } + +procedure TIdIOHandler.Close; +//do not do FInputBuffer.Clear; here. +//it breaks reading when remote connection does a disconnect +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + try + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Disconnect; + end; + finally + FOpened := False; + WriteBufferClear; + end; +end; + +destructor TIdIOHandler.Destroy; +begin + Close; + FreeAndNil(FInputBuffer); + FreeAndNil(FWriteBuffer); + inherited Destroy; +end; + +procedure TIdIOHandler.AfterAccept; +begin + // +end; + +procedure TIdIOHandler.Open; +begin + FOpened := False; + FClosedGracefully := False; + WriteBufferClear; + FInputBuffer.Clear; + FOpened := True; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +{$IFNDEF USE_OBJECT_ARC} +procedure TIdIOHandler.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FIntercept) then begin + FIntercept := nil; + end; + inherited Notification(AComponent, OPeration); +end; +{$ENDIF} + +procedure TIdIOHandler.SetIntercept(AValue: TIdConnectionIntercept); +begin + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FIntercept := AValue; + {$ELSE} + if FIntercept <> AValue then begin + // remove self from the Intercept's free notification list + if Assigned(FIntercept) then begin + FIntercept.RemoveFreeNotification(Self); + end; + FIntercept := AValue; + // add self to the Intercept's free notification list + if Assigned(AValue) then begin + AValue.FreeNotification(Self); + end; + end; + {$ENDIF} +end; + +class procedure TIdIOHandler.SetDefaultClass; +begin + GIOHandlerClassDefault := Self; + RegisterIOHandler; +end; + +procedure TIdIOHandler.SetDefStringEncoding(const AEncoding: IIdTextEncoding); +var + LEncoding: IIdTextEncoding; +begin + if FDefStringEncoding <> AEncoding then + begin + LEncoding := AEncoding; + EnsureEncoding(LEncoding); + FDefStringEncoding := LEncoding; + end; +end; + +{$IFDEF STRING_IS_ANSI} +procedure TIdIOHandler.SetDefAnsiEncoding(const AEncoding: IIdTextEncoding); +var + LEncoding: IIdTextEncoding; +begin + if FDefAnsiEncoding <> AEncoding then + begin + LEncoding := AEncoding; + EnsureEncoding(LEncoding, encOSDefault); + FDefAnsiEncoding := LEncoding; + end; +end; +{$ENDIF} + +class function TIdIOHandler.MakeDefaultIOHandler(AOwner: TComponent = nil): TIdIOHandler; +begin + Result := GIOHandlerClassDefault.Create(AOwner); +end; + +class procedure TIdIOHandler.RegisterIOHandler; +begin + if GIOHandlerClassList = nil then begin + GIOHandlerClassList := TIdIOHandlerClassList.Create; + end; + {$IFNDEF DOTNET_EXCLUDE} + //TODO: Reenable this. Dot net wont allow class references as objects + // Use an array? + if GIOHandlerClassList.IndexOf(Self) = -1 then begin + GIOHandlerClassList.Add(Self); + end; + {$ENDIF} +end; + +{ + Creates an IOHandler of type ABaseType, or descendant. +} +class function TIdIOHandler.MakeIOHandler(ABaseType: TIdIOHandlerClass; + AOwner: TComponent = nil): TIdIOHandler; +begin + Result := TryMakeIOHandler(ABaseType, AOwner); + if not Assigned(Result) then begin + raise EIdException.CreateFmt(RSIOHandlerTypeNotInstalled, [ABaseType.ClassName]); + end; +end; + +class function TIdIOHandler.TryMakeIOHandler(ABaseType: TIdIOHandlerClass; + AOwner: TComponent = nil): TIdIOHandler; +var + i: Integer; +begin + if GIOHandlerClassList <> nil then begin + for i := GIOHandlerClassList.Count - 1 downto 0 do begin + if TIdIOHandlerClass(GIOHandlerClassList[i]).InheritsFrom(ABaseType) then begin + Result := TIdIOHandlerClass(GIOHandlerClassList[i]).Create; + Exit; + end; + end; + end; + Result := nil; +end; + +function TIdIOHandler.GetDestination: string; +begin + Result := FDestination; +end; + +procedure TIdIOHandler.SetDestination(const AValue: string); +begin + FDestination := AValue; +end; + +procedure TIdIOHandler.BufferRemoveNotify(ASender: TObject; ABytes: Integer); +begin + DoWork(wmRead, ABytes); +end; + +procedure TIdIOHandler.WriteBufferOpen(AThreshold: Integer); +begin + if FWriteBuffer <> nil then begin + FWriteBuffer.Clear; + end else begin + FWriteBuffer := TIdBuffer.Create; + end; + FWriteBufferThreshold := AThreshold; +end; + +procedure TIdIOHandler.WriteBufferClose; +begin + try + WriteBufferFlush; + finally FreeAndNil(FWriteBuffer); end; +end; + +procedure TIdIOHandler.WriteBufferFlush(AByteCount: Integer); +var + LBytes: TIdBytes; +begin + if FWriteBuffer <> nil then begin + if FWriteBuffer.Size > 0 then begin + FWriteBuffer.ExtractToBytes(LBytes, AByteCount); + WriteDirect(LBytes); + end; + end; +end; + +procedure TIdIOHandler.WriteBufferClear; +begin + if FWriteBuffer <> nil then begin + FWriteBuffer.Clear; + end; +end; + +procedure TIdIOHandler.WriteBufferCancel; +begin + WriteBufferClear; + WriteBufferClose; +end; + +procedure TIdIOHandler.Write(const AOut: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + if AOut <> '' then begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ASrcEncoding := iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + Write( + ToBytes(AOut, -1, 1, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ) + ); + end; +end; + +procedure TIdIOHandler.Write(AValue: Byte); +begin + Write(ToBytes(AValue)); +end; + +procedure TIdIOHandler.Write(AValue: Char; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ASrcEncoding := iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + Write( + ToBytes(AValue, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ) + ); +end; + +procedure TIdIOHandler.Write(AValue: UInt32; AConvert: Boolean = True); +begin + if AConvert then begin + AValue := GStack.HostToNetwork(AValue); + end; + Write(ToBytes(AValue)); +end; + +procedure TIdIOHandler.Write(AValue: Int32; AConvert: Boolean = True); +begin + if AConvert then begin + AValue := Int32(GStack.HostToNetwork(UInt32(AValue))); + end; + Write(ToBytes(AValue)); +end; + +{$IFDEF HAS_UInt64} + {$IFDEF BROKEN_UInt64_HPPEMIT} + {$DEFINE HAS_TIdUInt64_QuadPart} + {$ENDIF} +{$ELSE} + {$IFNDEF HAS_QWord} + {$DEFINE HAS_TIdUInt64_QuadPart} + {$ENDIF} +{$ENDIF} + +procedure TIdIOHandler.Write(AValue: Int64; AConvert: Boolean = True); +{$IFDEF AVOID_URW_ERRORS} +var + h: Int64; +{$ELSE} + {$IFDEF HAS_TIdUInt64_QuadPart} +var + h: TIdUInt64; + {$ENDIF} +{$ENDIF} +begin + if AConvert then begin + {$IFDEF AVOID_URW_ERRORS} + // assigning to a local variable to avoid an "Internal error URW533" compiler + // error in Delphi 5, and an "Internal error URW699" compiler error in Delphi + // 6. Later versions seem OK without it... + h := GStack.HostToNetwork(UInt64(AValue)); + AValue := h; + {$ELSE} + {$IFDEF HAS_TIdUInt64_QuadPart} + // assigning to a local variable if UInt64 is not a native type, or if using + // a C++Builder version that has problems with UInt64 parameters... + h.QuadPart := UInt64(AValue); + h := GStack.HostToNetwork(h); + AValue := Int64(h.QuadPart); + {$ELSE} + AValue := Int64(GStack.HostToNetwork(UInt64(AValue))); + {$ENDIF} + {$ENDIF} + end; + Write(ToBytes(AValue)); +end; + +procedure TIdIOHandler.Write(AValue: TIdUInt64; AConvert: Boolean = True); +begin + if AConvert then begin + AValue := GStack.HostToNetwork(AValue); + end; + Write(ToBytes(AValue)); +end; + +procedure TIdIOHandler.Write(AValue: TStrings; AWriteLinesCount: Boolean = False; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + i: Integer; + LBufferingStarted: Boolean; +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ASrcEncoding := iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + LBufferingStarted := not WriteBufferingActive; + if LBufferingStarted then begin + WriteBufferOpen; + end; + try + if AWriteLinesCount then begin + Write(AValue.Count); + end; + for i := 0 to AValue.Count - 1 do begin + WriteLn(AValue.Strings[i], AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); + end; + if LBufferingStarted then begin + WriteBufferClose; + end; + except + if LBufferingStarted then begin + WriteBufferCancel; + end; + raise; + end; +end; + +procedure TIdIOHandler.Write(AValue: UInt16; AConvert: Boolean = True); +begin + if AConvert then begin + AValue := GStack.HostToNetwork(AValue); + end; + Write(ToBytes(AValue)); +end; + +procedure TIdIOHandler.Write(AValue: Int16; AConvert: Boolean = True); +begin + if AConvert then begin + AValue := Int16(GStack.HostToNetwork(UInt16(AValue))); + end; + Write(ToBytes(AValue)); +end; + +function TIdIOHandler.ReadString(ABytes: Integer; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LBytes: TIdBytes; +begin + if ABytes > 0 then begin + ReadBytes(LBytes, ABytes, False); + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + Result := BytesToString(LBytes, 0, ABytes, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + end else begin + Result := ''; + end; +end; + +procedure TIdIOHandler.ReadStrings(ADest: TStrings; AReadLinesCount: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + i: Integer; +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + if AReadLinesCount < 0 then begin + AReadLinesCount := ReadInt32; + end; + for i := 0 to AReadLinesCount - 1 do begin + ADest.Add(ReadLn(AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + )); + end; +end; + +function TIdIOHandler.ReadUInt16(AConvert: Boolean = True): UInt16; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, SizeOf(UInt16), False); + Result := BytesToUInt16(LBytes); + if AConvert then begin + Result := GStack.NetworkToHost(Result); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdIOHandler.ReadWord(AConvert: Boolean = True): UInt16; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadUInt16(AConvert); +end; + +function TIdIOHandler.ReadInt16(AConvert: Boolean = True): Int16; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, SizeOf(Int16), False); + Result := BytesToInt16(LBytes); + if AConvert then begin + Result := Int16(GStack.NetworkToHost(UInt16(Result))); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdIOHandler.ReadSmallInt(AConvert: Boolean = True): Int16; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadInt16(AConvert); +end; + +function TIdIOHandler.ReadChar(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): Char; +var + I, J, NumChars, NumBytes: Integer; + LBytes: TIdBytes; + {$IFDEF DOTNET} + LChars: array[0..1] of Char; + {$ELSE} + LChars: TIdWideChars; + {$IFDEF STRING_IS_ANSI} + LWTmp: TIdUnicodeString; + LATmp: TIdBytes; + {$ENDIF} + {$ENDIF} +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + // 2 Chars to handle UTF-16 surrogates + NumBytes := AByteEncoding.GetMaxByteCount(2); + SetLength(LBytes, NumBytes); + {$IFNDEF DOTNET} + SetLength(LChars, 2); + {$ENDIF} + NumChars := 0; + if NumBytes > 0 then + begin + for I := 1 to NumBytes do + begin + LBytes[I-1] := ReadByte; + NumChars := AByteEncoding.GetChars(LBytes, 0, I, LChars, 0); + if NumChars > 0 then begin + // RLebeau 10/19/2012: when Indy switched to its own UTF-8 implementation + // to avoid the MB_ERR_INVALID_CHARS flag on Windows, it accidentally broke + // this loop! Since this is not commonly used, this was not noticed until + // now. On Windows at least, GetChars() now returns >0 for an invalid + // sequence, so we have to check if any of the returned characters are the + // Unicode U+FFFD character, indicating bad data... + for J := 0 to NumChars-1 do begin + if LChars[J] = TIdWideChar($FFFD) then begin + // keep reading... + NumChars := 0; + Break; + end; + end; + if NumChars > 0 then begin + Break; + end; + end; + end; + end; + {$IFDEF STRING_IS_UNICODE} + // RLebeau: if the bytes were decoded into surrogates, the second + // surrogate is lost here, as it can't be returned unless we cache + // it somewhere for the the next ReadChar() call to retreive. Just + // raise an error for now. Users will have to update their code to + // read surrogates differently... + Assert(NumChars = 1); + Result := LChars[0]; + {$ELSE} + // RLebeau: since we can only return an AnsiChar here, let's convert + // the decoded characters, surrogates and all, into their Ansi + // representation. This will have the same problem as above if the + // conversion results in a multibyte character sequence... + SetString(LWTmp, PWideChar(LChars), NumChars); + LATmp := ADestEncoding.GetBytes(LWTmp); // convert to Ansi + Assert(Length(LATmp) = 1); + Result := Char(LATmp[0]); + {$ENDIF} +end; + +function TIdIOHandler.ReadByte: Byte; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, 1, False); + Result := LBytes[0]; +end; + +function TIdIOHandler.ReadInt32(AConvert: Boolean): Int32; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, SizeOf(Int32), False); + Result := BytesToInt32(LBytes); + if AConvert then begin + Result := Int32(GStack.NetworkToHost(UInt32(Result))); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdIOHandler.ReadLongInt(AConvert: Boolean): Int32; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadInt32(AConvert); +end; + +function TIdIOHandler.ReadInt64(AConvert: boolean): Int64; +var + LBytes: TIdBytes; + {$IFDEF AVOID_URW_ERRORS} + h: Int64; + {$ELSE} + {$IFDEF HAS_TIdUInt64_QuadPart} + h: TIdUInt64; + {$ENDIF} + {$ENDIF} +begin + ReadBytes(LBytes, SizeOf(Int64), False); + Result := BytesToInt64(LBytes); + if AConvert then begin + {$IFDEF AVOID_URW_ERRORS} + // assigning to a local variable to avoid an "Internal error URW533" compiler + // error in Delphi 5, and an "Internal error URW699" compiler error in Delphi + // 6. Later versions seem OK without it... + h := GStack.NetworkToHost(UInt64(Result)); + Result := h; + {$ELSE} + {$IFDEF HAS_TIdUInt64_QuadPart} + // assigning to a local variable if UInt64 is not a native type, or if using + // a C++Builder version that has problems with UInt64 parameters... + h.QuadPart := UInt64(AValue); + h := GStack.NetworkToHost(h); + Result := Int64(h.QuadPart); + {$ELSE} + Result := Int64(GStack.NetworkToHost(UInt64(Result))); + {$ENDIF} + {$ENDIF} + end; +end; + +function TIdIOHandler.ReadUInt64(AConvert: boolean): TIdUInt64; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, SizeOf(TIdUInt64), False); + Result := BytesToUInt64(LBytes); + if AConvert then begin + Result := GStack.NetworkToHost(Result); + end; +end; + +function TIdIOHandler.ReadUInt32(AConvert: Boolean): UInt32; +var + LBytes: TIdBytes; +begin + ReadBytes(LBytes, SizeOf(UInt32), False); + Result := BytesToUInt32(LBytes); + if AConvert then begin + Result := GStack.NetworkToHost(Result); + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdIOHandler.ReadLongWord(AConvert: Boolean): UInt32; +{$I IdDeprecatedImplBugOn.inc} +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadUInt32(AConvert); +end; + +function TIdIOHandler.ReadLn(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadLn(LF, IdTimeoutDefault, -1, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +function TIdIOHandler.ReadLn(ATerminator: string; AByteEncoding: IIdTextEncoding + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadLn(ATerminator, IdTimeoutDefault, -1, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +function TIdIOHandler.ReadLn(ATerminator: string; ATimeout: Integer = IdTimeoutDefault; + AMaxLineLength: Integer = -1; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LInputBufferSize: Integer; + LStartPos: Integer; + LTermPos: Integer; + LReadLnStartTime: TIdTicks; + LTerm, LResult: TIdBytes; +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + if AMaxLineLength < 0 then begin + AMaxLineLength := MaxLineLength; + end; + // User may pass '' if they need to pass arguments beyond the first. + if ATerminator = '' then begin + ATerminator := LF; + end; + // TODO: encountered an email that was using charset "cp1026", which encodes + // a LF character to byte $25 instead of $0A (and decodes byte $0A to character + // #$8E instead of #$A). To account for that, don't encoding the LF using the + // specified encoding anymore, force the encoding to what it should be. But + // what if UTF-16 is being used? + { + if ATerminator = LF then begin + LTerm := ToBytes(Byte($0A)); + end else begin + LTerm := ToBytes(ATerminator, AByteEncoding + {$IFDEF STRING_IS_ANSI, ADestEncoding{$ENDIF + ); + end; + } + LTerm := ToBytes(ATerminator, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + FReadLnSplit := False; + FReadLnTimedOut := False; + LTermPos := -1; + LStartPos := 0; + LReadLnStartTime := Ticks64; + repeat + LInputBufferSize := FInputBuffer.Size; + if LInputBufferSize > 0 then begin + if LStartPos < LInputBufferSize then begin + LTermPos := FInputBuffer.IndexOf(LTerm, LStartPos); + end else begin + LTermPos := -1; + end; + LStartPos := IndyMax(LInputBufferSize-(Length(LTerm)-1), 0); + end; + // if the line length is limited and terminator is found after the limit or not found and the limit is exceeded + if (AMaxLineLength > 0) and ((LTermPos > AMaxLineLength) or ((LTermPos = -1) and (LStartPos > AMaxLineLength))) then begin + if MaxLineAction = maException then begin + raise EIdReadLnMaxLineLengthExceeded.Create(RSReadLnMaxLineLengthExceeded); + end; + // RLebeau: WARNING - if the line is using multibyte character sequences + // and a sequence staddles the AMaxLineLength boundary, this will chop + // the sequence, producing invalid data! + FReadLnSplit := True; + Result := FInputBuffer.ExtractToString(AMaxLineLength, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + Exit; + end + // ReadFromSource blocks - do not call unless we need to + else if LTermPos = -1 then begin + // ReadLn needs to call this as data may exist in the buffer, but no EOL yet disconnected + CheckForDisconnect(True, True); + // Can only return -1 if timeout + FReadLnTimedOut := ReadFromSource(True, ATimeout, False) = -1; + if (not FReadLnTimedOut) and (ATimeout >= 0) then begin + if GetElapsedTicks(LReadLnStartTime) >= UInt32(ATimeout) then begin + FReadLnTimedOut := True; + end; + end; + if FReadLnTimedOut then begin + Result := ''; + Exit; + end; + end; + until LTermPos > -1; + // Extract actual data + { + IMPORTANT!!! + + When encoding from UTF8 to Unicode or ASCII, you will not always get the same + number of bytes that you input so you may have to recalculate LTermPos since + that was based on the number of bytes in the input stream. If do not do this, + you will probably get an incorrect result or a range check error since the + string is shorter then the original buffer position. + + JPM + } + // RLebeau 11/19/08: this is no longer needed as the terminator is encoded to raw bytes now ... + { + Result := FInputBuffer.Extract(LTermPos + Length(ATerminator), AEncoding); + LTermPos := IndyMin(LTermPos, Length(Result)); + if (ATerminator = LF) and (LTermPos > 0) then begin + if Result[LTermPos] = CR then begin + Dec(LTermPos); + end; + end; + SetLength(Result, LTermPos); + } + FInputBuffer.ExtractToBytes(LResult, LTermPos + Length(LTerm)); + if (ATerminator = LF) and (LTermPos > 0) then begin + if LResult[LTermPos-1] = Ord(CR) then begin + Dec(LTermPos); + end; + end; + Result := BytesToString(LResult, 0, LTermPos, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +function TIdIOHandler.ReadLnRFC(var VMsgEnd: Boolean; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := ReadLnRFC(VMsgEnd, LF, '.', AByteEncoding {do not localize} + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +function TIdIOHandler.ReadLnRFC(var VMsgEnd: Boolean; const ALineTerminator: string; + const ADelim: String = '.'; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +begin + Result := ReadLn(ALineTerminator, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + // Do not use ATerminator since always ends with . (standard) + if Result = ADelim then + begin + VMsgEnd := True; + Exit; + end; + if TextStartsWith(Result, '..') then begin {do not localize} + Delete(Result, 1, 1); + end; + VMsgEnd := False; +end; + +function TIdIOHandler.ReadLnSplit(var AWasSplit: Boolean; ATerminator: string = LF; + ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + FOldAction: TIdMaxLineAction; +begin + FOldAction := MaxLineAction; + MaxLineAction := maSplit; + try + Result := ReadLn(ATerminator, ATimeout, AMaxLineLength, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + AWasSplit := FReadLnSplit; + finally + MaxLineAction := FOldAction; + end; +end; + +function TIdIOHandler.ReadLnWait(AFailCount: Integer = MaxInt; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LAttempts: Integer; +begin + // MtW: this is mostly used when empty lines could be sent. + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + Result := ''; + LAttempts := 0; + while LAttempts < AFailCount do + begin + Result := Trim(ReadLn(AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + )); + if Length(Result) > 0 then begin + Exit; + end; + if ReadLnTimedOut then begin + raise EIdReadTimeout.Create(RSReadTimeout); + end; + Inc(LAttempts); + end; + raise EIdReadLnWaitMaxAttemptsExceeded.Create(RSReadLnWaitMaxAttemptsExceeded); +end; + +function TIdIOHandler.ReadFromSource(ARaiseExceptionIfDisconnected: Boolean; + ATimeout: Integer; ARaiseExceptionOnTimeout: Boolean): Integer; +var + LByteCount: Integer; + LLastError: Integer; + LBuffer: TIdBytes; + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + if ATimeout = IdTimeoutDefault then begin + // MtW: check for 0 too, for compatibility + if (ReadTimeout = IdTimeoutDefault) or (ReadTimeout = 0) then begin + ATimeout := IdTimeoutInfinite; + end else begin + ATimeout := ReadTimeout; + end; + end; + Result := 0; + // Check here as this side may have closed the socket + CheckForDisconnect(ARaiseExceptionIfDisconnected); + if SourceIsAvailable then begin + repeat + LByteCount := 0; + if Readable(ATimeout) then begin + if Opened then begin + // No need to call AntiFreeze, the Readable does that. + if SourceIsAvailable then begin + // TODO: Whey are we reallocating LBuffer every time? This + // should be a one time operation per connection. + + // RLebeau: because the Intercept does not allow the buffer + // size to be specified, and the Intercept could potentially + // resize the buffer... + + SetLength(LBuffer, RecvBufferSize); + try + LByteCount := ReadDataFromSource(LBuffer); + if LByteCount > 0 then begin + SetLength(LBuffer, LByteCount); + + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Receive(LBuffer); + {$IFDEF USE_OBJECT_ARC}LIntercept := nil;{$ENDIF} + LByteCount := Length(LBuffer); + end; + + // Pass through LBuffer first so it can go through Intercept + //TODO: If not intercept, we can skip this step + InputBuffer.Write(LBuffer); + end; + finally + LBuffer := nil; + end; + end + else if ARaiseExceptionIfDisconnected then begin + raise EIdClosedSocket.Create(RSStatusDisconnected); + end; + end + else if ARaiseExceptionIfDisconnected then begin + raise EIdNotConnected.Create(RSNotConnected); + end; + if LByteCount < 0 then + begin + LLastError := CheckForError(LByteCount); + if LLastError = Id_WSAETIMEDOUT then begin + // Timeout + if ARaiseExceptionOnTimeout then begin + raise EIdReadTimeout.Create(RSReadTimeout); + end; + Result := -1; + Break; + end; + FClosedGracefully := True; + Close; + // Do not raise unless all data has been read by the user + if InputBufferIsEmpty and ARaiseExceptionIfDisconnected then begin + RaiseError(LLastError); + end; + LByteCount := 0; + end + else if LByteCount = 0 then begin + FClosedGracefully := True; + end; + // Check here as other side may have closed connection + CheckForDisconnect(ARaiseExceptionIfDisconnected); + Result := LByteCount; + end else begin + // Timeout + if ARaiseExceptionOnTimeout then begin + raise EIdReadTimeout.Create(RSReadTimeout); + end; + Result := -1; + Break; + end; + until (LByteCount <> 0) or (not SourceIsAvailable); + end + else if ARaiseExceptionIfDisconnected then begin + raise EIdNotConnected.Create(RSNotConnected); + end; +end; + +function TIdIOHandler.CheckForDataOnSource(ATimeout: Integer = 0): Boolean; +var + LPrevSize: Integer; +begin + Result := False; + // RLebeau - Connected() might read data into the InputBuffer, thus + // leaving no data for ReadFromSource() to receive a second time, + // causing a result of False when it should be True instead. So we + // save the current size of the InputBuffer before calling Connected() + // and then compare it afterwards.... + LPrevSize := InputBuffer.Size; + if Connected then begin + // return whether at least 1 byte was received + Result := (InputBuffer.Size > LPrevSize) or (ReadFromSource(False, ATimeout, False) > 0); + end; +end; + +procedure TIdIOHandler.Write(AStream: TStream; ASize: TIdStreamSize = 0; + AWriteByteCount: Boolean = FALSE); +var + LBuffer: TIdBytes; + LStreamPos: TIdStreamSize; + LBufSize: Integer; + // LBufferingStarted: Boolean; +begin + if ASize < 0 then begin //"-1" All from current position + LStreamPos := AStream.Position; + ASize := AStream.Size - LStreamPos; + //todo is this step required? + AStream.Position := LStreamPos; + end + else if ASize = 0 then begin //"0" ALL + ASize := AStream.Size; + AStream.Position := 0; + end; + //else ">0" number of bytes + + // RLebeau 3/19/2006: DO NOT ENABLE WRITE BUFFERING IN THIS METHOD! + // + // When sending large streams, especially with LargeStream enabled, + // this can easily cause "Out of Memory" errors. It is the caller's + // responsibility to enable/disable write buffering as needed before + // calling one of the Write() methods. + // + // Also, forcing write buffering in this method is having major + // impacts on TIdFTP, TIdFTPServer, and TIdHTTPServer. + + if AWriteByteCount then begin + if LargeStream then begin + Write(Int64(ASize)); + end else begin + {$IFDEF STREAM_SIZE_64} + if ASize > High(Integer) then begin + raise EIdIOHandlerRequiresLargeStream.Create(RSRequiresLargeStream); + end; + {$ENDIF} + Write(Int32(ASize)); + end; + end; + + BeginWork(wmWrite, ASize); + try + SetLength(LBuffer, FSendBufferSize); + while ASize > 0 do begin + LBufSize := IndyMin(ASize, Length(LBuffer)); + // Do not use ReadBuffer. Some source streams are real time and will not + // return as much data as we request. Kind of like recv() + // NOTE: We use .Size - size must be supported even if real time + LBufSize := TIdStreamHelper.ReadBytes(AStream, LBuffer, LBufSize); + if LBufSize <= 0 then begin + raise EIdNoDataToRead.Create(RSIdNoDataToRead); + end; + Write(LBuffer, LBufSize); + // RLebeau: DoWork() is called in WriteDirect() + //DoWork(wmWrite, LBufSize); + Dec(ASize, LBufSize); + end; + finally + EndWork(wmWrite); + LBuffer := nil; + end; +end; + +procedure TIdIOHandler.ReadBytes(var VBuffer: TIdBytes; AByteCount: Integer; AAppend: Boolean = True); +begin + Assert(FInputBuffer<>nil); + if AByteCount > 0 then begin + // Read from stack until we have enough data + while FInputBuffer.Size < AByteCount do begin + // RLebeau: in case the other party disconnects + // after all of the bytes were transmitted ok. + // No need to throw an exception just yet... + if ReadFromSource(False) > 0 then begin + if FInputBuffer.Size >= AByteCount then begin + Break; // we have enough data now + end; + end; + CheckForDisconnect(True, True); + end; + FInputBuffer.ExtractToBytes(VBuffer, AByteCount, AAppend); + end else if AByteCount < 0 then begin + ReadFromSource(False, ReadTimeout, False); + CheckForDisconnect(True, True); + FInputBuffer.ExtractToBytes(VBuffer, -1, AAppend); + end; +end; + +procedure TIdIOHandler.WriteLn(AEncoding: IIdTextEncoding = nil); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + {$IFNDEF VCL_6_OR_ABOVE} + // RLebeau: in Delphi 5, explicitly specifying the nil value for the third + // parameter causes a "There is no overloaded version of 'WriteLn' that can + // be called with these arguments" compiler error. Must be a compiler bug, + // because it compiles fine in Delphi 6. The parameter value is nil by default + // anyway, so we don't really need to specify it here at all, but I'm documenting + // this so we know for future reference... + // + WriteLn('', AEncoding); + {$ELSE} + WriteLn('', AEncoding{$IFDEF STRING_IS_ANSI}, nil{$ENDIF}); + {$ENDIF} +end; + +procedure TIdIOHandler.WriteLn(const AOut: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + // TODO: RLebeau 1/2/2015: encountered an email that was using charset "cp1026", + // which encodes a LF character to byte $25 instead of $0A (and decodes + // byte $0A to character #$8E instead of #$A). To account for that, don't + // encoding the CRLF using the specified encoding anymore, force the encoding + // to what it should be... + // + // But, what to do if the target encoding is UTF-16? + { + Write(AOut, AByteEncoding{$IFDEF STRING_IS_ANSI, ASrcEncoding{$ENDIF); + Write(EOL, Indy8BitEncoding{$IFDEF STRING_IS_ANSI, Indy8BitEncoding{$ENDIF); + } + + // Do as one write so it only makes one call to network + Write(AOut + EOL, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.WriteLnRFC(const AOut: string = ''; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + if TextStartsWith(AOut, '.') then begin {do not localize} + WriteLn('.' + AOut, AByteEncoding {do not localize} + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); + end else begin + WriteLn(AOut, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); + end; +end; + +function TIdIOHandler.Readable(AMSec: Integer): Boolean; +begin + // In case descendant does not override this or other methods but implements the higher level + // methods + Result := False; +end; + +procedure TIdIOHandler.SetHost(const AValue: string); +begin + FHost := AValue; +end; + +procedure TIdIOHandler.SetPort(AValue: Integer); +begin + FPort := AValue; +end; + +function TIdIOHandler.Connected: Boolean; +begin + CheckForDisconnect(False); + Result := + ( + ( + // Set when closed properly. Reflects actual socket state. + (not ClosedGracefully) + // Created on Open. Prior to Open ClosedGracefully is still false. + and (FInputBuffer <> nil) + ) + // Buffer must be empty. Even if closed, we are "connected" if we still have + // data + or (not InputBufferIsEmpty) + ) + and Opened; +end; + +// TODO: move this into IdGlobal.pas +procedure AdjustStreamSize(const AStream: TStream; const ASize: TIdStreamSize); +var + LStreamPos: TIdStreamSize; +begin + LStreamPos := AStream.Position; + AStream.Size := ASize; + // Must reset to original value in cases where size changes position + if AStream.Position <> LStreamPos then begin + AStream.Position := LStreamPos; + end; +end; + +procedure TIdIOHandler.ReadStream(AStream: TStream; AByteCount: TIdStreamSize; + AReadUntilDisconnect: Boolean); +var + i: Integer; + LBuf: TIdBytes; + LByteCount, LPos: TIdStreamSize; + {$IFNDEF STREAM_SIZE_64} + LTmp: Int64; + {$ENDIF} +const + cSizeUnknown = -1; +begin + Assert(AStream<>nil); + + if (AByteCount = cSizeUnknown) and (not AReadUntilDisconnect) then begin + // Read size from connection + if LargeStream then begin + {$IFDEF STREAM_SIZE_64} + LByteCount := ReadInt64; + {$ELSE} + LTmp := ReadInt64; + if LTmp > MaxInt then begin + raise EIdIOHandlerStreamDataTooLarge.Create(RSDataTooLarge); + end; + LByteCount := TIdStreamSize(LTmp); + {$ENDIF} + end else begin + LByteCount := ReadInt32; + end; + end else begin + LByteCount := AByteCount; + end; + + // Presize stream if we know the size - this reduces memory/disk allocations to one time + // Have an option for this? user might not want to presize, eg for int64 files + if LByteCount > -1 then begin + LPos := AStream.Position; + if (High(TIdStreamSize) - LPos) < LByteCount then begin + raise EIdIOHandlerStreamDataTooLarge.Create(RSDataTooLarge); + end; + AdjustStreamSize(AStream, LPos + LByteCount); + end; + + if (LByteCount <= cSizeUnknown) and (not AReadUntilDisconnect) then begin + AReadUntilDisconnect := True; + end; + + if AReadUntilDisconnect then begin + BeginWork(wmRead); + end else begin + BeginWork(wmRead, LByteCount); + end; + + try + // If data already exists in the buffer, write it out first. + // should this loop for all data in buffer up to workcount? not just one block? + if FInputBuffer.Size > 0 then begin + if AReadUntilDisconnect then begin + i := FInputBuffer.Size; + end else begin + i := IndyMin(FInputBuffer.Size, LByteCount); + Dec(LByteCount, i); + end; + FInputBuffer.ExtractToStream(AStream, i); + end; + + // RLebeau - don't call Connected() here! ReadBytes() already + // does that internally. Calling Connected() here can cause an + // EIdConnClosedGracefully exception that breaks the loop + // prematurely and thus leave unread bytes in the InputBuffer. + // Let the loop catch the exception before exiting... + + SetLength(LBuf, RecvBufferSize); // preallocate the buffer + repeat + if AReadUntilDisconnect then begin + i := Length(LBuf); + end else begin + i := IndyMin(LByteCount, Length(LBuf)); + if i < 1 then begin + Break; + end; + end; + //TODO: Improve this - dont like the use of the exception handler + //DONE -oAPR: Dont use a string, use a memory buffer or better yet the buffer itself. + try + try + ReadBytes(LBuf, i, False); + except + on E: Exception do begin + // RLebeau - ReadFromSource() inside of ReadBytes() + // could have filled the InputBuffer with more bytes + // than actually requested, so don't extract too + // many bytes here... + i := IndyMin(i, FInputBuffer.Size); + FInputBuffer.ExtractToBytes(LBuf, i, False); + if AReadUntilDisconnect then begin + if E is EIdConnClosedGracefully then begin + Exit; + end + else if E is EIdSocketError then begin + case EIdSocketError(E).LastError of + Id_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET: begin + Exit; + end; + end; + end; + end; + raise; + end; + end; + TIdAntiFreezeBase.DoProcess; + finally + if i > 0 then begin + TIdStreamHelper.Write(AStream, LBuf, i); + if not AReadUntilDisconnect then begin + Dec(LByteCount, i); + end; + end; + end; + until False; + finally + EndWork(wmRead); + if AStream.Size > AStream.Position then begin + AStream.Size := AStream.Position; + end; + LBuf := nil; + end; +end; + +procedure TIdIOHandler.Discard(AByteCount: Int64); +var + LSize: Integer; +begin + Assert(AByteCount >= 0); + if AByteCount > 0 then + begin + BeginWork(wmRead, AByteCount); + try + repeat + LSize := iif(AByteCount < MaxInt, Integer(AByteCount), MaxInt); + LSize := IndyMin(LSize, FInputBuffer.Size); + if LSize > 0 then begin + FInputBuffer.Remove(LSize); + Dec(AByteCount, LSize); + if AByteCount < 1 then begin + Break; + end; + end; + // RLebeau: in case the other party disconnects + // after all of the bytes were transmitted ok. + // No need to throw an exception just yet... + if ReadFromSource(False) < 1 then begin + CheckForDisconnect(True, True); + end; + until False; + finally + EndWork(wmRead); + end; + end; +end; + +procedure TIdIOHandler.DiscardAll; +begin + BeginWork(wmRead); + try + // If data already exists in the buffer, discard it first. + FInputBuffer.Clear; + // RLebeau - don't call Connected() here! ReadBytes() already + // does that internally. Calling Connected() here can cause an + // EIdConnClosedGracefully exception that breaks the loop + // prematurely and thus leave unread bytes in the InputBuffer. + // Let the loop catch the exception before exiting... + repeat + //TODO: Improve this - dont like the use of the exception handler + try + if ReadFromSource(False) > 0 then begin + FInputBuffer.Clear; + end else begin; + CheckForDisconnect(True, True); + end; + except + on E: Exception do begin + // RLebeau - ReadFromSource() could have filled the + // InputBuffer with more bytes... + FInputBuffer.Clear; + if E is EIdConnClosedGracefully then begin + Break; + end else begin + raise; + end; + end; + end; + TIdAntiFreezeBase.DoProcess; + until False; + finally + EndWork(wmRead); + end; +end; + +procedure TIdIOHandler.RaiseConnClosedGracefully; +begin + (* ************************************************************* // + ------ If you receive an exception here, please read. ---------- + + If this is a SERVER + ------------------- + The client has disconnected the socket normally and this exception is used to notify the + server handling code. This exception is normal and will only happen from within the IDE, not + while your program is running as an EXE. If you do not want to see this, add this exception + or EIdSilentException to the IDE options as exceptions not to break on. + + From the IDE just hit F9 again and Indy will catch and handle the exception. + + Please see the FAQ and help file for possible further information. + The FAQ is at http://www.nevrona.com/Indy/FAQ.html + + If this is a CLIENT + ------------------- + The server side of this connection has disconnected normaly but your client has attempted + to read or write to the connection. You should trap this error using a try..except. + Please see the help file for possible further information. + + // ************************************************************* *) + raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully); +end; + +function TIdIOHandler.InputBufferAsString(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + Result := FInputBuffer.ExtractToString(FInputBuffer.Size, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +function TIdIOHandler.AllData(AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LBytes: Integer; +begin + Result := ''; + BeginWork(wmRead); + try + if Connected then + begin + try + try + repeat + LBytes := ReadFromSource(False, 250, False); + until LBytes = 0; // -1 on timeout + finally + if not InputBufferIsEmpty then begin + Result := InputBufferAsString(AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + end; + end; + except end; + end; + finally + EndWork(wmRead); + end; +end; + +procedure TIdIOHandler.PerformCapture(const ADest: TObject; + out VLineCount: Integer; const ADelim: string; + AUsesDotTransparency: Boolean; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + s: string; + LStream: TStream; + LStrings: TStrings; +begin + VLineCount := 0; + + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ADestEncoding := iif(ADestEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + + LStream := nil; + LStrings := nil; + + if ADest is TStrings then begin + LStrings := TStrings(ADest); + end + else if ADest is TStream then begin + LStream := TStream(ADest); + end + else begin + raise EIdObjectTypeNotSupported.Create(RSObjectTypeNotSupported); + end; + + BeginWork(wmRead); + try + repeat + s := ReadLn(AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + if s = ADelim then begin + Exit; + end; + // S.G. 6/4/2004: All the consumers to protect themselves against memory allocation attacks + if FMaxCapturedLines > 0 then begin + if VLineCount > FMaxCapturedLines then begin + raise EIdMaxCaptureLineExceeded.Create(RSMaximumNumberOfCaptureLineExceeded); + end; + end; + // For RFC retrieves that use dot transparency + // No length check necessary, if only one byte it will be byte x + #0. + if AUsesDotTransparency then begin + if TextStartsWith(s, '..') then begin + Delete(s, 1, 1); + end; + end; + // Write to output + Inc(VLineCount); + if LStrings <> nil then begin + LStrings.Add(s); + end + else if LStream <> nil then begin + WriteStringToStream(LStream, s+EOL, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); + end; + until False; + finally + EndWork(wmRead); + end; +end; + +function TIdIOHandler.InputLn(const AMask: String = ''; AEcho: Boolean = True; + ATabWidth: Integer = 8; AMaxLineLength: Integer = -1; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; AAnsiEncoding: IIdTextEncoding = nil{$ENDIF} + ): String; +var + i: Integer; + LChar: Char; + LTmp: string; +begin + Result := ''; + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + AAnsiEncoding := iif(AAnsiEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + if AMaxLineLength < 0 then begin + AMaxLineLength := MaxLineLength; + end; + repeat + LChar := ReadChar(AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + i := Length(Result); + if i <= AMaxLineLength then begin + case LChar of + BACKSPACE: + begin + if i > 0 then begin + SetLength(Result, i - 1); + if AEcho then begin + Write(BACKSPACE + ' ' + BACKSPACE, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end; + end; + end; + TAB: + begin + if ATabWidth > 0 then begin + i := ATabWidth - (i mod ATabWidth); + LTmp := StringOfChar(' ', i); + Result := Result + LTmp; + if AEcho then begin + Write(LTmp, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end; + end else begin + Result := Result + LChar; + if AEcho then begin + Write(LChar, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end; + end; + end; + LF: ; + CR: ; + #27: ; //ESC - currently not supported + else + Result := Result + LChar; + if AEcho then begin + if Length(AMask) = 0 then begin + Write(LChar, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end else begin + Write(AMask, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end; + end; + end; + end; + until LChar = LF; + // Remove CR trail + i := Length(Result); + while (i > 0) and CharIsInSet(Result, i, EOL) do begin + Dec(i); + end; + SetLength(Result, i); + if AEcho then begin + WriteLn(AByteEncoding); + end; +end; + +//TODO: Add a time out (default to infinite) and event to pass data +//TODO: Add a max size argument as well. +//TODO: Add a case insensitive option +function TIdIOHandler.WaitFor(const AString: string; ARemoveFromBuffer: Boolean = True; + AInclusive: Boolean = False; AByteEncoding: IIdTextEncoding = nil; + ATimeout: Integer = IdTimeoutDefault + {$IFDEF STRING_IS_ANSI}; AAnsiEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LBytes: TIdBytes; + LPos: Integer; +begin + Result := ''; + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + AAnsiEncoding := iif(AAnsiEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + LBytes := ToBytes(AString, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + LPos := 0; + repeat + LPos := InputBuffer.IndexOf(LBytes, LPos); + if LPos <> -1 then begin + if ARemoveFromBuffer and AInclusive then begin + Result := InputBuffer.ExtractToString(LPos+Length(LBytes), AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + end else begin + Result := InputBuffer.ExtractToString(LPos, AByteEncoding + {$IFDEF STRING_IS_ANSI}, AAnsiEncoding{$ENDIF} + ); + if ARemoveFromBuffer then begin + InputBuffer.Remove(Length(LBytes)); + end; + if AInclusive then begin + Result := Result + AString; + end; + end; + Exit; + end; + LPos := IndyMax(0, InputBuffer.Size - (Length(LBytes)-1)); + ReadFromSource(True, ATimeout, True); + until False; +end; + +procedure TIdIOHandler.Capture(ADest: TStream; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Capture(ADest, '.', True, AByteEncoding {do not localize} + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.Capture(ADest: TStream; out VLineCount: Integer; + const ADelim: string = '.'; AUsesDotTransparency: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + PerformCapture(ADest, VLineCount, ADelim, AUsesDotTransparency, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.Capture(ADest: TStream; ADelim: string; + AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LLineCount: Integer; +begin + PerformCapture(ADest, LLineCount, '.', AUsesDotTransparency, AByteEncoding {do not localize} + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.Capture(ADest: TStrings; out VLineCount: Integer; + const ADelim: string = '.'; AUsesDotTransparency: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + PerformCapture(ADest, VLineCount, ADelim, AUsesDotTransparency, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.Capture(ADest: TStrings; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LLineCount: Integer; +begin + PerformCapture(ADest, LLineCount, '.', True, AByteEncoding {do not localize} + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.Capture(ADest: TStrings; const ADelim: string; + AUsesDotTransparency: Boolean = True; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LLineCount: Integer; +begin + PerformCapture(ADest, LLineCount, ADelim, AUsesDotTransparency, AByteEncoding + {$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF} + ); +end; + +procedure TIdIOHandler.InputBufferToStream(AStream: TStream; AByteCount: Integer = -1); +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + FInputBuffer.ExtractToStream(AStream, AByteCount); +end; + +function TIdIOHandler.InputBufferIsEmpty: Boolean; +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := FInputBuffer.Size = 0; +end; + +procedure TIdIOHandler.Write(const ABuffer: TIdBytes; const ALength: Integer = -1; + const AOffset: Integer = 0); +var + LLength: Integer; +begin + LLength := IndyLength(ABuffer, ALength, AOffset); + if LLength > 0 then begin + if FWriteBuffer = nil then begin + WriteDirect(ABuffer, LLength, AOffset); + end else begin + // Write Buffering is enabled + FWriteBuffer.Write(ABuffer, LLength, AOffset); + if (FWriteBuffer.Size >= WriteBufferThreshold) and (WriteBufferThreshold > 0) then begin + repeat + WriteBufferFlush(WriteBufferThreshold); + until FWriteBuffer.Size < WriteBufferThreshold; + end; + end; + end; +end; + +procedure TIdIOHandler.WriteRFCStrings(AStrings: TStrings; AWriteTerminator: Boolean = True; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + i: Integer; +begin + AByteEncoding := iif(AByteEncoding, FDefStringEncoding); + {$IFDEF STRING_IS_ANSI} + ASrcEncoding := iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault); + {$ENDIF} + for i := 0 to AStrings.Count - 1 do begin + WriteLnRFC(AStrings[i], AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); + end; + if AWriteTerminator then begin + WriteLn('.', AByteEncoding + {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF} + ); + end; +end; + +function TIdIOHandler.WriteFile(const AFile: String; AEnableTransferFile: Boolean): Int64; +var +//TODO: There is a way in linux to dump a file to a socket as well. use it. + LStream: TStream; + {$IFDEF WIN32_OR_WIN64} + LOldErrorMode : Integer; + {$ENDIF} +begin + Result := 0; + {$IFDEF WIN32_OR_WIN64} + LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); + try + {$ENDIF} + if not FileExists(AFile) then begin + raise EIdFileNotFound.CreateFmt(RSFileNotFound, [AFile]); + end; + LStream := TIdReadFileExclusiveStream.Create(AFile); + try + Write(LStream); + Result := LStream.Size; + finally + FreeAndNil(LStream); + end; + {$IFDEF WIN32_OR_WIN64} + finally + SetErrorMode(LOldErrorMode) + end; + {$ENDIF} +end; + +function TIdIOHandler.WriteBufferingActive: Boolean; +{$IFDEF USE_CLASSINLINE}inline;{$ENDIF} +begin + Result := FWriteBuffer <> nil; +end; + +procedure TIdIOHandler.CloseGracefully; +begin + FClosedGracefully := True +end; + +procedure TIdIOHandler.InterceptReceive(var VBuffer: TIdBytes); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Receive(VBuffer); + end; +end; + +procedure TIdIOHandler.InitComponent; +begin + inherited InitComponent; + FRecvBufferSize := GRecvBufferSizeDefault; + FSendBufferSize := GSendBufferSizeDefault; + FMaxLineLength := IdMaxLineLengthDefault; + FMaxCapturedLines := Id_IOHandler_MaxCapturedLines; + FLargeStream := False; + FReadTimeOut := IdTimeoutDefault; + FInputBuffer := TIdBuffer.Create(BufferRemoveNotify); + FDefStringEncoding := IndyTextEncoding_ASCII; + {$IFDEF STRING_IS_ANSI} + FDefAnsiEncoding := IndyTextEncoding_OSDefault; + {$ENDIF} +end; + +procedure TIdIOHandler.WriteBufferFlush; +begin + WriteBufferFlush(-1); +end; + +procedure TIdIOHandler.WriteBufferOpen; +begin + WriteBufferOpen(-1); +end; + +procedure TIdIOHandler.WriteDirect(const ABuffer: TIdBytes; const ALength: Integer = -1; + const AOffset: Integer = 0); +var + LTemp: TIdBytes; + LPos: Integer; + LSize: Integer; + LByteCount: Integer; + LLastError: Integer; + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + // Check if disconnected + CheckForDisconnect(True, True); + + LIntercept := Intercept; + if LIntercept <> nil then begin + // TODO: pass offset/size parameters to the Intercept + // so that a copy is no longer needed here + LTemp := ToBytes(ABuffer, ALength, AOffset); + LIntercept.Send(LTemp); + {$IFDEF USE_OBJECT_ARC}LIntercept := nil;{$ENDIF} + LSize := Length(LTemp); + LPos := 0; + end else begin + LTemp := ABuffer; + LSize := IndyLength(LTemp, ALength, AOffset); + LPos := AOffset; + end; + while LSize > 0 do + begin + LByteCount := WriteDataToTarget(LTemp, LPos, LSize); + if LByteCount < 0 then + begin + LLastError := CheckForError(LByteCount); + if LLastError <> Id_WSAETIMEDOUT then begin + FClosedGracefully := True; + Close; + end; + RaiseError(LLastError); + end; + // TODO - Have a AntiFreeze param which allows the send to be split up so that process + // can be called more. Maybe a prop of the connection, MaxSendSize? + TIdAntiFreezeBase.DoProcess(False); + if LByteCount = 0 then begin + FClosedGracefully := True; + end; + // Check if other side disconnected + CheckForDisconnect; + DoWork(wmWrite, LByteCount); + Inc(LPos, LByteCount); + Dec(LSize, LByteCount); + end; +end; + +initialization + +finalization + FreeAndNil(GIOHandlerClassList) +end. diff --git a/indy/Core/IdIOHandlerSocket.pas b/indy/Core/IdIOHandlerSocket.pas new file mode 100644 index 0000000..409da9a --- /dev/null +++ b/indy/Core/IdIOHandlerSocket.pas @@ -0,0 +1,575 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.38 11/10/2004 8:25:54 AM JPMugaas + Fix for AV caused by short-circut boolean evaluation. + + Rev 1.37 27.08.2004 21:58:20 Andreas Hausladen + Speed optimization ("const" for string parameters) + + Rev 1.36 8/2/04 5:44:40 PM RLebeau + Moved ConnectTimeout over from TIdIOHandlerStack + + Rev 1.35 7/21/2004 12:22:32 PM BGooijen + Fix to .connected + + Rev 1.34 6/30/2004 12:31:34 PM BGooijen + Added OnSocketAllocated + + Rev 1.33 4/24/04 12:52:52 PM RLebeau + Added setter method to UseNagle property + + Rev 1.32 2004.04.18 12:52:02 AM czhower + Big bug fix with server disconnect and several other bug fixed that I found + along the way. + + Rev 1.31 2004.02.03 4:16:46 PM czhower + For unit name changes. + + Rev 1.30 2/2/2004 11:46:46 AM BGooijen + Dotnet and TransparentProxy + + Rev 1.29 2/1/2004 9:44:00 PM JPMugaas + Start on reenabling Transparant proxy. + + Rev 1.28 2004.01.20 10:03:28 PM czhower + InitComponent + + Rev 1.27 1/2/2004 12:02:16 AM BGooijen + added OnBeforeBind/OnAfterBind + + Rev 1.26 12/31/2003 9:51:56 PM BGooijen + Added IPv6 support + + Rev 1.25 11/4/2003 10:37:40 PM BGooijen + JP's patch to fix the bound port + + Rev 1.24 10/19/2003 5:21:26 PM BGooijen + SetSocketOption + + Rev 1.23 10/18/2003 1:44:06 PM BGooijen + Added include + + Rev 1.22 2003.10.14 1:26:54 PM czhower + Uupdates + Intercept support + + Rev 1.21 10/9/2003 8:09:06 PM SPerry + bug fixes + + Rev 1.20 8/10/2003 2:05:50 PM SGrobety + Dotnet + + Rev 1.19 2003.10.07 10:18:26 PM czhower + Uncommneted todo code that is now non dotnet. + + Rev 1.18 2003.10.02 8:23:42 PM czhower + DotNet Excludes + + Rev 1.17 2003.10.01 9:11:18 PM czhower + .Net + + Rev 1.16 2003.10.01 5:05:12 PM czhower + .Net + + Rev 1.15 2003.10.01 2:46:38 PM czhower + .Net + + Rev 1.14 2003.10.01 11:16:32 AM czhower + .Net + + Rev 1.13 2003.09.30 1:22:58 PM czhower + Stack split for DotNet + + Rev 1.12 7/4/2003 08:26:44 AM JPMugaas + Optimizations. + + Rev 1.11 7/1/2003 03:39:44 PM JPMugaas + Started numeric IP function API calls for more efficiency. + + Rev 1.10 2003.06.30 5:41:56 PM czhower + -Fixed AV that occurred sometimes when sockets were closed with chains + -Consolidated code that was marked by a todo for merging as it no longer + needed to be separate + -Removed some older code that was no longer necessary + + Passes bubble tests. + + Rev 1.9 6/3/2003 11:45:58 PM BGooijen + Added .Connected + + Rev 1.8 2003.04.22 7:45:34 PM czhower + + Rev 1.7 4/2/2003 3:24:56 PM BGooijen + Moved transparantproxy from ..stack to ..socket + + Rev 1.6 2/28/2003 9:51:56 PM BGooijen + removed the field: FReadTimeout: Integer, it hided the one in TIdIOHandler + + Rev 1.5 2/26/2003 1:15:38 PM BGooijen + FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack + + Rev 1.4 2003.02.25 1:36:08 AM czhower + + Rev 1.3 2002.12.07 12:26:26 AM czhower + + Rev 1.2 12-6-2002 20:09:14 BGooijen + Changed SetDestination to search for the last ':', instead of the first + + Rev 1.1 12-6-2002 18:54:14 BGooijen + Added IPv6-support + + Rev 1.0 11/13/2002 08:45:08 AM JPMugaas +} + +unit IdIOHandlerSocket; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdCustomTransparentProxy, + IdBaseComponent, + IdGlobal, + IdIOHandler, + IdSocketHandle; + +const + IdDefTimeout = 0; + IdBoundPortDefault = 0; + +type + { + TIdIOHandlerSocket is the base class for socket IOHandlers that implement a + binding. + + Descendants + -TIdIOHandlerStack + -TIdIOHandlerChain + } + TIdIOHandlerSocket = class(TIdIOHandler) + protected + FBinding: TIdSocketHandle; + FBoundIP: string; + FBoundPort: TIdPort; + FBoundPortMax: TIdPort; + FBoundPortMin: TIdPort; + FDefaultPort: TIdPort; + FOnBeforeBind: TNotifyEvent; + FOnAfterBind: TNotifyEvent; + FOnSocketAllocated: TNotifyEvent; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FTransparentProxy: TIdCustomTransparentProxy; + FImplicitTransparentProxy: Boolean; + FUseNagle: Boolean; + FReuseSocket: TIdReuseSocket; + FIPVersion: TIdIPVersion; + // + procedure ConnectClient; virtual; + procedure DoBeforeBind; virtual; + procedure DoAfterBind; virtual; + procedure DoSocketAllocated; virtual; + procedure InitComponent; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + function GetDestination: string; override; + procedure SetDestination(const AValue: string); override; + function GetReuseSocket: TIdReuseSocket; + procedure SetReuseSocket(AValue: TIdReuseSocket); + function GetTransparentProxy: TIdCustomTransparentProxy; virtual; + procedure SetTransparentProxy(AProxy: TIdCustomTransparentProxy); virtual; + function GetUseNagle: Boolean; + procedure SetUseNagle(AValue: Boolean); + // + function SourceIsAvailable: Boolean; override; + function CheckForError(ALastResult: Integer): Integer; override; + procedure RaiseError(AError: Integer); override; + public + procedure AfterAccept; override; + destructor Destroy; override; + function BindingAllocated: Boolean; + procedure Close; override; + function Connected: Boolean; override; + procedure Open; override; + function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; override; + // + property Binding: TIdSocketHandle read FBinding; + property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax; + property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin; + // events + property OnBeforeBind: TNotifyEvent read FOnBeforeBind write FOnBeforeBind; + property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind; + property OnSocketAllocated: TNotifyEvent read FOnSocketAllocated write FOnSocketAllocated; + published + property BoundIP: string read FBoundIP write FBoundIP; + property BoundPort: TIdPort read FBoundPort write FBoundPort default IdBoundPortDefault; + property DefaultPort: TIdPort read FDefaultPort write FDefaultPort; + property IPVersion: TIdIPVersion read FIPVersion write FIPVersion default ID_DEFAULT_IP_VERSION; + property ReuseSocket: TIdReuseSocket read GetReuseSocket write SetReuseSocket default rsOSDependent; + property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy; + property UseNagle: boolean read GetUseNagle write SetUseNagle default True; + end; + +implementation + +uses + //facilitate inlining only. + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF WIN32_OR_WIN64 } + Windows, + {$ENDIF} + SysUtils, + IdStack, + IdStackConsts, + IdSocks; + +{ TIdIOHandlerSocket } + +procedure TIdIOHandlerSocket.AfterAccept; +begin + inherited AfterAccept; + FIPVersion := FBinding.IPVersion; +end; + +procedure TIdIOHandlerSocket.Close; +begin + if FBinding <> nil then begin + FBinding.CloseSocket; + end; + inherited Close; +end; + +procedure TIdIOHandlerSocket.ConnectClient; +var + LBinding: TIdSocketHandle; +begin + LBinding := Binding; + DoBeforeBind; + // Allocate the socket + LBinding.IPVersion := FIPVersion; + LBinding.AllocateSocket; + DoSocketAllocated; + // Bind the socket + if BoundIP <> '' then begin + LBinding.IP := BoundIP; + end; + LBinding.Port := BoundPort; + LBinding.ClientPortMin := BoundPortMin; + LBinding.ClientPortMax := BoundPortMax; + LBinding.ReuseSocket := FReuseSocket; + + // RLebeau 11/15/2014: Using the socket bind() function in a Mac OSX sandbox + // causes the Apple store to reject an app with the following error if it + // uses Indy client(s) and no Indy server(s): + // + // "This app uses one or more entitlements which do not have matching + // functionality within the app. Apps should have only the minimum set of + // entitlements necessary for the app to function properly. Please remove + // all entitlements that are not needed by your app and submit an updated + // binary for review, including the following: + // + // com.apple.security.network.server" + // + // Ideally, TIdSocketHandle.Bind() should not call TryBind() if the IP is + // blank and the Port, ClientPortMin, and ClientPortMax are all 0. However, + // TIdSocketHandle.Bind() is used for both clients and servers, and sometimes + // a server needs to bind to port 0 to get a random ephemeral port, which it + // can then report to clients. So lets do the check here instead, as this + // method is only used for clients... + + {$IFDEF DARWIN} + // TODO: remove the DARWIN check and just skip the Bind() on all platforms? + if (LBinding.IP <> '') or (LBinding.Port <> 0) or + ((LBinding.ClientPortMin <> 0) and (LBinding.ClientPortMax <> 0)) then + begin + LBinding.Bind; + end; + {$ELSE} + LBinding.Bind; + {$ENDIF} + + // Turn off Nagle if specified + LBinding.UseNagle := FUseNagle; + DoAfterBind; +end; + +function TIdIOHandlerSocket.Connected: Boolean; +begin + Result := (BindingAllocated and inherited Connected) or (not InputBufferIsEmpty); +end; + +destructor TIdIOHandlerSocket.Destroy; +begin + SetTransparentProxy(nil); + FreeAndNil(FBinding); + inherited Destroy; +end; + +procedure TIdIOHandlerSocket.DoBeforeBind; +begin + if Assigned(FOnBeforeBind) then begin + FOnBeforeBind(self); + end; +end; + +procedure TIdIOHandlerSocket.DoAfterBind; +begin + if Assigned(FOnAfterBind) then begin + FOnAfterBind(self); + end; +end; + +procedure TIdIOHandlerSocket.DoSocketAllocated; +begin + if Assigned(FOnSocketAllocated) then begin + FOnSocketAllocated(self); + end; +end; + +function TIdIOHandlerSocket.GetDestination: string; +begin + Result := Host; + if (Port <> DefaultPort) and (Port > 0) then begin + Result := Host + ':' + IntToStr(Port); + end; +end; + +procedure TIdIOHandlerSocket.Open; +begin + inherited Open; + + if not Assigned(FBinding) then begin + FBinding := TIdSocketHandle.Create(nil); + end else begin + FBinding.Reset(True); + end; + FBinding.ClientPortMin := BoundPortMin; + FBinding.ClientPortMax := BoundPortMax; + + //if the IOHandler is used to accept connections then port+host will be empty + if (Host <> '') and (Port > 0) then begin + ConnectClient; + end; +end; + +procedure TIdIOHandlerSocket.SetDestination(const AValue: string); +var + LPortStart: integer; +begin + // Bas Gooijen 06-Dec-2002: Changed to search the last ':', instead of the first: + LPortStart := LastDelimiter(':', AValue); + if LPortStart > 0 then begin + Host := Copy(AValue, 1, LPortStart-1); + Port := IndyStrToInt(Trim(Copy(AValue, LPortStart + 1, $FF)), DefaultPort); + end; +end; + +function TIdIOHandlerSocket.BindingAllocated: Boolean; +begin + Result := FBinding <> nil; + if Result then begin + Result := FBinding.HandleAllocated; + end; +end; + +function TIdIOHandlerSocket.WriteFile(const AFile: String; + AEnableTransferFile: Boolean): Int64; + {$IFDEF WIN32_OR_WIN64} +var + LOldErrorMode : Integer; + {$ENDIF} +begin + Result := 0; + {$IFDEF WIN32_OR_WIN64} + LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS); + try + {$ENDIF} + if FileExists(AFile) then begin + if Assigned(GServeFileProc) and (not WriteBufferingActive) + {and (Intercept = nil)} and AEnableTransferFile + then begin + Result := GServeFileProc(Binding.Handle, AFile); + Exit; + end + else + begin + Result := inherited WriteFile(AFile, AEnableTransferFile); + end; + end; + {$IFDEF WIN32_OR_WIN64} + finally + SetErrorMode(LOldErrorMode) + end; + {$ENDIF} +end; + +function TIdIOHandlerSocket.GetReuseSocket: TIdReuseSocket; +begin + if FBinding <> nil then begin + Result := FBinding.ReuseSocket; + end else begin + Result := FReuseSocket; + end; +end; + +procedure TIdIOHandlerSocket.SetReuseSocket(AValue: TIdReuseSocket); +begin + FReuseSocket := AValue; + if FBinding <> nil then begin + FBinding.ReuseSocket := AValue; + end; +end; + +procedure TIdIOHandlerSocket.SetTransparentProxy(AProxy : TIdCustomTransparentProxy); +var + LClass: TIdCustomTransparentProxyClass; + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + LTransparentProxy := FTransparentProxy; + + if LTransparentProxy <> AProxy then + begin + // All this is to preserve the compatibility with old version + // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object + // In the case when the ASocks points to an object with owner it is treated as component on form. + + // under ARC, all weak references to a freed object get nil'ed automatically + + if Assigned(AProxy) then begin + if not Assigned(AProxy.Owner) then begin + if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin + FTransparentProxy := nil; + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + end; + LClass := TIdCustomTransparentProxyClass(AProxy.ClassType); + if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end; + if not Assigned(LTransparentProxy) then begin + LTransparentProxy := LClass.Create(Self); + FTransparentProxy := LTransparentProxy; + FImplicitTransparentProxy := True; + end; + LTransparentProxy.Assign(AProxy); + end else begin + if Assigned(LTransparentProxy) then begin + if FImplicitTransparentProxy then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end else begin + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + end; + end; + FTransparentProxy := AProxy; + {$IFNDEF USE_OBJECT_ARC} + AProxy.FreeNotification(Self); + {$ENDIF} + end; + end + else if Assigned(LTransparentProxy) then begin + if FImplicitTransparentProxy then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end else begin + FTransparentProxy := nil; + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + end; + end; + end; +end; + +function TIdIOHandlerSocket.GetTransparentProxy: TIdCustomTransparentProxy; +var + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + LTransparentProxy := FTransparentProxy; + // Necessary at design time for Borland SOAP support + if LTransparentProxy = nil then begin + LTransparentProxy := TIdSocksInfo.Create(Self); //default + FTransparentProxy := LTransparentProxy; + FImplicitTransparentProxy := True; + end; + Result := LTransparentProxy; +end; + +function TIdIOHandlerSocket.GetUseNagle: Boolean; +begin + if FBinding <> nil then begin + Result := FBinding.UseNagle; + end else begin + Result := FUseNagle; + end; +end; + +procedure TIdIOHandlerSocket.SetUseNagle(AValue: Boolean); +begin + FUseNagle := AValue; + if FBinding <> nil then begin + FBinding.UseNagle := AValue; + end; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdIOHandlerSocket.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdIOHandlerSocket.InitComponent; +begin + inherited InitComponent; + FUseNagle := True; + FIPVersion := ID_DEFAULT_IP_VERSION; +end; + +function TIdIOHandlerSocket.SourceIsAvailable: Boolean; +begin + Result := BindingAllocated; +end; + +function TIdIOHandlerSocket.CheckForError(ALastResult: Integer): Integer; +begin + Result := GStack.CheckForSocketError(ALastResult, [Id_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET, Id_WSAETIMEDOUT]); +end; + +procedure TIdIOHandlerSocket.RaiseError(AError: Integer); +begin + GStack.RaiseSocketError(AError); +end; + +end. diff --git a/indy/Core/IdIOHandlerStack.pas b/indy/Core/IdIOHandlerStack.pas new file mode 100644 index 0000000..4c8aa12 --- /dev/null +++ b/indy/Core/IdIOHandlerStack.pas @@ -0,0 +1,442 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.53 3/10/05 3:23:16 PM RLebeau + Updated WriteDirect() to access the Intercept property directly. + + Rev 1.52 11/12/2004 11:30:16 AM JPMugaas + Expansions for IPv6. + + Rev 1.51 11/11/04 12:03:46 PM RLebeau + Updated DoConnectTimeout() to recognize IdTimeoutDefault + + Rev 1.50 6/18/04 1:06:58 PM RLebeau + Bug fix for ReadTimeout property + + Rev 1.49 5/4/2004 9:57:34 AM JPMugaas + Removed some old uncommented code and reenabled some TransparentProxy code + since it compile in DotNET. + + Rev 1.48 2004.04.18 12:52:02 AM czhower + Big bug fix with server disconnect and several other bug fixed that I found + along the way. + + Rev 1.47 2004.04.08 3:56:34 PM czhower + Fixed bug with Intercept byte count. Also removed Bytes from Buffer. + + Rev 1.46 2004.03.12 8:01:00 PM czhower + Exception update + + Rev 1.45 2004.03.07 11:48:42 AM czhower + Flushbuffer fix + other minor ones found + + Rev 1.44 2004.03.01 5:12:32 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.43 2/21/04 9:25:50 PM RLebeau + Fix for BBG #66 + + Added FLastSocketError member to TIdConnectThread + + Rev 1.42 2004.02.03 4:16:48 PM czhower + For unit name changes. + + Rev 1.41 12/31/2003 9:51:56 PM BGooijen + Added IPv6 support + + Rev 1.40 2003.12.28 1:05:58 PM czhower + .Net changes. + + Rev 1.39 11/21/2003 12:05:18 AM BGooijen + Terminated isn't public in TThread any more, made it public here now + + Rev 1.38 10/28/2003 9:15:44 PM BGooijen + .net + + Rev 1.37 10/18/2003 1:42:46 PM BGooijen + Added include + + Rev 1.36 2003.10.14 1:26:56 PM czhower + Uupdates + Intercept support + + Rev 1.35 2003.10.11 5:48:36 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.34 10/9/2003 8:09:10 PM SPerry + bug fixes + + Rev 1.33 10/5/2003 11:02:36 PM BGooijen + Write buffering + + Rev 1.32 05/10/2003 23:01:02 HHariri + Fix for connect problem when IP address specified as opposed to host + + Rev 1.31 2003.10.02 8:23:42 PM czhower + DotNet Excludes + + Rev 1.30 2003.10.02 10:16:28 AM czhower + .Net + + Rev 1.29 2003.10.01 9:11:18 PM czhower + .Net + + Rev 1.28 2003.10.01 5:05:14 PM czhower + .Net + + Rev 1.27 2003.10.01 2:46:38 PM czhower + .Net + + Rev 1.26 2003.10.01 2:30:38 PM czhower + .Net + + Rev 1.22 10/1/2003 12:14:14 AM BGooijen + DotNet: removing CheckForSocketError + + Rev 1.21 2003.10.01 1:37:34 AM czhower + .Net + + Rev 1.19 2003.09.30 1:22:58 PM czhower + Stack split for DotNet + + Rev 1.18 2003.07.14 1:57:22 PM czhower + -First set of IOCP fixes. + -Fixed a threadsafe problem with the stack class. + + Rev 1.17 2003.07.14 12:54:32 AM czhower + Fixed graceful close detection if it occurs after connect. + + Rev 1.16 2003.07.10 4:34:58 PM czhower + Fixed AV, added some new comments + + Rev 1.15 7/4/2003 08:26:46 AM JPMugaas + Optimizations. + + Rev 1.14 7/1/2003 03:39:48 PM JPMugaas + Started numeric IP function API calls for more efficiency. + + Rev 1.13 6/30/2003 10:25:18 AM BGooijen + removed unnecessary assignment to FRecvBuffer.Size + + Rev 1.12 6/29/2003 10:56:28 PM BGooijen + Removed .Memory from the buffer, and added some extra methods + + Rev 1.11 2003.06.25 4:28:32 PM czhower + Formatting and fixed a short circuit clause. + + Rev 1.10 6/3/2003 11:43:52 PM BGooijen + Elimintated some code + + Rev 1.9 4/16/2003 3:31:26 PM BGooijen + Removed InternalCheckForDisconnect, added .Connected + + Rev 1.8 4/14/2003 11:44:20 AM BGooijen + CheckForDisconnect calls ReadFromSource now + + Rev 1.7 4/2/2003 3:24:56 PM BGooijen + Moved transparantproxy from ..stack to ..socket + + Rev 1.6 3/5/2003 11:04:32 PM BGooijen + Fixed Intercept, but the part in WriteBuffer doesn't look really nice yet + + Rev 1.5 3/3/2003 11:31:58 PM BGooijen + fixed stack overflow in .CheckForDisconnect + + Rev 1.4 2/26/2003 1:15:40 PM BGooijen + FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack + + Rev 1.3 2003.02.25 1:36:12 AM czhower + + Rev 1.2 2002.12.06 11:49:34 PM czhower + + Rev 1.1 12-6-2002 20:10:18 BGooijen + Added IPv6-support + + Rev 1.0 11/13/2002 08:45:16 AM JPMugaas +} + +unit IdIOHandlerStack; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, IdSocketHandle, IdIOHandlerSocket, IdExceptionCore, IdStack, + SysUtils; + +type + TIdIOHandlerStack = class(TIdIOHandlerSocket) + protected + procedure ConnectClient; override; + function ReadDataFromSource(var VBuffer: TIdBytes): Integer; override; + function WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override; + public + procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True; + AIgnoreBuffer: Boolean = False); override; + function Connected: Boolean; override; + function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; override; + published + property ReadTimeout; + end; + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + IdAntiFreezeBase, IdResourceStringsCore, IdResourceStrings, IdStackConsts, IdException, + IdTCPConnection, IdComponent, IdIOHandler; + +type + TIdConnectThread = class(TThread) + protected + FBinding: TIdSocketHandle; + FLastSocketError: Integer; + FExceptionMessage: string; + FExceptionOccured: Boolean; + procedure Execute; override; + procedure DoTerminate; override; + public + constructor Create(ABinding: TIdSocketHandle); reintroduce; + property Terminated; + end; + +{ TIdIOHandlerStack } + +function TIdIOHandlerStack.Connected: Boolean; +begin + ReadFromSource(False, 0, False); + Result := inherited Connected; +end; + +procedure TIdIOHandlerStack.ConnectClient; + + procedure DoConnectTimeout(ATimeout: Integer); + var + LSleepTime: Integer; + LThread: TIdConnectThread; + begin + if ATimeout = IdTimeoutDefault then begin + ATimeout := IdTimeoutInfinite; + end; + LThread := TIdConnectThread.Create(Binding); + try + // IndySleep + if TIdAntiFreezeBase.ShouldUse then begin + LSleepTime := IndyMin(GAntiFreeze.IdleTimeOut, 125); + end else begin + LSleepTime := 125; + end; + + if ATimeout = IdTimeoutInfinite then begin + while not LThread.Terminated do begin + IndySleep(LSleepTime); + TIdAntiFreezeBase.DoProcess; + end; + end else + begin + // TODO: we need to take the actual clock into account, not just + // decrement by the sleep interval. If IndySleep() runs longer then + // requested, that would slow down the loop and exceed the original + // timeout that was requested... + while (ATimeout > 0) and (not LThread.Terminated) do begin + IndySleep(IndyMin(ATimeout, LSleepTime)); + TIdAntiFreezeBase.DoProcess; + Dec(ATimeout, IndyMin(ATimeout, LSleepTime)); + end; + end; + + if LThread.Terminated then begin + if LThread.FExceptionOccured then begin + // TODO: acquire the actual Exception object from TIdConnectThread and re-raise it here + if LThread.FLastSocketError <> 0 then begin + raise EIdSocketError.CreateError(LThread.FLastSocketError, LThread.FExceptionMessage); + end; + raise EIdConnectException.Create(LThread.FExceptionMessage); + end; + end else begin + LThread.Terminate; + Close; + LThread.WaitFor; + raise EIdConnectTimeout.Create(RSConnectTimeout); + end; + finally + LThread.Free; + end; + end; + +var + LHost: String; + LPort: Integer; + LIP: string; + LIPVersion : TIdIPVersion; +begin + inherited ConnectClient; + if Assigned(FTransparentProxy) then begin + if FTransparentProxy.Enabled then begin + LHost := FTransparentProxy.Host; + LPort := FTransparentProxy.Port; + LIPVersion := FTransparentProxy.IPVersion; + end else begin + LHost := Host; + LPort := Port; + LIPVersion := IPVersion; + end; + end else begin + LHost := Host; + LPort := Port; + LIPVersion := IPVersion; + end; + if LIPVersion = Id_IPv4 then + begin + if not GStack.IsIP(LHost) then begin + if Assigned(OnStatus) then begin + DoStatus(hsResolving, [LHost]); + end; + LIP := GStack.ResolveHost(LHost, LIPVersion); + end else begin + LIP := LHost; + end; + end + else + begin //IPv6 + LIP := MakeCanonicalIPv6Address(LHost); + if LIP='' then begin //if MakeCanonicalIPv6Address failed, we have a hostname + if Assigned(OnStatus) then begin + DoStatus(hsResolving, [LHost]); + end; + LIP := GStack.ResolveHost(LHost, LIPVersion); + end else begin + LIP := LHost; + end; + end; + Binding.SetPeer(LIP, LPort, LIPVersion); + // Connect + //note for status events, we check specifically for them here + //so we don't do a string conversion in Binding.PeerIP. + if Assigned(OnStatus) then begin + DoStatus(hsConnecting, [Binding.PeerIP]); + end; + + if ConnectTimeout = 0 then begin + if TIdAntiFreezeBase.ShouldUse then begin + DoConnectTimeout(120000); // 2 Min + end else begin + Binding.Connect; + end; + end else begin + DoConnectTimeout(ConnectTimeout); + end; + if Assigned(FTransparentProxy) then begin + if FTransparentProxy.Enabled then begin + FTransparentProxy.Connect(Self, Host, Port, IPVersion); + end; + end; +end; + +function TIdIOHandlerStack.Readable(AMSec: integer): boolean; +begin + Result := Binding.Readable(AMSec); +end; + +function TIdIOHandlerStack.WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; +begin + Assert(Binding<>nil); + Result := Binding.Send(ABuffer, AOffset, ALength); +end; + +// Reads any data in tcp/ip buffer and puts it into Indy buffer +// This must be the ONLY raw read from Winsock routine +// This must be the ONLY call to RECV - all data goes thru this method +function TIdIOHandlerStack.ReadDataFromSource(var VBuffer: TIdBytes): Integer; +begin + Assert(Binding<>nil); + Result := Binding.Receive(VBuffer); +end; + +procedure TIdIOHandlerStack.CheckForDisconnect( + ARaiseExceptionIfDisconnected: Boolean; AIgnoreBuffer: Boolean); +var + LDisconnected: Boolean; +begin + // ClosedGracefully // Server disconnected + // IOHandler = nil // Client disconnected + if ClosedGracefully then begin + if BindingAllocated then begin + Close; + // Call event handlers to inform the user that we were disconnected + DoStatus(hsDisconnected); + //DoOnDisconnected; + end; + LDisconnected := True; + end else begin + LDisconnected := not BindingAllocated; + end; + // Do not raise unless all data has been read by the user + if LDisconnected then begin + if (InputBufferIsEmpty or AIgnoreBuffer) and ARaiseExceptionIfDisconnected then begin + RaiseConnClosedGracefully; + end; + end; +end; + +{ TIdConnectThread } + +constructor TIdConnectThread.Create(ABinding: TIdSocketHandle); +begin + FBinding := ABinding; + inherited Create(False); +end; + +procedure TIdConnectThread.Execute; +begin + try + FBinding.Connect; + except + on E: Exception do begin + // TODO: acquire the actual Exception object and re-raise it in TIdIOHandlerStack.ConnectClient() + FExceptionOccured := True; + FExceptionMessage := E.Message; + if E is EIdSocketError then begin + if (EIdSocketError(E).LastError <> Id_WSAEBADF) and (EIdSocketError(E).LastError <> Id_WSAENOTSOCK) then begin + FLastSocketError := EIdSocketError(E).LastError; + end; + end; + end; + end; +end; + +procedure TIdConnectThread.DoTerminate; +begin + // Necessary as caller checks this + Terminate; + inherited; +end; + +initialization + TIdIOHandlerStack.SetDefaultClass; +end. diff --git a/indy/Core/IdIOHandlerStream.pas b/indy/Core/IdIOHandlerStream.pas new file mode 100644 index 0000000..745ca2b --- /dev/null +++ b/indy/Core/IdIOHandlerStream.pas @@ -0,0 +1,332 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.21 3/10/05 3:24:30 PM RLebeau + Updated ReadFromSource() and WriteDirect() to access the Intercept property + directly. + + Rev 1.20 10/21/2004 11:07:30 PM BGooijen + works in win32 now too + + Rev 1.19 10/21/2004 1:52:56 PM BGooijen + Raid 214235 + + Rev 1.18 7/23/04 6:20:52 PM RLebeau + Removed memory leaks in Send/ReceiveStream property setters + + Rev 1.17 2004.05.20 11:39:08 AM czhower + IdStreamVCL + + Rev 1.16 23/04/2004 20:29:36 CCostelloe + Minor change to support IdMessageClient's new TIdIOHandlerStreamMsg + + Rev 1.15 2004.04.16 11:30:32 PM czhower + Size fix to IdBuffer, optimizations, and memory leaks + + Rev 1.14 2004.04.08 3:56:36 PM czhower + Fixed bug with Intercept byte count. Also removed Bytes from Buffer. + + Rev 1.13 2004.03.07 11:48:46 AM czhower + Flushbuffer fix + other minor ones found + + Rev 1.12 2004.03.03 11:55:04 AM czhower + IdStream change + + Rev 1.11 2004.02.03 4:17:16 PM czhower + For unit name changes. + + Rev 1.10 11/01/2004 19:52:44 CCostelloe + Revisions for TIdMessage SaveToFile & LoadFromFile for D7 & D8 + + Rev 1.8 08/01/2004 23:37:16 CCostelloe + Minor changes + + Rev 1.7 1/8/2004 1:01:22 PM BGooijen + Cleaned up + + Rev 1.6 1/8/2004 4:23:06 AM BGooijen + temp fixed TIdIOHandlerStream.WriteToDestination + + Rev 1.5 08/01/2004 00:25:22 CCostelloe + Start of reimplementing LoadFrom/SaveToFile + + Rev 1.4 2003.12.31 7:44:54 PM czhower + Matched constructors visibility to ancestor. + + Rev 1.3 2003.10.24 10:44:54 AM czhower + IdStream implementation, bug fixes. + + Rev 1.2 2003.10.14 11:19:14 PM czhower + Updated for better functionality. + + Rev 1.1 2003.10.14 1:27:14 PM czhower + Uupdates + Intercept support + + Rev 1.0 2003.10.13 6:40:40 PM czhower + Moved from root + + Rev 1.9 2003.10.11 10:00:36 PM czhower + Compiles again. + + Rev 1.8 10/10/2003 10:53:42 PM BGooijen + Changed const-ness of some methods to reflect base class changes + + Rev 1.7 7/10/2003 6:07:58 PM SGrobety + .net + + Rev 1.6 17/07/2003 00:01:24 CCostelloe + Added (empty) procedures for the base classes' abstract CheckForDataOnSource + and CheckForDisconnect + + Rev 1.5 7/1/2003 12:45:56 PM BGooijen + changed FInputBuffer.Size := 0 to FInputBuffer.Clear + + Rev 1.4 12-8-2002 21:05:28 BGooijen + Removed call to Close in .Destroy, this is already done in + TIdIOHandler.Destroy + + Rev 1.3 12/7/2002 06:42:44 PM JPMugaas + These should now compile except for Socks server. IPVersion has to be a + property someplace for that. + + Rev 1.2 12/5/2002 02:53:52 PM JPMugaas + Updated for new API definitions. + + Rev 1.1 05/12/2002 15:29:16 AO'Neill + + Rev 1.0 11/13/2002 07:55:08 AM JPMugaas +} + +unit IdIOHandlerStream; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdBaseComponent, + IdGlobal, + IdIOHandler, + IdStream; + +type + TIdIOHandlerStream = class; + TIdIOHandlerStreamType = (stRead, stWrite, stReadWrite); + TIdOnGetStreams = procedure(ASender: TIdIOHandlerStream; + var VReceiveStream: TStream; var VSendStream: TStream) of object; + + TIdIOHandlerStream = class(TIdIOHandler) + protected + FFreeStreams: Boolean; + FOnGetStreams: TIdOnGetStreams; + FReceiveStream: TStream; + FSendStream: TStream; + FStreamType: TIdIOHandlerStreamType; + // + procedure InitComponent; override; + function ReadDataFromSource(var VBuffer: TIdBytes): Integer; override; + function WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override; + function SourceIsAvailable: Boolean; override; + function CheckForError(ALastResult: Integer): Integer; override; + procedure RaiseError(AError: Integer); override; + public + function StreamingAvailable: Boolean; + procedure CheckForDisconnect(ARaiseExceptionIfDisconnected: Boolean = True; + AIgnoreBuffer: Boolean = False); override; + constructor Create(AOwner: TComponent; AReceiveStream: TStream; ASendStream: TStream = nil); reintroduce; overload; virtual; + constructor Create(AOwner: TComponent); reintroduce; overload; + function Connected: Boolean; override; + procedure Close; override; + procedure Open; override; + function Readable(AMSec: integer = IdTimeoutDefault): boolean; override; + // + property ReceiveStream: TStream read FReceiveStream; + property SendStream: TStream read FSendStream; + property StreamType: TIdIOHandlerStreamType read FStreamType; + published + property FreeStreams: Boolean read FFreeStreams write FFreeStreams default True; + // + property OnGetStreams: TIdOnGetStreams read FOnGetStreams write FOnGetStreams; + end; + +implementation + +uses + IdException, IdComponent, SysUtils; + +{ TIdIOHandlerStream } + +procedure TIdIOHandlerStream.InitComponent; +begin + inherited InitComponent; + FDefStringEncoding := IndyTextEncoding_8Bit; +end; + +procedure TIdIOHandlerStream.CheckForDisconnect( + ARaiseExceptionIfDisconnected: Boolean = True; + AIgnoreBuffer: Boolean = False); +var + LDisconnected: Boolean; +begin + // ClosedGracefully // Server disconnected + // IOHandler = nil // Client disconnected + if ClosedGracefully then begin + if StreamingAvailable then begin + Close; + // Call event handlers to inform the user that we were disconnected + DoStatus(hsDisconnected); + //DoOnDisconnected; + end; + LDisconnected := True; + end else begin + LDisconnected := not StreamingAvailable; + end; + // Do not raise unless all data has been read by the user + if LDisconnected then begin + if (InputBufferIsEmpty or AIgnoreBuffer) and ARaiseExceptionIfDisconnected then begin + RaiseConnClosedGracefully; + end; + end; +end; + +procedure TIdIOHandlerStream.Close; +begin + inherited Close; + if FreeStreams then begin + FreeAndNil(FReceiveStream); + FreeAndNil(FSendStream); + end else begin + FReceiveStream := nil; + FSendStream := nil; + end; +end; + +function TIdIOHandlerStream.StreamingAvailable: Boolean; +begin + Result := False; // Just to avoid warning message + case FStreamType of + stRead: Result := Assigned(ReceiveStream); + stWrite: Result := Assigned(SendStream); + stReadWrite: Result := Assigned(ReceiveStream) and Assigned(SendStream); + end; +end; + +function TIdIOHandlerStream.Connected: Boolean; +begin + Result := (StreamingAvailable and inherited Connected) or (not InputBufferIsEmpty); +end; + +constructor TIdIOHandlerStream.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FFreeStreams := True; + FStreamType := stReadWrite; +end; + +constructor TIdIOHandlerStream.Create(AOwner: TComponent; AReceiveStream: TStream; + ASendStream: TStream = nil); +begin + inherited Create(AOwner); + // + FFreeStreams := True; + FReceiveStream := AReceiveStream; + FSendStream := ASendStream; + // + if Assigned(FReceiveStream) and (not Assigned(FSendStream)) then begin + FStreamType := stRead; + end else if (not Assigned(FReceiveStream)) and Assigned(FSendStream) then begin + FStreamType := stWrite; + end else begin + FStreamType := stReadWrite; + end; +end; + +procedure TIdIOHandlerStream.Open; +begin + inherited Open; + if Assigned(OnGetStreams) then begin + OnGetStreams(Self, FReceiveStream, FSendStream); + end; + if Assigned(FReceiveStream) and (not Assigned(FSendStream)) then begin + FStreamType := stRead; + end else if (not Assigned(FReceiveStream)) and Assigned(FSendStream) then begin + FStreamType := stWrite; + end else begin + FStreamType := stReadWrite; + end; +end; + +function TIdIOHandlerStream.Readable(AMSec: Integer): Boolean; +begin + Result := Assigned(ReceiveStream); + // RLebeau: not checking the Position anymore. Was + // causing deadlocks when trying to read past EOF. + // This way, when EOF is reached, ReadFromSource() + // will return 0, which will be interpretted as the + // connnection being closed... + { + if Result then begin + Result := ReceiveStream.Position < ReceiveStream.Size; + end; + } +end; + +function TIdIOHandlerStream.ReadDataFromSource(var VBuffer: TIdBytes): Integer; +begin + // We dont want to read the whole stream in at a time. If its a big + // file will consume way too much memory by loading it all at once. + // So lets read it in chunks. + if Assigned(FReceiveStream) then begin + Result := IndyMin(32 * 1024, Length(VBuffer)); + if Result > 0 then begin + Result := TIdStreamHelper.ReadBytes(FReceiveStream, VBuffer, Result); + end; + end else begin + Result := 0; + end; +end; + +function TIdIOHandlerStream.WriteDataToTarget(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; +begin + if Assigned(FSendStream) then begin + Result := TIdStreamHelper.Write(FSendStream, ABuffer, ALength, AOffset); + end else begin + Result := IndyLength(ABuffer, ALength, AOffset); + end; +end; + +function TIdIOHandlerStream.SourceIsAvailable: Boolean; +begin + Result := Assigned(ReceiveStream); +end; + +function TIdIOHandlerStream.CheckForError(ALastResult: Integer): Integer; +begin + Result := ALastResult; + if Result < 0 then begin + raise EIdException.Create('Stream error'); {do not localize} + end; +end; + +procedure TIdIOHandlerStream.RaiseError(AError: Integer); +begin + raise EIdException.Create('Stream error'); {do not localize} +end; + +end. diff --git a/indy/Core/IdIPAddress.pas b/indy/Core/IdIPAddress.pas new file mode 100644 index 0000000..0b9430d --- /dev/null +++ b/indy/Core/IdIPAddress.pas @@ -0,0 +1,293 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 2/8/05 5:29:16 PM RLebeau + Updated GetHToNBytes() to use CopyTIdWord() instead of AppendBytes() for IPv6 + addresses. + + Rev 1.9 28.09.2004 20:54:32 Andreas Hausladen + Removed unused functions that were moved to IdGlobal + + Rev 1.8 6/11/2004 8:48:20 AM DSiders + Added "Do not Localize" comments. + + Rev 1.7 5/19/2004 10:44:34 PM DSiders + Corrected spelling for TIdIPAddress.MakeAddressObject method. + + Rev 1.6 14/04/2004 17:35:38 HHariri + Removed IP6 for BCB temporarily + + Rev 1.5 2/11/2004 5:10:40 AM JPMugaas + Moved IPv6 address definition to System package. + + Rev 1.4 2004.02.03 4:17:18 PM czhower + For unit name changes. + + Rev 1.3 2/2/2004 12:22:24 PM JPMugaas + Now uses IdGlobal IPVersion Type. Added HToNBytes for things that need + to export into NetworkOrder for structures used in protocols. + + Rev 1.2 1/3/2004 2:13:56 PM JPMugaas + Removed some empty function code that wasn't used. + Added some value comparison functions. + Added a function in the IPAddress object for comparing the value with another + IP address. Note that this comparison is useful as an IP address will take + several forms (especially common with IPv6). + Added a property for returning the IP address as a string which works for + both IPv4 and IPv6 addresses. + + Rev 1.1 1/3/2004 1:03:14 PM JPMugaas + Removed Lo as it was not needed and is not safe in NET. + + Rev 1.0 1/1/2004 4:00:18 PM JPMugaas + An object for handling both IPv4 and IPv6 addresses. This is a proposal with + some old code for conversions. +} + +unit IdIPAddress; + +interface + +{$I IdCompilerDefines.inc} +//we need to put this in Delphi mode to work + +uses + Classes, + IdGlobal; + +type + TIdIPAddress = class(TObject) + protected + FIPv4 : UInt32; + FAddrType : TIdIPVersion; + //general conversion stuff + //property as String Get methods + function GetIPv4AsString : String; + function GetIPv6AsString : String; + function GetIPAddress : String; + public + //We can't make this into a property for C++Builder + IPv6 : TIdIPv6Address; + + constructor Create; virtual; + class function MakeAddressObject(const AIP : String) : TIdIPAddress; overload; + class function MakeAddressObject(const AIP : String; const AIPVersion: TIdIPVersion) : TIdIPAddress; overload; + function CompareAddress(const AIP : String; var VErr : Boolean) : Integer; + function HToNBytes: TIdBytes; + + property IPv4 : UInt32 read FIPv4 write FIPv4; + property IPv4AsString : String read GetIPv4AsString; + property IPv6AsString : String read GetIPv6AsString; + property AddrType : TIdIPVersion read FAddrType write FAddrType; + property IPAsString : String read GetIPAddress; + end; + +implementation + +uses + IdStack, SysUtils; + +//IPv4 address conversion +//Much of this is based on http://www.pc-help.org/obscure.htm + +function CompareUInt16(const AWord1, AWord2 : UInt16) : Integer; +{$IFDEF USE_INLINE}inline;{$ENDIF} +{ +AWord1 > AWord2 > 0 +AWord1 < AWord2 < 0 +AWord1 = AWord2 = 0 +} +begin + if AWord1 > AWord2 then begin + Result := 1; + end else if AWord1 < AWord2 then begin + Result := -1; + end else begin + Result := 0; + end; +end; + +function CompareUInt32(const ACard1, ACard2 : UInt32) : Integer; +{$IFDEF USE_INLINE}inline;{$ENDIF} +{ +ACard1 > ACard2 > 0 +ACard1 < ACard2 < 0 +ACard1 = ACard2 = 0 +} +begin + if ACard1 > ACard2 then begin + Result := 1; + end else if ACard1 < ACard2 then begin + Result := -1; + end else begin + Result := 0; + end; +end; + +{ TIdIPAddress } + +function TIdIPAddress.CompareAddress(const AIP: String; var VErr: Boolean): Integer; +var + LIP2 : TIdIPAddress; + i : Integer; +{ +Note that the IP address in the object is S1. +S1 > S2 > 0 +S1 < S2 < 0 +S1 = S2 = 0 +} +begin + Result := 0; + //LIP2 may be nil if the IP address is invalid + LIP2 := MakeAddressObject(AIP); + VErr := not Assigned(LIP2); + if not VErr then begin + try + // we can't compare an IPv4 address with an IPv6 address + VErr := FAddrType <> LIP2.FAddrType; + if not VErr then begin + if FAddrType = Id_IPv4 then begin + Result := CompareUInt32(FIPv4, LIP2.FIPv4); + end else begin + for I := 0 to 7 do begin + Result := CompareUInt16(IPv6[i], LIP2.IPv6[i]); + if Result <> 0 then begin + Break; + end; + end; + end; + end; + finally + FreeAndNil(LIP2); + end; + end; +end; + +constructor TIdIPAddress.Create; +begin + inherited Create; + FAddrType := Id_IPv4; + FIPv4 := 0; //'0.0.0.0' +end; + +function TIdIPAddress.HToNBytes: TIdBytes; +var + I : Integer; +begin + if FAddrType = Id_IPv4 then begin + Result := ToBytes(GStack.HostToNetwork(FIPv4)); + end else begin + SetLength(Result, 16); + for I := 0 to 7 do begin + CopyTIdUInt16(GStack.HostToNetwork(IPv6[i]), Result, 2*I); + end; + end; +end; + +function TIdIPAddress.GetIPAddress: String; +begin + if FAddrType = Id_IPv4 then begin + Result := GetIPv4AsString; + end else begin + Result := GetIPv6AsString; + end; +end; + +function TIdIPAddress.GetIPv4AsString: String; +begin + if FAddrType = Id_IPv4 then begin + Result := IntToStr((FIPv4 shr 24) and $FF) + '.'; + Result := Result + IntToStr((FIPv4 shr 16) and $FF) + '.'; + Result := Result + IntToStr((FIPv4 shr 8) and $FF) + '.'; + Result := Result + IntToStr(FIPv4 and $FF); + end else begin + Result := ''; + end; +end; + +function TIdIPAddress.GetIPv6AsString: String; +var + I: Integer; +begin + if FAddrType = Id_IPv6 then begin + Result := IntToHex(IPv6[0], 4); + for i := 1 to 7 do begin + Result := Result + ':' + IntToHex(IPv6[i], 4); + end; + end else begin + Result := ''; + end; +end; + +class function TIdIPAddress.MakeAddressObject(const AIP: String): TIdIPAddress; +var + LErr : Boolean; +begin + Result := TIdIPAddress.Create; + try + IPv6ToIdIPv6Address(AIP, Result.IPv6, LErr); + if not LErr then begin + Result.FAddrType := Id_IPv6; + Exit; + end; + Result.FIPv4 := IPv4ToUInt32(AIP, LErr); + if not LErr then begin + Result.FAddrType := Id_IPv4; + Exit; + end; + //this is not a valid IP address + FreeAndNil(Result); + except + FreeAndNil(Result); + raise; + end; +end; + +class function TIdIPAddress.MakeAddressObject(const AIP: String; const AIPVersion: TIdIPVersion): TIdIPAddress; +var + LErr : Boolean; +begin + Result := TIdIPAddress.Create; + try + case AIPVersion of + Id_IPV4: + begin + Result.FIPv4 := IPv4ToUInt32(AIP, LErr); + if not LErr then begin + Result.FAddrType := Id_IPv4; + Exit; + end; + end; + Id_IPv6: + begin + IPv6ToIdIPv6Address(AIP, Result.IPv6, LErr); + if not LErr then begin + Result.FAddrType := Id_IPv6; + Exit; + end + end; + end; + //this is not a valid IP address + FreeAndNil(Result); + except + FreeAndNil(Result); + raise; + end; +end; + +end. diff --git a/indy/Core/IdIPMCastBase.pas b/indy/Core/IdIPMCastBase.pas new file mode 100644 index 0000000..99acc59 --- /dev/null +++ b/indy/Core/IdIPMCastBase.pas @@ -0,0 +1,257 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 2004.02.03 5:43:52 PM czhower + Name changes + + Rev 1.3 1/21/2004 3:11:06 PM JPMugaas + InitComponent + + Rev 1.2 10/26/2003 09:11:50 AM JPMugaas + Should now work in NET. + + Rev 1.1 2003.10.12 4:03:56 PM czhower + compile todos + + Rev 1.0 11/13/2002 07:55:16 AM JPMugaas +} + +unit IdIPMCastBase; + +interface + +{$I IdCompilerDefines.inc} +//here to flip FPC into Delphi mode + +uses + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + Classes, + {$ENDIF} + IdComponent, IdException, IdGlobal, IdSocketHandle, + IdStack; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Idipmcastbase@TIdIPMCastBase@SetPortA$qqrxi"="@Idipmcastbase@TIdIPMCastBase@SetPort$qqrxi"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Idipmcastbase@TIdIPMCastBase@SetPortW$qqrxi"="@Idipmcastbase@TIdIPMCastBase@SetPort$qqrxi"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +const + IPMCastLo = 224; + IPMCastHi = 239; + +type + TIdIPMv6Scope = ( IdIPv6MC_InterfaceLocal, +{ Interface-Local scope spans only a single interface on a node + and is useful only for loopback transmission of multicast.} + IdIPv6MC_LinkLocal, +{ Link-Local multicast scope spans the same topological region as +the corresponding unicast scope. } + IdIPv6MC_AdminLocal, +{ Admin-Local scope is the smallest scope that must be +administratively configured, i.e., not automatically derived +from physical connectivity or other, non-multicast-related +configuration.} + IdIPv6MC_SiteLocal, +{ Site-Local scope is intended to span a single site. } + IdIPv6MC_OrgLocal, +{Organization-Local scope is intended to span multiple sites +belonging to a single organization.} + IdIPv6MC_Global); + + TIdIPMCValidScopes = 0..$F; + + TIdIPMCastBase = class(TIdComponent) + protected + FDsgnActive: Boolean; + FMulticastGroup: String; + FPort: Integer; + FIPVersion: TIdIPVersion; + FReuseSocket: TIdReuseSocket; + // + procedure CloseBinding; virtual; abstract; + function GetActive: Boolean; virtual; + function GetBinding: TIdSocketHandle; virtual; abstract; + procedure Loaded; override; + procedure SetActive(const Value: Boolean); virtual; + procedure SetMulticastGroup(const Value: string); virtual; + procedure SetPort(const Value: integer); virtual; + function GetIPVersion: TIdIPVersion; virtual; + procedure SetIPVersion(const AValue: TIdIPVersion); virtual; + // + property Active: Boolean read GetActive write SetActive Default False; + property MulticastGroup: string read FMulticastGroup write SetMulticastGroup; + property Port: Integer read FPort write SetPort; + property IPVersion: TIdIPVersion read GetIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION; + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + function IsValidMulticastGroup(const Value: string): Boolean; +{These two items are helper functions that allow you to specify the scope for +a Variable Scope Multicast Addresses. Some are listed in IdAssignedNumbers +as the Id_IPv6MC_V_ constants. You can't use them out of the box in the +MulticastGroup property because you need to specify the scope. This provides +you with more flexibility than you would get with IPv4 multicasting.} + class function SetIPv6AddrScope(const AVarIPv6Addr : String; const AScope : TIdIPMv6Scope ) : String; overload; + class function SetIPv6AddrScope(const AVarIPv6Addr : String; const AScope : TIdIPMCValidScopes): String; overload; + // + property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent; + published + end; + + EIdMCastException = Class(EIdException); + EIdMCastNoBindings = class(EIdMCastException); + EIdMCastNotValidAddress = class(EIdMCastException); + EIdMCastReceiveErrorZeroBytes = class(EIdMCastException); + +const + DEF_IPv6_MGROUP = 'FF01:0:0:0:0:0:0:1'; + +implementation + +uses + IdAssignedNumbers, + IdResourceStringsCore, IdStackConsts, SysUtils; + +{ TIdIPMCastBase } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdIPMCastBase.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +function TIdIPMCastBase.GetIPVersion: TIdIPVersion; +begin + Result := FIPVersion; +end; + +procedure TIdIPMCastBase.InitComponent; +begin + inherited InitComponent; + FMultiCastGroup := Id_IPMC_All_Systems; + FIPVersion := ID_DEFAULT_IP_VERSION; + FReuseSocket := rsOSDependent; +end; + +function TIdIPMCastBase.GetActive: Boolean; +begin + Result := FDsgnActive; +end; + +function TIdIPMCastBase.IsValidMulticastGroup(const Value: string): Boolean; +begin +//just here to prevent a warning from Delphi about an unitialized result + Result := False; + case FIPVersion of + Id_IPv4 : Result := GStack.IsValidIPv4MulticastGroup(Value); + Id_IPv6 : Result := GStack.IsValidIPv6MulticastGroup(Value); + end; +end; + +procedure TIdIPMCastBase.Loaded; +var + b: Boolean; +begin + inherited Loaded; + b := FDsgnActive; + FDsgnActive := False; + Active := b; +end; + +procedure TIdIPMCastBase.SetActive(const Value: Boolean); +begin + if Active <> Value then begin + if not (IsDesignTime or IsLoading) then begin + if Value then begin + GetBinding; + end + else begin + CloseBinding; + end; + end + else begin // don't activate at designtime (or during loading of properties) {Do not Localize} + FDsgnActive := Value; + end; + end; +end; + +class function TIdIPMCastBase.SetIPv6AddrScope(const AVarIPv6Addr: String; + const AScope: TIdIPMv6Scope): String; +begin + + case AScope of + IdIPv6MC_InterfaceLocal : Result := SetIPv6AddrScope(AVarIPv6Addr,$1); + IdIPv6MC_LinkLocal : Result := SetIPv6AddrScope(AVarIPv6Addr,$2); + IdIPv6MC_AdminLocal : Result := SetIPv6AddrScope(AVarIPv6Addr,$4); + IdIPv6MC_SiteLocal : Result := SetIPv6AddrScope(AVarIPv6Addr,$5); + IdIPv6MC_OrgLocal : Result := SetIPv6AddrScope(AVarIPv6Addr,$8); + IdIPv6MC_Global : Result := SetIPv6AddrScope(AVarIPv6Addr,$E); + else + Result := AVarIPv6Addr; + end; +end; + +class function TIdIPMCastBase.SetIPv6AddrScope(const AVarIPv6Addr: String; + const AScope: TIdIPMCValidScopes): String; +begin + //Replace the X in the Id_IPv6MC_V_ constants with the specified scope + Result := ReplaceOnlyFirst(AVarIPv6Addr,'X',IntToHex(AScope,1)); +end; + +procedure TIdIPMCastBase.SetIPVersion(const AValue: TIdIPVersion); +begin + if AValue <> IPVersion then + begin + Active := False; + FIPVersion := AValue; + case AValue of + Id_IPv4: FMulticastGroup := Id_IPMC_All_Systems; + Id_IPv6: FMulticastGroup := DEF_IPv6_MGROUP; + end; + end; +end; + +procedure TIdIPMCastBase.SetMulticastGroup(const Value: string); +begin + if (FMulticastGroup <> Value) then begin + if IsValidMulticastGroup(Value) then + begin + Active := False; + FMulticastGroup := Value; + end else + begin + Raise EIdMCastNotValidAddress.Create(RSIPMCastInvalidMulticastAddress); + end; + end; +end; + +procedure TIdIPMCastBase.SetPort(const Value: integer); +begin + if FPort <> Value then begin + Active := False; + FPort := Value; + end; +end; + +end. diff --git a/indy/Core/IdIPMCastClient.pas b/indy/Core/IdIPMCastClient.pas new file mode 100644 index 0000000..ecba430 --- /dev/null +++ b/indy/Core/IdIPMCastClient.pas @@ -0,0 +1,312 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 14/06/2004 21:38:28 CCostelloe + Converted StringToTIn4Addr call + + Rev 1.5 09/06/2004 10:00:34 CCostelloe + Kylix 3 patch + + Rev 1.4 2004.02.03 5:43:52 PM czhower + Name changes + + Rev 1.3 1/21/2004 3:11:08 PM JPMugaas + InitComponent + + Rev 1.2 10/26/2003 09:11:52 AM JPMugaas + Should now work in NET. + + Rev 1.1 2003.10.12 4:03:56 PM czhower + compile todos + + Rev 1.0 11/13/2002 07:55:22 AM JPMugaas +} + +unit IdIPMCastClient; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + {$IFDEF VCL_2010_OR_ABOVE} + Classes, //here to facilitate inlining + {$ENDIF} + IdException, + IdGlobal, + IdIPMCastBase, + IdUDPBase, + IdComponent, + IdSocketHandle, + IdThread; + +const + DEF_IMP_THREADEDEVENT = False; + +type + TIPMCastReadEvent = procedure(Sender: TObject; const AData: TIdBytes; ABinding: TIdSocketHandle) of object; + + TIdIPMCastClient = class; + + TIdIPMCastListenerThread = class(TIdThread) + protected + IncomingData: TIdSocketHandle; + FAcceptWait: integer; + FBuffer: TIdBytes; + FBufferSize: integer; + procedure Run; override; + public + FServer: TIdIPMCastClient; + // + constructor Create(AOwner: TIdIPMCastClient); reintroduce; + destructor Destroy; override; + + procedure IPMCastRead; + // + property AcceptWait: integer read FAcceptWait write FAcceptWait; + end; + + TIdIPMCastClient = class(TIdIPMCastBase) + protected + FBindings: TIdSocketHandles; + FBufferSize: Integer; + FCurrentBinding: TIdSocketHandle; + FListenerThread: TIdIPMCastListenerThread; + FOnIPMCastRead: TIPMCastReadEvent; + FThreadedEvent: boolean; + // + procedure CloseBinding; override; + procedure DoIPMCastRead(const AData: TIdBytes; ABinding: TIdSocketHandle);virtual; + function GetActive: Boolean; override; + function GetBinding: TIdSocketHandle; override; + function GetDefaultPort: integer; + procedure PacketReceived(const AData: TIdBytes; ABinding: TIdSocketHandle); + procedure SetBindings(const Value: TIdSocketHandles); + procedure SetDefaultPort(const AValue: integer); + procedure InitComponent; override; + public + destructor Destroy; override; + // + published + property IPVersion; + property Active; + property Bindings: TIdSocketHandles read FBindings write SetBindings; + property BufferSize: Integer read FBufferSize write FBufferSize default ID_UDP_BUFFERSIZE; + property DefaultPort: integer read GetDefaultPort write SetDefaultPort; + property MulticastGroup; + property ReuseSocket; + property ThreadedEvent: boolean read FThreadedEvent write FThreadedEvent default DEF_IMP_THREADEDEVENT; + property OnIPMCastRead: TIPMCastReadEvent read FOnIPMCastRead write FOnIPMCastRead; + end; + +implementation + +uses + IdResourceStringsCore, + IdStack, + IdStackConsts, + SysUtils; + +{ TIdIPMCastClient } + +procedure TIdIPMCastClient.InitComponent; +begin + inherited InitComponent; + BufferSize := ID_UDP_BUFFERSIZE; + FThreadedEvent := DEF_IMP_THREADEDEVENT; + FBindings := TIdSocketHandles.Create(Self); +end; + +procedure TIdIPMCastClient.CloseBinding; +var + i: integer; +begin + if Assigned(FCurrentBinding) then begin + // Necessary here - cancels the recvfrom in the listener thread + FListenerThread.Stop; + try + for i := 0 to Bindings.Count - 1 do begin + if Bindings[i].HandleAllocated then begin + // RLebeau: DropMulticastMembership() can raise an exception if + // the network cable has been pulled out... + // TODO: update DropMulticastMembership() to not raise an exception... + try + Bindings[i].DropMulticastMembership(FMulticastGroup); + except + end; + end; + Bindings[i].CloseSocket; + end; + finally + FListenerThread.WaitFor; + FreeAndNil(FListenerThread); + FCurrentBinding := nil; + end; + end; +end; + +procedure TIdIPMCastClient.DoIPMCastRead(const AData: TIdBytes; ABinding: TIdSocketHandle); +begin + if Assigned(OnIPMCastRead) then begin + OnIPMCastRead(Self, AData, ABinding); + end; +end; + +function TIdIPMCastClient.GetActive: Boolean; +begin + // inherited GetActive keeps track of design-time Active property + Result := inherited GetActive or + (Assigned(FCurrentBinding) and FCurrentBinding.HandleAllocated); +end; + +function TIdIPMCastClient.GetBinding: TIdSocketHandle; +var + i: integer; +begin + if not Assigned(FCurrentBinding) then + begin + if Bindings.Count < 1 then begin + if DefaultPort > 0 then begin + Bindings.Add.IPVersion := FIPVersion; + end else begin + raise EIdMCastNoBindings.Create(RSNoBindingsSpecified); + end; + end; + for i := 0 to Bindings.Count - 1 do begin + Bindings[i].AllocateSocket(Id_SOCK_DGRAM); + // do not overwrite if the default. This allows ReuseSocket to be set per binding + if FReuseSocket <> rsOSDependent then begin + Bindings[i].ReuseSocket := FReuseSocket; + end; + Bindings[i].Bind; + Bindings[i].AddMulticastMembership(FMulticastGroup); + end; + FCurrentBinding := Bindings[0]; + FListenerThread := TIdIPMCastListenerThread.Create(Self); + FListenerThread.Start; + end; + Result := FCurrentBinding; +end; + +function TIdIPMCastClient.GetDefaultPort: integer; +begin + result := FBindings.DefaultPort; +end; + +procedure TIdIPMCastClient.PacketReceived(const AData: TIdBytes; ABinding: TIdSocketHandle); +begin + FCurrentBinding := ABinding; + DoIPMCastRead(AData, ABinding); +end; + +procedure TIdIPMCastClient.SetBindings(const Value: TIdSocketHandles); +begin + FBindings.Assign(Value); +end; + +procedure TIdIPMCastClient.SetDefaultPort(const AValue: integer); +begin + if (FBindings.DefaultPort <> AValue) then begin + FBindings.DefaultPort := AValue; + FPort := AValue; + end; +end; + +destructor TIdIPMCastClient.Destroy; +begin + Active := False; + FreeAndNil(FBindings); + inherited Destroy; +end; + +{ TIdIPMCastListenerThread } + +constructor TIdIPMCastListenerThread.Create(AOwner: TIdIPMCastClient); +begin + inherited Create(True); + FAcceptWait := 1000; + FBufferSize := AOwner.BufferSize; + FBuffer := nil; + FServer := AOwner; +end; + +destructor TIdIPMCastListenerThread.Destroy; +begin + inherited Destroy; +end; + +procedure TIdIPMCastListenerThread.Run; +var + PeerIP: string; + PeerPort: TIdPort; + PeerIPVersion: TIdIPVersion; + ByteCount: Integer; + LReadList: TIdSocketList; + i: Integer; + LBuffer : TIdBytes; +begin + SetLength(LBuffer, FBufferSize); + + // create a socket list to select for read + LReadList := TIdSocketList.CreateSocketList; + try + // fill list of socket handles for reading + for i := 0 to FServer.Bindings.Count - 1 do + begin + LReadList.Add(FServer.Bindings[i].Handle); + end; + + // select the handles for reading + LReadList.SelectRead(AcceptWait); + + for i := 0 to LReadList.Count - 1 do + begin + // Doublecheck to see if we've been stopped + // Depending on timing - may not reach here + // if stopped the run method of the ancestor + + if not Stopped then + begin + IncomingData := FServer.Bindings.BindingByHandle(TIdStackSocketHandle(LReadList[i])); + ByteCount := IncomingData.RecvFrom(LBuffer, PeerIP, PeerPort, PeerIPVersion); + if ByteCount <= 0 then + begin + raise EIdUDPReceiveErrorZeroBytes.Create(RSIPMCastReceiveError0); + end; + SetLength(FBuffer, ByteCount); + CopyTIdBytes(LBuffer, 0, FBuffer, 0, ByteCount); + IncomingData.SetPeer(PeerIP, PeerPort, PeerIPVersion); + if FServer.ThreadedEvent then begin + IPMCastRead; + end else begin + Synchronize(IPMCastRead); + end; + end; + end; + finally + LReadList.Free; + end; +end; + +procedure TIdIPMCastListenerThread.IPMCastRead; +begin + FServer.PacketReceived(FBuffer, IncomingData); +end; + +end. diff --git a/indy/Core/IdIPMCastServer.pas b/indy/Core/IdIPMCastServer.pas new file mode 100644 index 0000000..4e8d068 --- /dev/null +++ b/indy/Core/IdIPMCastServer.pas @@ -0,0 +1,221 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 14/06/2004 21:38:42 CCostelloe + Converted StringToTIn4Addr call + + Rev 1.6 09/06/2004 10:00:50 CCostelloe + Kylix 3 patch + + Rev 1.5 2004.02.03 5:43:52 PM czhower + Name changes + + Rev 1.4 1/21/2004 3:11:10 PM JPMugaas + InitComponent + + Rev 1.3 10/26/2003 09:11:54 AM JPMugaas + Should now work in NET. + + Rev 1.2 2003.10.24 10:38:28 AM czhower + UDP Server todos + + Rev 1.1 2003.10.12 4:03:58 PM czhower + compile todos + + Rev 1.0 11/13/2002 07:55:26 AM JPMugaas + + 2001-10-16 DSiders + Modified TIdIPMCastServer.MulticastBuffer to + validate the AHost argument to the method instead + of the MulticastGroup property. +} + +unit IdIPMCastServer; + +{ + Dr. Harley J. Mackenzie, Initial revision. +} + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + IdComponent, + IdGlobal, + IdIPMCastBase, + IdSocketHandle; + +const + DEF_IMP_LOOPBACK = True; + DEF_IMP_TTL = 1; + +type + TIdIPMCastServer = class(TIdIPMCastBase) + protected + FBinding: TIdSocketHandle; + FBoundIP: String; + FBoundPort: TIdPort; + FLoopback: Boolean; + FTimeToLive: Byte; + // + procedure ApplyLoopback; + procedure ApplyTimeToLive; + procedure CloseBinding; override; + function GetActive: Boolean; override; + function GetBinding: TIdSocketHandle; override; + procedure Loaded; override; + procedure MulticastBuffer(const AHost: string; const APort: Integer; const ABuffer : TIdBytes); + procedure SetLoopback(const AValue: Boolean); virtual; + procedure SetTTL(const AValue: Byte); virtual; + procedure InitComponent; override; + public + procedure Send(const AData: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Send(const ABuffer : TIdBytes); overload; + destructor Destroy; override; + // + property Binding: TIdSocketHandle read GetBinding; + published + property Active; + property BoundIP: String read FBoundIP write FBoundIP; + property BoundPort: TIdPort read FBoundPort write FBoundPort; + property Loopback: Boolean read FLoopback write SetLoopback default DEF_IMP_LOOPBACK; + property MulticastGroup; + property IPVersion; + property Port; + property ReuseSocket; + property TimeToLive: Byte read FTimeToLive write SetTTL default DEF_IMP_TTL; + end; + +implementation + +{ TIdIPMCastServer } + +uses + IdResourceStringsCore, + IdStack, + IdStackConsts, + SysUtils; + +procedure TIdIPMCastServer.InitComponent; +begin + inherited InitComponent; + FLoopback := DEF_IMP_LOOPBACK; + FTimeToLive := DEF_IMP_TTL; +end; + +procedure TIdIPMCastServer.Loaded; +var + b: Boolean; +begin + inherited Loaded; + b := FDsgnActive; + FDsgnActive := False; + Active := b; +end; + +destructor TIdIPMCastServer.Destroy; +begin + Active := False; + inherited Destroy; +end; + +procedure TIdIPMCastServer.CloseBinding; +begin + FreeAndNil(FBinding); +end; + +function TIdIPMCastServer.GetActive: Boolean; +begin + Result := (inherited GetActive) or (Assigned(FBinding) and FBinding.HandleAllocated); +end; + +function TIdIPMCastServer.GetBinding: TIdSocketHandle; +begin + if not Assigned(FBinding) then begin + FBinding := TIdSocketHandle.Create(nil); + end; + if not FBinding.HandleAllocated then begin + FBinding.IPVersion := FIPVersion; + FBinding.AllocateSocket(Id_SOCK_DGRAM); + FBinding.IP := FBoundIP; + FBinding.Port := FBoundPort; + FBinding.ReuseSocket := FReuseSocket; + FBinding.Bind; + ApplyTimeToLive; + ApplyLoopback; + end; + Result := FBinding; +end; + +procedure TIdIPMCastServer.MulticastBuffer(const AHost: string; const APort: Integer; const ABuffer : TIdBytes); +begin + // DS - if not IsValidMulticastGroup(FMulticastGroup) then + if not IsValidMulticastGroup(AHost) then begin + raise EIdMCastNotValidAddress.Create(RSIPMCastInvalidMulticastAddress); + end; + Binding.SendTo(AHost, APort, ABuffer, Binding.IPVersion); +end; + +procedure TIdIPMCastServer.Send(const AData: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + MulticastBuffer(FMulticastGroup, FPort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +procedure TIdIPMCastServer.Send(const ABuffer : TIdBytes); +begin + MulticastBuffer(FMulticastGroup, FPort, ABuffer); +end; + +procedure TIdIPMCastServer.SetLoopback(const AValue: Boolean); +begin + if FLoopback <> AValue then begin + FLoopback := AValue; + ApplyLoopback; + end; +end; + +procedure TIdIPMCastServer.SetTTL(const AValue: Byte); +begin + if FTimeToLive <> AValue then begin + FTimeToLive := AValue; + ApplyTimeToLive; + end; +end; + +procedure TIdIPMCastServer.ApplyLoopback; +begin + if Assigned(FBinding) and FBinding.HandleAllocated then begin + FBinding.SetLoopBack(FLoopback); + end; +end; + +procedure TIdIPMCastServer.ApplyTimeToLive; +begin + if Assigned(FBinding) and FBinding.HandleAllocated then begin + FBinding.SetMulticastTTL(FTimeToLive); + end; +end; + +end. + diff --git a/indy/Core/IdIcmpClient.pas b/indy/Core/IdIcmpClient.pas new file mode 100644 index 0000000..9d03633 --- /dev/null +++ b/indy/Core/IdIcmpClient.pas @@ -0,0 +1,824 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 2004-04-25 12:08:24 Mattias + Fixed multithreading issue + + Rev 1.7 2004.02.03 4:16:42 PM czhower + For unit name changes. + + Rev 1.6 2/1/2004 4:53:30 PM JPMugaas + Removed Todo; + + Rev 1.5 2004.01.20 10:03:24 PM czhower + InitComponent + + Rev 1.4 2003.12.31 10:37:54 PM czhower + GetTickcount --> Ticks + + Rev 1.3 10/16/2003 11:06:14 PM SPerry + Moved ICMP_MIN to IdRawHeaders + + Rev 1.2 2003.10.11 5:48:04 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.1 2003.09.30 1:22:56 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 08:44:30 AM JPMugaas + + 25/1/02: SGrobety: + Modified the component to support multithreaded PING and traceroute + NOTE!!! + The component no longer use the timing informations contained + in the packet to compute the roundtrip time. This is because + that information is only correctly set in case of ECHOREPLY + In case of TTL, it is incorrect. +} + +unit IdIcmpClient; + +{ + Note that we can NOT remove the DotNET IFDEFS from this unit. The reason is + that Microsoft NET Framework 1.1 does not support ICMPv6 and that's required + for IPv6. In Win32 and Linux, we definately can and want to support IPv6. + + If we support a later version of the NET framework that has a better API, I may + consider revisiting this. +} + +// SG 25/1/02: Modified the component to support multithreaded PING and traceroute + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdGlobal, + IdRawBase, + IdRawClient, + IdStackConsts, + IdBaseComponent; + +const + DEF_PACKET_SIZE = 32; + MAX_PACKET_SIZE = 1024; + Id_TIDICMP_ReceiveTimeout = 5000; + +type + TReplyStatusTypes = (rsEcho, + rsError, rsTimeOut, rsErrorUnreachable, + rsErrorTTLExceeded,rsErrorPacketTooBig, + rsErrorParameter, + rsErrorDatagramConversion, + rsErrorSecurityFailure, + rsSourceQuench, + rsRedirect, + rsTimeStamp, + rsInfoRequest, + rsAddressMaskRequest, + rsTraceRoute, + rsMobileHostReg, + rsMobileHostRedir, + rsIPv6WhereAreYou, + rsIPv6IAmHere, + rsSKIP); + + TReplyStatus = class(TObject) + protected + FBytesReceived: integer; // number of bytes in reply from host + FFromIpAddress: string; // IP address of replying host + FToIpAddress : string; //who receives it (i.e., us. This is for multihorned machines + FMsgType: byte; + FMsgCode : Byte; + FSequenceId: word; // sequence id of ping reply + // TODO: roundtrip time in ping reply should be float, not byte + FMsRoundTripTime: UInt32; // ping round trip time in milliseconds + FTimeToLive: byte; // time to live + FReplyStatusType: TReplyStatusTypes; + FPacketNumber : Integer;//number in packet for TraceRoute + FHostName : String; //Hostname of computer that replied, used with TraceRoute + FMsg : String; + FRedirectTo : String; // valid only for rsRedirect + public + property RedirectTo : String read FRedirectTo write FRedirectTo; + property Msg : String read FMsg write FMsg; + property BytesReceived: integer read FBytesReceived write FBytesReceived; // number of bytes in reply from host + property FromIpAddress: string read FFromIpAddress write FFromIpAddress; // IP address of replying host + property ToIpAddress : string read FToIpAddress write FToIpAddress; //who receives it (i.e., us. This is for multihorned machines + property MsgType: byte read FMsgType write FMsgType; + property MsgCode : Byte read FMsgCode write FMsgCode; + property SequenceId: word read FSequenceId write FSequenceId; // sequence id of ping reply + // TODO: roundtrip time in ping reply should be float, not byte + property MsRoundTripTime: UInt32 read FMsRoundTripTime write FMsRoundTripTime; // ping round trip time in milliseconds + property TimeToLive: byte read FTimeToLive write FTimeToLive; // time to live + property ReplyStatusType: TReplyStatusTypes read FReplyStatusType write FReplyStatusType; + property HostName : String read FHostName write FHostName; + property PacketNumber : Integer read FPacketNumber write FPacketNumber; + end; + + TOnReplyEvent = procedure(ASender: TComponent; const AReplyStatus: TReplyStatus) of object; + + // TODO: on MacOSX (and maybe iOS?), can use a UDP socket instead of a RAW + // socket so that non-privilege processes do not require root access... + + TIdCustomIcmpClient = class(TIdRawClient) + protected + FStartTime : TIdTicks; //this is a fallback if no packet is returned + FPacketSize : Integer; + FBufReceive: TIdBytes; + FBufIcmp: TIdBytes; + wSeqNo: word; + iDataSize: integer; + FReplyStatus: TReplyStatus; + FOnReply: TOnReplyEvent; + FReplydata: String; + // + {$IFNDEF DOTNET_1_1} + function DecodeIPv6Packet(BytesRead: UInt32): Boolean; + {$ENDIF} + function DecodeIPv4Packet(BytesRead: UInt32): Boolean; + function DecodeResponse(BytesRead: UInt32): Boolean; + procedure DoReply; virtual; + procedure GetEchoReply; + procedure InitComponent; override; + {$IFNDEF DOTNET_1_1} + procedure PrepareEchoRequestIPv6(const ABuffer: String); + {$ENDIF} + procedure PrepareEchoRequestIPv4(const ABuffer: String); + procedure PrepareEchoRequest(const ABuffer: String); + procedure SendEchoRequest; overload; + procedure SendEchoRequest(const AIP : String); overload; + function GetPacketSize: Integer; + procedure SetPacketSize(const AValue: Integer); + + //these are made public in the client + procedure InternalPing(const AIP : String; const ABuffer: String = ''; SequenceID: Word = 0); overload; {Do not Localize} + // + property PacketSize : Integer read GetPacketSize write SetPacketSize default DEF_PACKET_SIZE; + property ReplyData: string read FReplydata; + property ReplyStatus: TReplyStatus read FReplyStatus; + + property OnReply: TOnReplyEvent read FOnReply write FOnReply; + + public + destructor Destroy; override; + procedure Send(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); override; + procedure Send(const ABuffer : TIdBytes); override; + function Receive(ATimeOut: Integer): TReplyStatus; + end; + + TIdIcmpClient = class(TIdCustomIcmpClient) + public + procedure Ping(const ABuffer: String = ''; SequenceID: Word = 0); {Do not Localize} + property ReplyData; + property ReplyStatus; + published + property Host; + {$IFNDEF DOTNET_1_1} + property IPVersion; + {$ENDIF} + property PacketSize; + property ReceiveTimeout default Id_TIDICMP_ReceiveTimeout; + property OnReply; + end; + +implementation + +uses + //facilitate inlining only. + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + {$IFDEF DARWIN} + Macapi.CoreServices, + {$ENDIF} + {$ENDIF} + IdExceptionCore, IdRawHeaders, IdResourceStringsCore, + IdStack, IdStruct, SysUtils; + +{ TIdCustomIcmpClient } + +procedure TIdCustomIcmpClient.PrepareEchoRequest(const ABuffer: String); +begin + {$IFNDEF DOTNET_1_1} + if IPVersion = Id_IPv6 then begin + PrepareEchoRequestIPv6(ABuffer); + Exit; + end; + {$ENDIF} + PrepareEchoRequestIPv4(ABuffer); +end; + +{ TIdIPv4_ICMP } + +type + TIdIPv4_ICMP = class(TIdStruct) + protected + Fip_hdr: TIdIPHdr; + Ficmp_hdr: TIdICMPHdr; + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property ip_hdr: TIdIPHdr read Fip_hdr; + property icmp_hdr: TIdICMPHdr read Ficmp_hdr; + end; + +constructor TIdIPv4_ICMP.Create; +begin + inherited Create; + Fip_hdr := TIdIPHdr.Create; + Ficmp_hdr := TIdICMPHdr.Create; +end; + +destructor TIdIPv4_ICMP.Destroy; +begin + FreeAndNil(Fip_hdr); + FreeAndNil(Ficmp_hdr); + inherited Destroy; +end; + +function TIdIPv4_ICMP.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + Fip_hdr.BytesLen + Ficmp_hdr.BytesLen; +end; + +procedure TIdIPv4_ICMP.ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fip_hdr.ReadStruct(ABytes, VIndex); + Ficmp_hdr.ReadStruct(ABytes, VIndex); +end; + +procedure TIdIPv4_ICMP.WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + Fip_hdr.WriteStruct(VBytes, VIndex); + Ficmp_hdr.WriteStruct(VBytes, VIndex); +end; + +{ TIdCustomIcmpClient } + +procedure TIdCustomIcmpClient.SendEchoRequest; +begin + Send(FBufIcmp); +end; + +function TIdCustomIcmpClient.DecodeResponse(BytesRead: UInt32): Boolean; +begin + if BytesRead = 0 then begin + // Timed out + FReplyStatus.MsRoundTripTime := GetElapsedTicks(FStartTime); + FReplyStatus.BytesReceived := 0; + if IPVersion = Id_IPv4 then + begin + FReplyStatus.FromIpAddress := '0.0.0.0'; + FReplyStatus.ToIpAddress := '0.0.0.0'; + end else + begin + FReplyStatus.FromIpAddress := '::0'; + FReplyStatus.ToIpAddress := '::0'; + end; + FReplyStatus.MsgType := 0; + FReplyStatus.SequenceId := wSeqNo; + FReplyStatus.TimeToLive := 0; + FReplyStatus.ReplyStatusType := rsTimeOut; + Result := True; + end else + begin + FReplyStatus.ReplyStatusType := rsError; + {$IFNDEF DOTNET_1_1} + if IPVersion = Id_IPv6 then begin + Result := DecodeIPv6Packet(BytesRead); + Exit; + end; + {$ENDIF} + Result := DecodeIPv4Packet(BytesRead); + end; +end; + +procedure TIdCustomIcmpClient.GetEchoReply; +begin + Receive(FReceiveTimeout); +end; + +function TIdCustomIcmpClient.Receive(ATimeOut: Integer): TReplyStatus; +var + BytesRead : Integer; + TripTime: UInt32; +begin + Result := FReplyStatus; + FillBytes(FBufReceive, Length(FBufReceive), 0); + FStartTime := Ticks64; + repeat + BytesRead := ReceiveBuffer(FBufReceive, ATimeOut); + if DecodeResponse(BytesRead) then begin + Break; + end; + TripTime := GetElapsedTicks(FStartTime); + ATimeOut := ATimeOut - Integer(TripTime); // compute new timeout value + FReplyStatus.MsRoundTripTime := TripTime; + FReplyStatus.Msg := RSICMPTimeout; + // We caught a response that wasn't meant for this thread - so we must + // make sure we don't report it as such in case we time out after this + FReplyStatus.BytesReceived := 0; + if IPVersion = Id_IPv4 then + begin + FReplyStatus.FromIpAddress := '0.0.0.0'; + FReplyStatus.ToIpAddress := '0.0.0.0'; + end else + begin + FReplyStatus.FromIpAddress := '::0'; + FReplyStatus.ToIpAddress := '::0'; + end; + FReplyStatus.MsgType := 0; + FReplyStatus.SequenceId := wSeqNo; + FReplyStatus.TimeToLive := 0; + FReplyStatus.ReplyStatusType := rsTimeOut; + until ATimeOut <= 0; +end; + +procedure TIdCustomIcmpClient.DoReply; +begin + if Assigned(FOnReply) then begin + FOnReply(Self, FReplyStatus); + end; +end; + +procedure TIdCustomIcmpClient.InitComponent; +begin + inherited InitComponent; + FReplyStatus:= TReplyStatus.Create; + FProtocol := Id_IPPROTO_ICMP; + {$IFNDEF DOTNET_1_1} + ProtocolIPv6 := Id_IPPROTO_ICMPv6; + {$ENDIF} + wSeqNo := 3489; // SG 25/1/02: Arbitrary Constant <> 0 + FReceiveTimeOut := Id_TIDICMP_ReceiveTimeout; + FPacketSize := DEF_PACKET_SIZE; +end; + +destructor TIdCustomIcmpClient.Destroy; +begin + FreeAndNil(FReplyStatus); + inherited Destroy; +end; + +function TIdCustomIcmpClient.DecodeIPv4Packet(BytesRead: UInt32): Boolean; +var + LIPHeaderLen: UInt32; + LIdx: UInt32; + RTTime: UInt32; + LActualSeqID: UInt16; + LIcmp: TIdIPv4_ICMP; + LIcmpts: TIdICMPTs; +begin + Result := False; + + LIpHeaderLen := (FBufReceive[0] and $0F) * 4; + if BytesRead < (LIpHeaderLen + ICMP_MIN) then begin + raise EIdIcmpException.Create(RSICMPNotEnoughtBytes); + end; + LIdx := 0; + + LIcmp := TIdIPv4_ICMP.Create; + try + LIcmp.ReadStruct(FBufReceive, LIdx); + + {$IFDEF LINUX} + // TODO: baffled as to why linux kernel sends back echo from localhost + {$ENDIF} + + case LIcmp.icmp_hdr.icmp_type of + Id_ICMP_ECHOREPLY, Id_ICMP_ECHO: + begin + FReplyStatus.ReplyStatusType := rsEcho; + FReplyData := BytesToStringRaw(FBufReceive, LIdx, -1); + // result is only valid if the seq. number is correct + end; + Id_ICMP_UNREACH: + FReplyStatus.ReplyStatusType := rsErrorUnreachable; + Id_ICMP_TIMXCEED: + FReplyStatus.ReplyStatusType := rsErrorTTLExceeded; + Id_ICMP_PARAMPROB : + FReplyStatus.ReplyStatusType := rsErrorParameter; + Id_ICMP_REDIRECT : + FReplyStatus.ReplyStatusType := rsRedirect; + Id_ICMP_TSTAMP, Id_ICMP_TSTAMPREPLY : + FReplyStatus.ReplyStatusType := rsTimeStamp; + Id_ICMP_IREQ, Id_ICMP_IREQREPLY : + FReplyStatus.ReplyStatusType := rsInfoRequest; + Id_ICMP_MASKREQ, Id_ICMP_MASKREPLY : + FReplyStatus.ReplyStatusType := rsAddressMaskRequest; + Id_ICMP_TRACEROUTE : + FReplyStatus.ReplyStatusType := rsTraceRoute; + Id_ICMP_DATAGRAM_CONV : + FReplyStatus.ReplyStatusType := rsErrorDatagramConversion; + Id_ICMP_MOB_HOST_REDIR : + FReplyStatus.ReplyStatusType := rsMobileHostRedir; + Id_ICMP_IPv6_WHERE_ARE_YOU : + FReplyStatus.ReplyStatusType := rsIPv6WhereAreYou; + Id_ICMP_IPv6_I_AM_HERE : + FReplyStatus.ReplyStatusType := rsIPv6IAmHere; + Id_ICMP_MOB_REG_REQ, Id_ICMP_MOB_REG_REPLY : + FReplyStatus.ReplyStatusType := rsMobileHostReg; + Id_ICMP_PHOTURIS : + FReplyStatus.ReplyStatusType := rsErrorSecurityFailure; + else + raise EIdICMPException.Create(RSICMPNonEchoResponse);// RSICMPNonEchoResponse = 'Non-echo type response received' + end; // case + + // check if we got a reply to the packet that was actually sent + case FReplyStatus.ReplyStatusType of + rsEcho: + begin + LActualSeqID := LIcmp.icmp_hdr.icmp_hun.echo_seq; + RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIdx)); + end; + rsTimeStamp: + begin + LActualSeqID := LIcmp.icmp_hdr.icmp_hun.echo_seq; + LIcmpts := TIdICMPTs.Create; + try + LIcmpts.ReadStruct(FBufReceive, LIpHeaderLen); + RTTime := (LIcmpts.ttime and $80000000) - (LIcmpts.otime and $80000000); + finally + LIcmpts.Free; + end; + end; + else + begin + // not an echo or timestamp reply: the original IP frame is + // contained withing the DATA section of the packet... + // pOriginalIP := PIdIPHdr(@picmp^.icmp_dun.data); + + // TODO: verify this! I don't think it is indexing far enough into the data + LActualSeqID := BytesToUInt16(FBufReceive, LIpHeaderLen+8+6);//pOriginalICMP^.icmp_hun.echo.seq; + RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIpHeaderLen+8+8)); //pOriginalICMP^.icmp_dun.ts.otime; + + // move to offset + // pOriginalICMP := Pointer(PtrUInt(pOriginalIP) + (iIpHeaderLen)); + // extract information from original ICMP frame + // ActualSeqID := pOriginalICMP^.icmp_hun.echo.seq; + // RTTime := Ticks64 - pOriginalICMP^.icmp_dun.ts.otime; + // Result := pOriginalICMP^.icmp_hun.echo.seq = wSeqNo; + end; + end; + + Result := LActualSeqID = wSeqNo;//;picmp^.icmp_hun.echo.seq = wSeqNo; + if Result then + begin + if FReplyStatus.ReplyStatusType = rsEcho then begin + FReplyStatus.BytesReceived := BytesRead - (Id_IP_HSIZE + ICMP_MIN + SizeOf(UInt32)); + end else begin + FReplyStatus.BytesReceived := BytesRead - (Id_IP_HSIZE + ICMP_MIN); + end; + + FReplyStatus.FromIpAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(Licmp.ip_hdr.ip_src.s_l)); + FReplyStatus.ToIpAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(Licmp.ip_hdr.ip_dst.s_l)); + FReplyStatus.MsgType := LIcmp.icmp_hdr.icmp_type; //picmp^.icmp_type; + FReplyStatus.MsgCode := LIcmp.icmp_hdr.icmp_code; //picmp^.icmp_code; + FReplyStatus.SequenceId := LActualSeqID; + FReplyStatus.MsRoundTripTime := RTTime; + FReplyStatus.TimeToLive := LIcmp.ip_hdr.ip_ttl; + // now process our message stuff + + case FReplyStatus.MsgType of + Id_ICMP_UNREACH: + begin + case FReplyStatus.MsgCode of + Id_ICMP_UNREACH_NET : FReplyStatus.Msg := RSICMPNetUnreachable; + Id_ICMP_UNREACH_HOST : FReplyStatus.Msg := RSICMPHostUnreachable; + Id_ICMP_UNREACH_PROTOCOL : FReplyStatus.Msg := RSICMPProtUnreachable; + Id_ICMP_UNREACH_NEEDFRAG : FReplyStatus.Msg := RSICMPFragmentNeeded; + Id_ICMP_UNREACH_SRCFAIL : FReplyStatus.Msg := RSICMPSourceRouteFailed; + Id_ICMP_UNREACH_NET_UNKNOWN : FReplyStatus.Msg := RSICMPDestNetUnknown; + Id_ICMP_UNREACH_HOST_UNKNOWN : FReplyStatus.Msg := RSICMPDestHostUnknown; + Id_ICMP_UNREACH_ISOLATED : FReplyStatus.Msg := RSICMPSourceIsolated; + Id_ICMP_UNREACH_NET_PROHIB : FReplyStatus.Msg := RSICMPDestNetProhibitted; + Id_ICMP_UNREACH_HOST_PROHIB : FReplyStatus.Msg := RSICMPDestHostProhibitted; + Id_ICMP_UNREACH_TOSNET : FReplyStatus.Msg := RSICMPTOSNetUnreach; + Id_ICMP_UNREACH_TOSHOST : FReplyStatus.Msg := RSICMPTOSHostUnreach; + Id_ICMP_UNREACH_FILTER_PROHIB : FReplyStatus.Msg := RSICMPAdminProhibitted; + Id_ICMP_UNREACH_HOST_PRECEDENCE : FReplyStatus.Msg := RSICMPHostPrecViolation; + Id_ICMP_UNREACH_PRECEDENCE_CUTOFF : FReplyStatus.Msg := RSICMPPrecedenceCutoffInEffect; + end; + end; + Id_ICMP_TIMXCEED: + begin + case FReplyStatus.MsgCode of + 0 : FReplyStatus.Msg := RSICMPTTLExceeded; + 1 : FReplyStatus.Msg := RSICMPFragAsmExceeded; + end; + end; + Id_ICMP_PARAMPROB : FReplyStatus.Msg := IndyFormat(RSICMPParamError, [FReplyStatus.MsgCode]); + Id_ICMP_REDIRECT: + begin + FReplyStatus.RedirectTo := MakeUInt32IntoIPv4Address(GStack.NetworkToHOst(LIcmp.icmp_hdr.icmp_hun.gateway_s_l)); + case FReplyStatus.MsgCode of + 0 : FReplyStatus.Msg := RSICMPRedirNet; + 1 : FReplyStatus.Msg := RSICMPRedirHost; + 2 : FReplyStatus.Msg := RSICMPRedirTOSNet; + 3 : FReplyStatus.Msg := RSICMPRedirTOSHost; + end; + end; + Id_ICMP_SOURCEQUENCH : FReplyStatus.Msg := RSICMPSourceQuenchMsg; + Id_ICMP_ECHOREPLY, Id_ICMP_ECHO : FReplyStatus.Msg := RSICMPEcho; + Id_ICMP_TSTAMP, Id_ICMP_TSTAMPREPLY : FReplyStatus.Msg := RSICMPTimeStamp; + Id_ICMP_IREQ, Id_ICMP_IREQREPLY : FReplyStatus.Msg := RSICMPTimeStamp; + Id_ICMP_MASKREQ, Id_ICMP_MASKREPLY : FReplyStatus.Msg := RSICMPMaskRequest; + Id_ICMP_TRACEROUTE : + begin + case FReplyStatus.MsgCode of + Id_ICMP_TRACEROUTE_PACKET_FORWARDED : FReplyStatus.Msg := RSICMPTracePacketForwarded; + Id_ICMP_TRACEROUTE_NO_ROUTE : FReplyStatus.Msg := RSICMPTraceNoRoute; + end; + end; + Id_ICMP_DATAGRAM_CONV: + begin + case FReplyStatus.MsgCode of + Id_ICMP_CONV_UNSPEC : FReplyStatus.Msg := RSICMPTracePacketForwarded; + Id_ICMP_CONV_DONTCONV_OPTION : FReplyStatus.Msg := RSICMPTraceNoRoute; + Id_ICMP_CONV_UNKNOWN_MAN_OPTION : FReplyStatus.Msg := RSICMPConvUnknownMandOptPresent; + Id_ICMP_CONV_UNKNWON_UNSEP_OPTION : FReplyStatus.Msg := RSICMPConvKnownUnsupportedOptionPresent; + Id_ICMP_CONV_UNSEP_TRANSPORT : FReplyStatus.Msg := RSICMPConvUnsupportedTransportProtocol; + Id_ICMP_CONV_OVERALL_LENGTH_EXCEEDED : FReplyStatus.Msg := RSICMPConvOverallLengthExceeded; + Id_ICMP_CONV_IP_HEADER_LEN_EXCEEDED : FReplyStatus.Msg := RSICMPConvIPHeaderLengthExceeded; + Id_ICMP_CONV_TRANS_PROT_255 : FReplyStatus.Msg := RSICMPConvTransportProtocol_255; + Id_ICMP_CONV_PORT_OUT_OF_RANGE : FReplyStatus.Msg := RSICMPConvPortConversionOutOfRange; + Id_ICMP_CONV_TRANS_HEADER_LEN_EXCEEDED : FReplyStatus.Msg := RSICMPConvTransportHeaderLengthExceeded; + Id_ICMP_CONV_32BIT_ROLLOVER_AND_ACK : FReplyStatus.Msg := RSICMPConv32BitRolloverMissingAndACKSet; + Id_ICMP_CONV_UNKNOWN_MAN_TRANS_OPTION : FReplyStatus.Msg := RSICMPConvUnknownMandatoryTransportOptionPresent; + end; + end; + Id_ICMP_MOB_HOST_REDIR : FReplyStatus.Msg := RSICMPMobileHostRedirect; + Id_ICMP_IPv6_WHERE_ARE_YOU : FReplyStatus.Msg := RSICMPIPv6WhereAreYou; + Id_ICMP_IPv6_I_AM_HERE : FReplyStatus.Msg := RSICMPIPv6IAmHere; + Id_ICMP_MOB_REG_REQ, Id_ICMP_MOB_REG_REPLY : FReplyStatus.Msg := RSICMPIPv6IAmHere; + Id_ICMP_SKIP : FReplyStatus.Msg := RSICMPSKIP; + Id_ICMP_PHOTURIS : + begin + case FReplyStatus.MsgCode of + Id_ICMP_BAD_SPI : FReplyStatus.Msg := RSICMPSecBadSPI; + Id_ICMP_AUTH_FAILED : FReplyStatus.Msg := RSICMPSecAuthenticationFailed; + Id_ICMP_DECOMPRESS_FAILED : FReplyStatus.Msg := RSICMPSecDecompressionFailed; + Id_ICMP_DECRYPTION_FAILED : FReplyStatus.Msg := RSICMPSecDecryptionFailed; + Id_ICMP_NEED_AUTHENTICATION : FReplyStatus.Msg := RSICMPSecNeedAuthentication; + Id_ICMP_NEED_AUTHORIZATION : FReplyStatus.Msg := RSICMPSecNeedAuthorization; + end; + end; + end; + end; + finally + FreeAndNil(LIcmp); + end; +end; + +procedure TIdCustomIcmpClient.PrepareEchoRequestIPv4(const ABuffer: String); +var + LIcmp: TIdICMPHdr; + LIdx: UInt32; + LBuffer: TIdBytes; + LBufferLen: Integer; +begin + LBuffer := ToBytes(ABuffer, IndyTextEncoding_8Bit); + LBufferLen := IndyMin(Length(LBuffer), FPacketSize); + + SetLength(FBufIcmp, ICMP_MIN + SizeOf(TIdTicks) + LBufferLen); + FillBytes(FBufIcmp, Length(FBufIcmp), 0); + SetLength(FBufReceive, Length(FBufIcmp) + Id_IP_HSIZE); + + LIdx := 0; + LIcmp := TIdICMPHdr.Create; + try + LIcmp.icmp_type := Id_ICMP_ECHO; + LIcmp.icmp_code := 0; + LIcmp.icmp_sum := 0; + LIcmp.icmp_hun.echo_id := Word(CurrentProcessId); + LIcmp.icmp_hun.echo_seq := wSeqNo; + LIcmp.WriteStruct(FBufIcmp, LIdx); + CopyTIdTicks(Ticks64, FBufIcmp, LIdx); + Inc(LIdx, SizeOf(TIdTicks)); + if LBufferLen > 0 then begin + CopyTIdBytes(LBuffer, 0, FBufIcmp, LIdx, LBufferLen); + end; + finally + FreeAndNil(LIcmp); + end; +end; + +{$IFNDEF DOTNET_1_1} +procedure TIdCustomIcmpClient.PrepareEchoRequestIPv6(const ABuffer: String); +var + LIcmp : TIdicmp6_hdr; + LIdx : UInt32; + LBuffer: TIdBytes; + LBufferLen: Integer; +begin + LBuffer := ToBytes(ABuffer, IndyTextEncoding_8Bit); + LBufferLen := IndyMin(Length(LBuffer), FPacketSize); + + SetLength(FBufIcmp, ICMP_MIN + SizeOf(TIdTicks) + LBufferLen); + FillBytes(FBufIcmp, Length(FBufIcmp), 0); + SetLength(FBufReceive, Length(FBufIcmp) + (Id_IPv6_HSIZE*2)); + + LIdx := 0; + LIcmp := TIdicmp6_hdr.Create; + try + LIcmp.icmp6_type := ICMP6_ECHO_REQUEST; + LIcmp.icmp6_code := 0; + LIcmp.data.icmp6_un_data16[0] := Word(CurrentProcessId); + LIcmp.data.icmp6_un_data16[1] := wSeqNo; + LIcmp.icmp6_cksum := 0; + LIcmp.WriteStruct(FBufIcmp, LIdx); + CopyTIdTicks(Ticks64, FBufIcmp, LIdx); + Inc(LIdx, SizeOf(TIdTicks)); + if LBufferLen > 0 then begin + CopyTIdBytes(LBuffer, 0, FBufIcmp, LIdx, LBufferLen); + end; + finally + FreeAndNil(LIcmp); + end; +end; + +function TIdCustomIcmpClient.DecodeIPv6Packet(BytesRead: UInt32): Boolean; +var + LIdx : UInt32; + LIcmp : TIdicmp6_hdr; + RTTime : UInt32; + LActualSeqID : Word; +begin + LIdx := 0; + LIcmp := TIdicmp6_hdr.Create; + try + // Note that IPv6 raw headers are not being returned. + LIcmp.ReadStruct(FBufReceive, LIdx); + + case LIcmp.icmp6_type of + ICMP6_ECHO_REQUEST, + ICMP6_ECHO_REPLY : FReplyStatus.ReplyStatusType := rsEcho; + //group membership messages + ICMP6_MEMBERSHIP_QUERY : ; + ICMP6_MEMBERSHIP_REPORT : ; + ICMP6_MEMBERSHIP_REDUCTION : ; + //errors + ICMP6_DST_UNREACH : FReplyStatus.ReplyStatusType := rsErrorUnreachable; + ICMP6_PACKET_TOO_BIG : FReplyStatus.ReplyStatusType := rsErrorPacketTooBig; + ICMP6_TIME_EXCEEDED : FReplyStatus.ReplyStatusType := rsErrorTTLExceeded; + ICMP6_PARAM_PROB : FReplyStatus.ReplyStatusType := rsErrorParameter; + else FReplyStatus.ReplyStatusType := rsError; + end; + FReplyStatus.MsgType := LIcmp.icmp6_type; //picmp^.icmp_type; + FReplyStatus.MsgCode := LIcmp.icmp6_code; + + //errors are values less than ICMP6_INFOMSG_MASK + if LIcmp.icmp6_type < ICMP6_INFOMSG_MASK then + begin + //read info from the original packet part + LIcmp.ReadStruct(FBufReceive, LIdx); + end; + + LActualSeqID := LIcmp.data.icmp6_seq; + Result := LActualSeqID = wSeqNo; + + RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIdx)); + Inc(LIdx, SizeOf(TIdTicks)); + + if Result then + begin + FReplyStatus.BytesReceived := BytesRead - LIdx; + FReplyStatus.SequenceId := LActualSeqID; + FReplyStatus.MsRoundTripTime := RTTime; + // TimeToLive := FBufReceive[8]; + // TimeToLive := pip^.ip_ttl; + FReplyStatus.TimeToLive := FPkt.TTL; + FReplyStatus.FromIpAddress := FPkt.SourceIP; + FReplyStatus.ToIpAddress := FPkt.DestIP; + + case FReplyStatus.MsgType of + ICMP6_ECHO_REQUEST, ICMP6_ECHO_REPLY : FReplyStatus.Msg := RSICMPEcho; + ICMP6_TIME_EXCEEDED : + begin + case FReplyStatus.MsgCode of + ICMP6_TIME_EXCEED_TRANSIT : FReplyStatus.Msg := RSICMPHopLimitExceeded; + ICMP6_TIME_EXCEED_REASSEMBLY : FReplyStatus.Msg := RSICMPFragAsmExceeded; + end; + end; + ICMP6_DST_UNREACH : + begin + case FReplyStatus.MsgCode of + ICMP6_DST_UNREACH_NOROUTE : FReplyStatus.Msg := RSICMPNoRouteToDest; + ICMP6_DST_UNREACH_ADMIN : FReplyStatus.Msg := RSICMPAdminProhibitted; + ICMP6_DST_UNREACH_ADDR : FReplyStatus.Msg := RSICMPHostUnreachable; + ICMP6_DST_UNREACH_NOPORT : FReplyStatus.Msg := RSICMPProtUnreachable; + ICMP6_DST_UNREACH_SOURCE_FILTERING : FReplyStatus.Msg := RSICMPSourceFilterFailed; + ICMP6_DST_UNREACH_REJCT_DST : FReplyStatus.Msg := RSICMPRejectRoutToDest; + end; + end; + ICMP6_PACKET_TOO_BIG : FReplyStatus.Msg := IndyFormat(RSICMPPacketTooBig, [LIcmp.data.icmp6_mtu]); + ICMP6_PARAM_PROB : + begin + case FReplyStatus.MsgCode of + ICMP6_PARAMPROB_HEADER : FReplyStatus.Msg := IndyFormat(RSICMPParamHeader, [LIcmp.data.icmp6_pptr]); + ICMP6_PARAMPROB_NEXTHEADER : FReplyStatus.Msg := IndyFormat(RSICMPParamNextHeader, [LIcmp.data.icmp6_pptr]); + ICMP6_PARAMPROB_OPTION : FReplyStatus.Msg := IndyFormat(RSICMPUnrecognizedOpt, [LIcmp.data.icmp6_pptr]); + end; + end; + ICMP6_MEMBERSHIP_QUERY : ; + ICMP6_MEMBERSHIP_REPORT : ; + ICMP6_MEMBERSHIP_REDUCTION :; + end; + end; + finally + FreeAndNil(LIcmp); + end; +end; +{$ENDIF} + +procedure TIdCustomIcmpClient.Send(const AHost: string; const APort: TIdPort; + const ABuffer: TIdBytes); +var + LBuffer : TIdBytes; + LIP : String; +begin + LBuffer := ABuffer; + LIP := GStack.ResolveHost(AHost, IPVersion); + GStack.WriteChecksum(Binding.Handle, LBuffer, 2, LIP, APort, IPVersion); + FBinding.SendTo(LIP, APort, LBuffer, IPVersion); +end; + +procedure TIdCustomIcmpClient.Send(const ABuffer: TIdBytes); +var + LBuffer : TIdBytes; + LIP : String; +begin + LBuffer := ABuffer; + LIP := GStack.ResolveHost(Host, IPVersion); + GStack.WriteChecksum(Binding.Handle, LBuffer, 2, LIP, Port, IPVersion); + FBinding.SendTo(LIP, Port, LBuffer, IPVersion); +end; + +function TIdCustomIcmpClient.GetPacketSize: Integer; +begin + Result := FPacketSize; +end; + +procedure TIdCustomIcmpClient.SetPacketSize(const AValue: Integer); +begin + if AValue < 0 then begin + FPacketSize := 0; + end else begin + FPacketSize := IndyMin(AValue, MAX_PACKET_SIZE); + end; +end; + +procedure TIdCustomIcmpClient.InternalPing(const AIP, ABuffer: String; SequenceID: Word); +begin + if SequenceID <> 0 then begin + wSeqNo := SequenceID; + end; + PrepareEchoRequest(ABuffer); + SendEchoRequest(AIP); + GetEchoReply; + Binding.CloseSocket; + DoReply; + Inc(wSeqNo); // SG 25/1/02: Only increase sequence number when finished. +end; + +procedure TIdCustomIcmpClient.SendEchoRequest(const AIP: String); +begin + Send(AIP, 0, FBufIcmp); +end; + +{ TIdIcmpClient } + +procedure TIdIcmpClient.Ping(const ABuffer: String; SequenceID: Word); +begin + InternalPing(GStack.ResolveHost(Host, IPVersion), ABuffer, SequenceID); +end; + +end. diff --git a/indy/Core/IdIntercept.pas b/indy/Core/IdIntercept.pas new file mode 100644 index 0000000..aef5dc5 --- /dev/null +++ b/indy/Core/IdIntercept.pas @@ -0,0 +1,256 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 3/10/2005 12:00:46 AM JPMugaas + Minor problem Craig Peterson had noted in an E-Mail to me. + + Rev 1.9 11/30/04 6:19:12 PM RLebeau + Promoted the TIdConnectionIntercept.Intercept property from protected to + published + + Rev 1.8 2004.02.03 4:16:44 PM czhower + For unit name changes. + + Rev 1.7 2004.01.20 10:03:24 PM czhower + InitComponent + + Rev 1.6 5/12/2003 12:33:32 AM GGrieve + add Data from BlockCipher descendent + + Rev 1.5 2003.10.14 1:26:48 PM czhower + Uupdates + Intercept support + + Rev 1.4 2003.10.11 5:48:16 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.3 10/5/2003 3:20:46 PM BGooijen + .net + + Rev 1.2 2003.10.01 1:12:34 AM czhower + .Net + + Rev 1.1 3/5/2003 10:59:48 PM BGooijen + Fixed (i know, the SendBuffer looks bad) + + Rev 1.0 11/13/2002 08:44:42 AM JPMugaas + + 2002-03-01 - Andrew P.Rybin + - Nested Intercept support (ex: ->logging->compression->encryption) + + 2002-04-09 - Chuck Smith + - set ABuffer.Position := 0; in OnSend/OnReceive for Nested Stream send/receive +} + +unit IdIntercept; + +interface + +{$I IdCompilerDefines.inc} +//here only to put FPC in Delphi mode + +uses + Classes, + IdGlobal, IdBaseComponent, IdBuffer, IdException; + +type + EIdInterceptCircularLink = class(EIdException); + TIdConnectionIntercept = class; + TIdInterceptNotifyEvent = procedure(ASender: TIdConnectionIntercept) of object; + TIdInterceptStreamEvent = procedure(ASender: TIdConnectionIntercept; var ABuffer: TIdBytes) of object; + + TIdConnectionIntercept = class(TIdBaseComponent) + protected + FConnection: TComponent; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIntercept: TIdConnectionIntercept; + FIsClient: Boolean; + {$IFDEF USE_OBJECT_ARC} + // When ARC is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so + // we will provide separate properties for those purposes + // + // TODO; use TValue instead of separating them + // + FDataObject: TObject; + FDataValue: PtrInt; + {$ELSE} + FData: TObject; + {$ENDIF} + + FOnConnect: TIdInterceptNotifyEvent; + FOnDisconnect: TIdInterceptNotifyEvent; + FOnReceive: TIdInterceptStreamEvent; + FOnSend: TIdInterceptStreamEvent; + // + procedure InitComponent; override; + {$IFNDEF USE_OBJECT_ARC} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + {$ENDIF} + procedure SetIntercept(AValue: TIdConnectionIntercept); + // + public + procedure Connect(AConnection: TComponent); virtual; + procedure Disconnect; virtual; + procedure Receive(var VBuffer: TIdBytes); virtual; + procedure Send(var VBuffer: TIdBytes); virtual; + // + property Connection: TComponent read FConnection; + property IsClient: Boolean read FIsClient; + + // user can use this to keep context + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read FDataObject write FDataObject; + property DataValue: PtrInt read FDataValue write FDataValue; + {$ELSE} + property Data: TObject read FData write FData; + {$ENDIF} + published + property Intercept: TIdConnectionIntercept read FIntercept write SetIntercept; + property OnConnect: TIdInterceptNotifyEvent read FOnConnect write FOnConnect; + property OnDisconnect: TIdInterceptNotifyEvent read FOnDisconnect write FOnDisconnect; + property OnReceive: TIdInterceptStreamEvent read FOnReceive write FOnReceive; + property OnSend: TIdInterceptStreamEvent read FOnSend write FOnSend; + end; + + TIdServerIntercept = class(TIdBaseComponent) + public + procedure Init; virtual; abstract; + function Accept(AConnection: TComponent): TIdConnectionIntercept; virtual; abstract; + end; + +implementation +uses + IdResourceStringsCore; + +{ TIdIntercept } + +procedure TIdConnectionIntercept.Disconnect; +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Disconnect; + end; + if Assigned(OnDisconnect) then begin + OnDisconnect(Self); + end; + FConnection := nil; +end; + +procedure TIdConnectionIntercept.Connect(AConnection: TComponent); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + FConnection := AConnection; + if Assigned(OnConnect) then begin + OnConnect(Self); + end; + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Connect(AConnection); + end; +end; + +procedure TIdConnectionIntercept.Receive(var VBuffer: TIdBytes); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Receive(VBuffer); + end; + if Assigned(OnReceive) then begin + OnReceive(Self, VBuffer); + end; +end; + +procedure TIdConnectionIntercept.Send(var VBuffer: TIdBytes); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; +begin + if Assigned(OnSend) then begin + OnSend(Self, VBuffer); + end; + LIntercept := Intercept; + if LIntercept <> nil then begin + LIntercept.Send(VBuffer); + end; +end; + +procedure TIdConnectionIntercept.SetIntercept(AValue: TIdConnectionIntercept); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIntercept: TIdConnectionIntercept; + LNextValue: TIdConnectionIntercept; +begin + LIntercept := FIntercept; + if LIntercept <> AValue then + begin + LNextValue := AValue; + while Assigned(LNextValue) do begin + if LNextValue = Self then begin //recursion + raise EIdInterceptCircularLink.CreateFmt(RSInterceptCircularLink, [ClassName]); + end; + LNextValue := LNextValue.Intercept; + end; + + // under ARC, all weak references to a freed object get nil'ed automatically + + {$IFNDEF USE_OBJECT_ARC} + // remove self from the Intercept's free notification list {Do not Localize} + if Assigned(LIntercept) then begin + LIntercept.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FIntercept := AValue; + + {$IFNDEF USE_OBJECT_ARC} + // add self to the Intercept's free notification list {Do not Localize} + if Assigned(AValue) then begin + AValue.FreeNotification(Self); + end; + {$ENDIF} + end; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +{$IFNDEF USE_OBJECT_ARC} +procedure TIdConnectionIntercept.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = Intercept) then begin + FIntercept := nil; + end; + inherited Notification(AComponent, OPeration); +end; +{$ENDIF} + +procedure TIdConnectionIntercept.InitComponent; +begin + inherited InitComponent; + FIsClient := True; +end; + +end. diff --git a/indy/Core/IdInterceptSimLog.pas b/indy/Core/IdInterceptSimLog.pas new file mode 100644 index 0000000..8dfbcd5 --- /dev/null +++ b/indy/Core/IdInterceptSimLog.pas @@ -0,0 +1,157 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 7/23/04 6:40:08 PM RLebeau + Added extra exception handling to Connect() + + Rev 1.5 2004.05.20 11:39:10 AM czhower + IdStreamVCL + + Rev 1.4 2004.02.03 4:17:18 PM czhower + For unit name changes. + + Rev 1.3 10/19/2003 11:38:26 AM DSiders + Added localization comments. + + Rev 1.2 2003.10.18 1:56:46 PM czhower + Now uses ASCII instead of binary format. + + Rev 1.1 2003.10.17 6:16:20 PM czhower + Functional complete. +} + +unit IdInterceptSimLog; + +{ + This file uses string outputs instead of binary so that the results can be + viewed and modified with notepad if necessary. + + Most times a Send/Receive includes a writeln, but may not always. We write out + an additional EOL to guarantee separation in notepad. + + It also auto detects when an EOL can be used instead. + + TODO: Can also change it to detect several EOLs and non binary and use :Lines:x +} + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, IdIntercept, IdBaseComponent; + +type + TIdInterceptSimLog = class(TIdConnectionIntercept) + private + protected + FFilename: string; + FStream: TStream; + // + procedure SetFilename(const AValue: string); + procedure WriteRecord(const ATag: string; const ABuffer: TIdBytes); + public + procedure Connect(AConnection: TComponent); override; + procedure Disconnect; override; + procedure Receive(var ABuffer: TIdBytes); override; + procedure Send(var ABuffer: TIdBytes); override; + published + property Filename: string read FFilename write SetFilename; + end; + +implementation + +uses + {$IFDEF DOTNET} + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + IdException, IdResourceStringsCore, SysUtils; + +{ TIdInterceptSimLog } + +procedure TIdInterceptSimLog.Connect(AConnection: TComponent); +begin + inherited Connect(AConnection); + // Warning! This will overwrite any existing file. It makes no sense + // to concatenate sim logs. + FStream := TIdFileCreateStream.Create(Filename); +end; + +procedure TIdInterceptSimLog.Disconnect; +begin + FreeAndNil(FStream); + inherited Disconnect; +end; + +procedure TIdInterceptSimLog.Receive(var ABuffer: TIdBytes); +begin + // let the next Intercept in the chain decode its data first + inherited Receive(ABuffer); + WriteRecord('Recv', ABuffer); {do not localize} +end; + +procedure TIdInterceptSimLog.Send(var ABuffer: TIdBytes); +begin + WriteRecord('Send', ABuffer); {do not localize} + // let the next Intercept in the chain encode its data next + inherited Send(ABuffer); +end; + +procedure TIdInterceptSimLog.SetFilename(const AValue: string); +begin + if Assigned(FStream) then begin + raise EIdException.Create(RSLogFileAlreadyOpen); + end; + FFilename := AValue; +end; + +procedure TIdInterceptSimLog.WriteRecord(const ATag: string; const ABuffer: TIdBytes); +var + i: Integer; + LUseEOL: Boolean; + LSize: Integer; +begin + LUseEOL := False; + LSize := Length(ABuffer); + if LSize > 1 then begin + if (ABuffer[LSize - 2] = 13) and (ABuffer[LSize - 1] = 10) then begin + LUseEOL := True; + for i := 0 to LSize - 3 do begin + // If any binary, CR or LF + if (ABuffer[i] < 32) or (ABuffer[i] > 127) then begin + LUseEOL := False; + Break; + end; + end; + end; + end; + with FStream do begin + if LUseEOL then begin + WriteLn(ATag + ':EOL'); {do not localize} + end else begin + WriteLn(ATag + ':Bytes:' + IntToStr(LSize)); {do not localize} + end; + end; + WriteStringToStream(FStream, ''); + WriteTIdBytesToStream(FStream, ABuffer, LSize); + WriteStringToStream(FStream, EOL); +end; + +end. diff --git a/indy/Core/IdInterceptThrottler.pas b/indy/Core/IdInterceptThrottler.pas new file mode 100644 index 0000000..89f171f --- /dev/null +++ b/indy/Core/IdInterceptThrottler.pas @@ -0,0 +1,106 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2004.02.03 4:17:18 PM czhower + For unit name changes. + + Rev 1.1 2003.10.19 12:10:00 AM czhower + Changed formula to be accurate with smaller numbers. + + Rev 1.0 2003.10.18 11:32:00 PM czhower + Initial checkin + + Rev 1.1 2003.10.14 1:27:16 PM czhower + Uupdates + Intercept support + + Rev 1.0 2003.10.13 6:40:40 PM czhower + Moved from root + + Rev 1.0 11/13/2002 07:55:12 AM JPMugaas +} + +unit IdInterceptThrottler; + +interface +{$i IdCompilerDefines.inc} + +uses + IdComponent, IdIntercept, IdGlobal; + +type + TIdInterceptThrottler = class(TIdConnectionIntercept) + protected + FBitsPerSec: Integer; + FRecvBitsPerSec: Integer; + FSendBitsPerSec: Integer; + procedure SetBitsPerSec(AValue: Integer); + public + procedure Receive(var ABuffer: TIdBytes); override; + procedure Send(var ABuffer: TIdBytes); override; + published + property BitsPerSec: Integer read FBitsPerSec write SetBitsPerSec; + property RecvBitsPerSec: Integer read FRecvBitsPerSec write FRecvBitsPerSec; + property SendBitsPerSec: Integer read FSendBitsPerSec write FSendBitsPerSec; + end; + +implementation + +uses + IdAntiFreezeBase, IdException; + +{ TIdInterceptThrottler } + +procedure TIdInterceptThrottler.Receive(var ABuffer: TIdBytes); +var + LInterval: Int64; +begin + inherited Receive(ABuffer); + if RecvBitsPerSec > 0 then begin + LInterval := (Int64(Length(ABuffer)) * 8 * 1000) div RecvBitsPerSec; + while LInterval > MaxInt do begin + TIdAntiFreezeBase.Sleep(MaxInt); + Dec(LInterval, MaxInt); + end; + TIdAntiFreezeBase.Sleep(Integer(LInterval)); + end; +end; + +procedure TIdInterceptThrottler.Send(var ABuffer: TIdBytes); +var + LInterval: Int64; +begin + inherited Send(ABuffer); + if SendBitsPerSec > 0 then begin + LInterval := (Int64(Length(ABuffer)) * 8 * 1000) div SendBitsPerSec; + while LInterval > MaxInt do begin + TIdAntiFreezeBase.Sleep(MaxInt); + Dec(LInterval, MaxInt); + end; + TIdAntiFreezeBase.Sleep(Integer(LInterval)); + end; +end; + +procedure TIdInterceptThrottler.SetBitsPerSec(AValue: Integer); +begin + FBitsPerSec := AValue; + FRecvBitsPerSec := AValue; + FSendBitsPerSec := AValue; +end; + +end. + diff --git a/indy/Core/IdLogBase.pas b/indy/Core/IdLogBase.pas new file mode 100644 index 0000000..a7c6c45 --- /dev/null +++ b/indy/Core/IdLogBase.pas @@ -0,0 +1,198 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 2004.02.03 4:17:14 PM czhower + For unit name changes. + + Rev 1.4 2004.01.20 10:03:28 PM czhower + InitComponent + + Rev 1.3 2003.10.17 6:15:54 PM czhower + Upgrades + + Rev 1.2 2003.10.14 1:27:08 PM czhower + Uupdates + Intercept support + + Rev 1.1 6/16/2003 10:39:02 AM EHill + Done: Expose Open/Close as public in TIdLogBase + + Rev 1.0 11/13/2002 07:55:58 AM JPMugaas +} + +unit IdLogBase; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdIntercept, IdGlobal, IdSocketHandle, IdBaseComponent; + +type + TIdLogBase = class(TIdConnectionIntercept) + protected + FActive: Boolean; + FLogTime: Boolean; + FReplaceCRLF: Boolean; + FStreamedActive: Boolean; + // + procedure InitComponent; override; + procedure LogStatus(const AText: string); virtual; abstract; + procedure LogReceivedData(const AText, AData: string); virtual; abstract; + procedure LogSentData(const AText, AData: string); virtual; abstract; + procedure SetActive(AValue: Boolean); virtual; + procedure Loaded; override; + function ReplaceCR(const AString : String) : String; + public + procedure Open; virtual; + procedure Close; virtual; + procedure Connect(AConnection: TComponent); override; + destructor Destroy; override; + procedure Disconnect; override; + procedure Receive(var ABuffer: TIdBytes); override; + procedure Send(var ABuffer: TIdBytes); override; + published + property Active: Boolean read FActive write SetActive default False; + property LogTime: Boolean read FLogTime write FLogTime default True; + property ReplaceCRLF: Boolean read FReplaceCRLF write FReplaceCRLF default true; + end; + +implementation + +uses + IdResourceStringsCore, SysUtils; + +const + LOldStr : array [0..2] of string = + ( EOL, CR, LF ); + LNewStr : array [0..2] of string = + ( RSLogEOL, RSLogCR, RSLogLF ); + +{ TIdLogBase } + +procedure TIdLogBase.Close; +begin +end; + +procedure TIdLogBase.Connect(AConnection: TComponent); +begin + inherited Connect(AConnection); + if FActive then begin + LogStatus(RSLogConnected); + end; +end; + +destructor TIdLogBase.Destroy; +begin + Active := False; + inherited Destroy; +end; + +procedure TIdLogBase.Disconnect; +begin + if FActive then begin + LogStatus(RSLogDisconnected); + end; + inherited Disconnect; +end; + +procedure TIdLogBase.InitComponent; +begin + inherited InitComponent; + FLogTime := True; + ReplaceCRLF := True; +end; + +procedure TIdLogBase.Loaded; +begin + inherited Loaded; + Active := FStreamedActive; +end; + +procedure TIdLogBase.Open; +begin +end; + +procedure TIdLogBase.Receive(var ABuffer: TIdBytes); +var + s: string; + LMsg: string; +begin + // let the next Intercept in the chain decode its data first + inherited Receive(ABuffer); + + if FActive then begin + LMsg := ''; + if LogTime then begin + LMsg := DateTimeToStr(Now); + end; + s := BytesToStringRaw(ABuffer); + if FReplaceCRLF then begin + s := ReplaceCR(S); + end; + LogReceivedData(LMsg, s); + end; +end; + +function TIdLogBase.ReplaceCR(const AString: String): String; +begin + Result := StringsReplace(AString, LOldStr, LNewStr); +end; + +procedure TIdLogBase.Send(var ABuffer: TIdBytes); +var + s: string; + LMsg: string; +begin + if FActive then begin + LMsg := ''; + if LogTime then begin + LMsg := DateTimeToStr(Now); + end; + s := BytesToStringRaw(ABuffer); + if FReplaceCRLF then begin + s := ReplaceCR(S); + end; + LogSentData(LMsg, s); + end; + + // let the next Intercept in the chain encode its data next + inherited Send(ABuffer); +end; + +procedure TIdLogBase.SetActive(AValue: Boolean); +begin + if IsDesignTime or IsLoading then begin + FStreamedActive := AValue; + end + else if FActive <> AValue then + begin + FActive := AValue; + if FActive then begin + Open; + end else begin + Close; + end; + end; +end; + +end. + + diff --git a/indy/Core/IdLogDebug.pas b/indy/Core/IdLogDebug.pas new file mode 100644 index 0000000..e021bd7 --- /dev/null +++ b/indy/Core/IdLogDebug.pas @@ -0,0 +1,72 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 8/6/04 12:21:28 AM RLebeau + Removed TIdLogDebugTarget type, not used anywhere + + Rev 1.3 2004.02.03 4:17:16 PM czhower + For unit name changes. + + Rev 1.2 2003.10.17 8:17:22 PM czhower + Removed const + + Rev 1.1 4/22/2003 4:34:22 PM BGooijen + DebugOutput is now in IdGlobal + + Rev 1.0 11/13/2002 07:56:02 AM JPMugaas +} + +unit IdLogDebug; + +interface +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode +uses + IdLogBase; + +type + TIdLogDebug = class(TIdLogBase) + protected + procedure LogStatus(const AText: string); override; + procedure LogReceivedData(const AText, AData: string); override; + procedure LogSentData(const AText, AData: string); override; + end; + +implementation + +uses + IdGlobal; + +{ TIdLogDebug } + +procedure TIdLogDebug.LogReceivedData(const AText, AData: string); +begin + DebugOutput('Recv ' + AText + ': ' + AData); {Do not Localize} +end; + +procedure TIdLogDebug.LogSentData(const AText, AData: string); +begin + DebugOutput('Sent ' + AText + ': ' + AData); {Do not Localize} +end; + +procedure TIdLogDebug.LogStatus(const AText: string); +begin + DebugOutput('Stat ' + AText); {Do not Localize} +end; + +end. diff --git a/indy/Core/IdLogEvent.pas b/indy/Core/IdLogEvent.pas new file mode 100644 index 0000000..0161dbb --- /dev/null +++ b/indy/Core/IdLogEvent.pas @@ -0,0 +1,83 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2004.05.20 12:34:28 PM czhower + Removed more non .NET compatible stream read and writes + + Rev 1.1 2003.10.17 8:17:22 PM czhower + Removed const + + Rev 1.0 11/13/2002 07:56:08 AM JPMugaas +} + +unit IdLogEvent; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + IdLogBase, IdBaseComponent, Classes; + +type + TLogItemStatusEvent = procedure(ASender: TComponent; const AText: string) of object; + TLogItemDataEvent = procedure(ASender: TComponent; const AText, AData: string) of object; + + TIdLogEvent = class(TIdLogBase) + protected + FOnReceived: TLogItemDataEvent; + FOnSent: TLogItemDataEvent; + FOnStatus: TLogItemStatusEvent; + // + procedure LogStatus(const AText: string); override; + procedure LogReceivedData(const AText, AData: string); override; + procedure LogSentData(const AText, AData: string); override; + public + published + property OnReceived: TLogItemDataEvent read FOnReceived write FOnReceived; + property OnSent: TLogItemDataEvent read FOnSent write FOnSent; + property OnStatus: TLogItemStatusEvent read FOnStatus write FOnStatus; + end; + +implementation + +{ TIdLogEvent } + +procedure TIdLogEvent.LogReceivedData(const AText, AData: string); +begin + if Assigned(OnReceived) then begin + OnReceived(Self, AText, AData); + end; +end; + +procedure TIdLogEvent.LogSentData(const AText, AData: string); +begin + if Assigned(OnSent) then begin + OnSent(Self, AText, AData); + end; +end; + +procedure TIdLogEvent.LogStatus(const AText: string); +begin + if Assigned(OnStatus) then begin + OnStatus(Self, AText); + end; +end; + +end. diff --git a/indy/Core/IdLogFile.pas b/indy/Core/IdLogFile.pas new file mode 100644 index 0000000..e85c457 --- /dev/null +++ b/indy/Core/IdLogFile.pas @@ -0,0 +1,171 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 7/23/04 6:36:54 PM RLebeau + Added extra exception handling to Open() + + Rev 1.7 2004.05.20 12:34:30 PM czhower + Removed more non .NET compatible stream read and writes + + Rev 1.6 2004.02.03 4:17:16 PM czhower + For unit name changes. + + Rev 1.5 2003.10.17 6:15:54 PM czhower + Upgrades + + Rev 1.4 2003.10.16 11:24:36 AM czhower + Bug fix + + Rev 1.3 10/15/2003 8:00:10 PM DSiders + Added resource string for exception raised in TIdLogFile.SetFilename. + + Rev 1.2 2003.10.14 1:27:10 PM czhower + Uupdates + Intercept support + + Rev 1.1 6/16/2003 11:01:06 AM EHill + Throw exception if the filename is set while the log is open. + Expose Open and Close as public instead of protected. + + Rev 1.0 11/13/2002 07:56:12 AM JPMugaas + + 19-Aug-2001 DSiders + Fixed bug in Open. Use file mode fmCreate when Filename does *not* exist. + + 19-Aug-2001 DSiders + Added protected method TIdLogFile.LogWriteString. + + 19-Aug-2001 DSiders + Changed implementation of TIdLogFile methods LogStatus, LogReceivedData, and + LogSentData to use LogWriteString. + + 19-Aug-2001 DSiders + Added class TIdLogFileEx with the LogFormat method. +} + +unit IdLogFile; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdLogBase; + +type + TIdLogFile = class(TIdLogBase) + protected + FFilename: String; + FFileStream: TStream; + // + procedure LogFormat(const AFormat: string; const AArgs: array of const); virtual; + procedure LogReceivedData(const AText, AData: string); override; + procedure LogSentData(const AText, AData: string); override; + procedure LogStatus(const AText: string); override; + procedure LogWriteString(const AText: string); virtual; + // + procedure SetFilename(const AFilename: String); + public + procedure Open; override; + procedure Close; override; + published + property Filename: String read FFilename write SetFilename; + end; + +implementation + +uses + IdGlobal, IdException, IdResourceStringsCore, IdBaseComponent, SysUtils; + +{ TIdLogFile } + +procedure TIdLogFile.Close; +begin + FreeAndNil(FFileStream); +end; + +procedure TIdLogFile.LogReceivedData(const AText, AData: string); +begin + LogWriteString(RSLogRecv + AText + ': ' + AData + EOL); {Do not translate} +end; + +procedure TIdLogFile.LogSentData(const AText, AData: string); +begin + LogWriteString(RSLogSent + AText + ': ' + AData + EOL); {Do not translate} +end; + +procedure TIdLogFile.LogStatus(const AText: string); +begin + LogWriteString(RSLogStat + AText + EOL); +end; + +procedure TIdLogFile.Open; +begin + if not IsDesignTime then begin + FFileStream := TIdAppendFileStream.Create(Filename); + end; +end; + +procedure TIdLogFile.LogWriteString(const AText: string); +var + LEncoding: IIdTextEncoding; +begin + if Assigned(FFileStream) then begin + LEncoding := IndyTextEncoding_8Bit; + WriteStringToStream(FFileStream, AText, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + end; +end; + +procedure TIdLogFile.LogFormat(const AFormat: string; const AArgs: array of const); +var + sPre: string; + sMsg: string; + sData: string; +begin + // forces Open to be called prior to Connect + if not Active then begin + Active := True; + end; + + sPre := ''; {Do not translate} + sMsg := ''; {Do not translate} + + if LogTime then begin + sPre := DateTimeToStr(Now) + ' '; {Do not translate} + end; + + sData := IndyFormat(AFormat, AArgs); + if FReplaceCRLF then begin + sData := ReplaceCR(sData); + end; + sMsg := sPre + sData + EOL; + + LogWriteString(sMsg); +end; + +procedure TIdLogFile.SetFilename(const AFilename: String); +begin + if Assigned(FFileStream) then begin + raise EIdException.Create(RSLogFileAlreadyOpen); + end; + FFilename := AFilename; +end; + +end. + diff --git a/indy/Core/IdLogStream.pas b/indy/Core/IdLogStream.pas new file mode 100644 index 0000000..6c1752b --- /dev/null +++ b/indy/Core/IdLogStream.pas @@ -0,0 +1,118 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 2004.05.20 12:34:32 PM czhower + Removed more non .NET compatible stream read and writes + + Rev 1.4 2004.01.20 10:03:30 PM czhower + InitComponent + + Rev 1.3 2003.10.17 6:15:56 PM czhower + Upgrades + + Rev 1.2 2003.10.17 4:28:54 PM czhower + Changed stream names to be consistent with IOHandlerStream + + Rev 1.1 2003.10.14 1:27:12 PM czhower + Uupdates + Intercept support + + Rev 1.0 11/13/2002 07:56:18 AM JPMugaas +} + +unit IdLogStream; + +interface +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode +uses + Classes, + IdLogBase, IdGlobal; + +type + TIdLogStream = class(TIdLogBase) + protected + FFreeStreams: Boolean; + FReceiveStream: TStream; + FSendStream: TStream; + // + procedure InitComponent; override; + procedure LogStatus(const AText: string); override; + procedure LogReceivedData(const AText, AData: string); override; + procedure LogSentData(const AText, AData: string); override; + public + procedure Disconnect; override; + // + property FreeStreams: Boolean read FFreeStreams write FFreeStreams; + property ReceiveStream: TStream read FReceiveStream write FReceiveStream; + property SendStream: TStream read FSendStream write FSendStream; + end; + +implementation + uses SysUtils; + +// TODO: This was orginally for VCL. For .Net what do we do? Convert back to +// 7 bit? Log all? Logging all seems to be a disaster. +// Text seems to be best, users are expecting text in this class. But +// this write stream will dump unicode out in .net..... +// So just convert it again back to 7 bit? How is proper to write +// 7 bit to file? Use AnsiString? + +{ TIdLogStream } + +procedure TIdLogStream.Disconnect; +begin + inherited Disconnect; + if FreeStreams then begin + FreeAndNil(FReceiveStream); + FreeAndNil(FSendStream); + end; +end; + +procedure TIdLogStream.InitComponent; +begin + inherited InitComponent; + FFreeStreams := True; +end; + +procedure TIdLogStream.LogReceivedData(const AText, AData: string); +var + LEncoding: IIdTextEncoding; +begin + if FReceiveStream <> nil then begin + LEncoding := IndyTextEncoding_8Bit; + WriteStringToStream(FReceiveStream, AData, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + end; +end; + +procedure TIdLogStream.LogSentData(const AText, AData: string); +var + LEncoding: IIdTextEncoding; +begin + if FSendStream <> nil then begin + LEncoding := IndyTextEncoding_8Bit; + WriteStringToStream(FSendStream, AData, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + end; +end; + +procedure TIdLogStream.LogStatus(const AText: string); +begin + // We just leave this empty because the AText is not part of the stream and we + // do not want to raise an abstract method exception. +end; + +end. diff --git a/indy/Core/IdRawBase.pas b/indy/Core/IdRawBase.pas new file mode 100644 index 0000000..a6e5713 --- /dev/null +++ b/indy/Core/IdRawBase.pas @@ -0,0 +1,306 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.15 7/9/04 4:26:28 PM RLebeau + Removed TIdBytes local variable from Send() + + Rev 1.14 09/06/2004 00:28:00 CCostelloe + Kylix 3 patch + + Rev 1.13 4/25/2004 7:54:26 AM JPMugaas + Fix for AV. + + Rev 1.12 2/8/2004 12:58:42 PM JPMugaas + Should now compile in DotNET. + + Rev 1.11 2004.02.03 4:16:48 PM czhower + For unit name changes. + + Rev 1.10 2/1/2004 6:10:14 PM JPMugaas + Should compile better. + + Rev 1.9 2/1/2004 4:52:34 PM JPMugaas + Removed the rest of the Todo; items. + + Rev 1.8 2004.01.20 10:03:30 PM czhower + InitComponent + + Rev 1.7 2004.01.02 9:38:46 PM czhower + Removed warning + + Rev 1.6 2003.10.24 10:09:54 AM czhower + Compiles + + Rev 1.5 2003.10.20 12:03:08 PM czhower + Added IdStackBSDBase to make it compile again. + + Rev 1.4 10/19/2003 10:41:12 PM BGooijen + Compiles in DotNet and D7 again + + Rev 1.3 10/19/2003 9:34:28 PM BGooijen + SetSocketOption + + Rev 1.2 2003.10.11 5:48:58 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.1 2003.09.30 1:23:00 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 08:45:24 AM JPMugaas +} + +unit IdRawBase; + +interface + +{ + We need to selectively disable some functionality in DotNET with buffers as + we don't want to impact anything else such as TIdICMPClient. +} + +{$I IdCompilerDefines.inc} + +uses + IdComponent, IdGlobal, IdSocketHandle, IdStack, + {$IFDEF MSWINDOWS} + IdWship6, + {$ENDIF} + IdStackConsts; + +const + Id_TIdRawBase_Port = 0; + Id_TIdRawBase_BufferSize = 8192; + GReceiveTimeout = 0; + GFTTL = 128; + +type + TIdRawBase = class(TIdComponent) + protected + FBinding: TIdSocketHandle; + FHost: string; + FPort: TIdPort; + FReceiveTimeout: integer; + FProtocol: TIdSocketProtocol; + FProtocolIPv6 : TIdSocketProtocol; + FTTL: Integer; + FPkt : TIdPacketInfo; + FConnected : Boolean; + // + function GetBinding: TIdSocketHandle; + function GetIPVersion: TIdIPVersion; + // + procedure InitComponent; override; + procedure SetIPVersion(const AValue: TIdIPVersion); + procedure SetTTL(const Value: Integer); + procedure SetHost(const AValue : String); virtual; + // + // TODO: figure out which ReceiveXXX functions we want + // + property IPVersion : TIdIPVersion read GetIPVersion write SetIPVersion; + // + property Port: TIdPort read FPort write FPort default Id_TIdRawBase_Port; + property Protocol: TIdSocketProtocol read FProtocol write FProtocol default Id_IPPROTO_RAW; + property ProtocolIPv6 : TIdSocketProtocol read FProtocolIPv6 write FProtocolIPv6; + property TTL: Integer read FTTL write SetTTL default GFTTL; + + public + destructor Destroy; override; + + function ReceiveBuffer(var VBuffer : TIdBytes; ATimeOut: Integer = -1): Integer; + procedure Send(const AData: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; virtual; + procedure Send(const AData: TIdBytes); overload; virtual; + procedure Send(const AHost: string; const APort: TIdPort; const AData: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; virtual; + procedure Send(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); overload; virtual; + // + property Binding: TIdSocketHandle read GetBinding; + property ReceiveTimeout: integer read FReceiveTimeout write FReceiveTimeout Default GReceiveTimeout; + published + property Host: string read FHost write SetHost; + end; + + +implementation + +uses + SysUtils; + +{ TIdRawBase } + +destructor TIdRawBase.Destroy; +begin + FreeAndNil(FBinding); + FreeAndNil(FPkt); + inherited Destroy; +end; + +function TIdRawBase.GetBinding: TIdSocketHandle; +begin + if not FBinding.HandleAllocated then begin + if FBinding.IPVersion = Id_IPv4 then + begin + FBinding.AllocateSocket(Id_SOCK_RAW, FProtocol); + end else + begin + FBinding.AllocateSocket(Id_SOCK_RAW, FProtocolIPv6); + {$IFDEF DOTNET} + {$IFDEF DOTNET_2_OR_ABOVE} + { + Microsoft NET Framework 1.1 may actually have the packetinfo option but that + will not do you any good because you need a RecvMsg function which is not + in NET 1.1. NET 2.0 does have a RecvMsg function, BTW. + } + //indicate we want packet information with RecvMsg calls + FBinding.SetSockOpt(Id_SOL_IPv6, Id_IPV6_PKTINFO, 1); + {$ENDIF} + {$ELSE} + //indicate we want packet information with RecvMsg WSARecvMsg calls + FBinding.SetSockOpt(Id_SOL_IPv6, Id_IPV6_PKTINFO, 1); + FBinding.SetSockOpt(Id_SOL_IPv6, Id_IPV6_HOPLIMIT, 1); + {$ENDIF} + end; + //set hop limit (or TTL as it was called in IPv4 + FBinding.SetTTL(FTTL); + end; + Result := FBinding; +end; + +function TIdRawBase.ReceiveBuffer(var VBuffer : TIdBytes; ATimeOut: Integer = -1): Integer; +var + LIP : String; + LPort : TIdPort; + LIPVersion: TIdIPVersion; +begin + Result := 0; + // TODO: pass flags to recv() + if ATimeOut < 0 then + begin + ATimeOut := FReceiveTimeout; + end; + if Length(VBuffer) > 0 then + begin + if Binding.Readable(ATimeOut) then begin + if FBinding.IPVersion = Id_IPv4 then + begin + Result := Binding.RecvFrom(VBuffer, LIP, LPort, LIPVersion); + FPkt.Reset; + FPkt.SourceIP := LIP; + FPkt.SourcePort := LPort; + FPkt.SourceIPVersion := LIPVersion; + FPkt.DestIPVersion := LIPVersion; + end else + begin + { + IMPORTANT!!!! + + Do NOT call GStack.ReceiveMsg unless it is absolutely necessary. + The reasons are: + + 1) WSARecvMsg is only supported on WindowsXP or later. I think Linux + might have a RecvMsg function as well but I'm not sure. + 2) GStack.ReceiveMsg is not supported in the Microsoft NET framework 1.1. + It may be supported in later versions. + + For IPv4 and raw sockets, it usually isn't because we get the raw header itself. + + For IPv6 and raw sockets, we call this to get information about the destination + IP address and hopefully, the TTL (hop count). + } + + Result := GStack.ReceiveMsg(Binding.Handle, VBuffer, FPkt); + end; + end; + end; +end; + +procedure TIdRawBase.Send(const AHost: string; const APort: TIdPort; const AData: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + Send(AHost, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +procedure TIdRawBase.Send(const AData: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + Send(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +procedure TIdRawBase.Send(const AData: TIdBytes); +begin + Send(Host, Port, AData); +end; + +procedure TIdRawBase.Send(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); +var + LIP : String; +begin + LIP := GStack.ResolveHost(AHost, FBinding.IPVersion); + Binding.SendTo(LIP, APort, ABuffer, FBinding.IPVersion); +end; + +procedure TIdRawBase.SetTTL(const Value: Integer); +begin + if FTTL <> Value then + begin + FTTL := Value; + if FBinding.HandleAllocated then + begin + FBinding.SetTTL(FTTL); + end; + end; +end; + +procedure TIdRawBase.InitComponent; +begin + inherited InitComponent; + FBinding := TIdSocketHandle.Create(nil); + FBinding.IPVersion := Id_IPv4; + FPkt := TIdPacketInfo.Create; + ReceiveTimeout := GReceiveTimeout; + FPort := Id_TIdRawBase_Port; + FProtocol := Id_IPPROTO_RAW; + FTTL := GFTTL; +end; + +function TIdRawBase.GetIPVersion; +begin + Result := FBinding.IPVersion; +end; + +procedure TIdRawBase.SetIPVersion(const AValue: TIdIPVersion); +begin + FBinding.IPVersion := AValue; +end; + +procedure TIdRawBase.SetHost(const AValue: String); +begin + FHost := AValue; +end; + +end. diff --git a/indy/Core/IdRawClient.pas b/indy/Core/IdRawClient.pas new file mode 100644 index 0000000..58764d9 --- /dev/null +++ b/indy/Core/IdRawClient.pas @@ -0,0 +1,47 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/13/2002 08:45:32 AM JPMugaas +} + +unit IdRawClient; + +interface +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdRawBase; + +type + TIdRawClient = class(TIdRawBase) + + published + property ReceiveTimeout; + property Host; + property Port; + property Protocol; + property ProtocolIPv6; + property IPVersion; + end; + +implementation + +{ TIdRawClient } + +end. diff --git a/indy/Core/IdRawFunctions.pas b/indy/Core/IdRawFunctions.pas new file mode 100644 index 0000000..bd58ff8 --- /dev/null +++ b/indy/Core/IdRawFunctions.pas @@ -0,0 +1,710 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 2004.02.03 4:16:50 PM czhower + For unit name changes. + + Rev 1.4 2/1/2004 4:52:30 PM JPMugaas + Removed the rest of the Todo; items. + + Rev 1.3 2/1/2004 4:20:30 PM JPMugaas + Should work in Win32. TODO: See about DotNET. + + Rev 1.2 2003.10.11 5:49:06 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.1 2003.09.30 1:23:00 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 08:45:36 AM JPMugaas +} + +unit IdRawFunctions; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, IdRawHeaders, IdStack; + +// ARP +procedure IdRawBuildArp(const AHwAddressFormat, AProtocolFormat: UInt16; + const AHwAddressLen, AProtocolLen: UInt8; const AnOpType: UInt16; + ASenderHw: TIdEtherAddr; ASenderPr: TIdInAddr; ATargetHw: TIdEtherAddr; + ATargetPr: TIdInAddr; const APayload: TIdBytes; var VBuffer: TIdBytes); + +// DNS +procedure IdRawBuildDns(const AnId, AFlags, ANumQuestions, ANumAnswerRecs, ANumAuthRecs, ANumAddRecs: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); + +// Ethernet +procedure IdRawBuildEthernet(ADest, ASource: TIdEtherAddr; AType: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); + +// ICMP +procedure IdRawBuildIcmpEcho(AType, ACode: UInt8; AnId, ASeq: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); +procedure IdRawBuildIcmpMask(AType, ACode: UInt8; AnId, ASeq: UInt16; AMask: UInt32; + const APayload: TIdBytes; var VBuffer: TIdBytes); +procedure IdRawBuildIcmpRedirect(const AType, ACode: UInt8; AGateway: TIdInAddr; + const AnOrigLen: UInt16; const AnOrigTos: UInt8; const AnOrigId, AnOrigFrag: UInt16; + const AnOrigTtl, AnOrigProtocol: UInt8; AnOrigSource, AnOrigDest: TIdInAddr; + const AnOrigPayload: TIdBytes; var VBuffer: TIdBytes); +procedure IdRawBuildIcmpTimeExceed(const AType, ACode: UInt8; const AnOrigLen: UInt16; + const AnOrigTos: UInt8; const AnOrigId, AnOrigFrag: UInt16; + const AnOrigTtl, AnOrigProtocol: UInt8; const AnOrigSource, AnOrigDest: TIdInAddr; + const AnOrigPayload: TIdBytes; var VBuffer: TIdBytes); +procedure IdRawBuildIcmpTimestamp(const AType, ACode: UInt8; const AnId, ASeq: UInt16; + const AnOtime, AnRtime, ATtime: TIdNetTime; const APayload: TIdBytes; + var VBuffer: TIdBytes); +procedure IdRawBuildIcmpUnreach(AType, ACode: UInt8; AnOrigLen: UInt16; + AnOrigTos: UInt8; AnOrigId, AnOrigFrag: UInt16; AnOrigTtl, AnOrigProtocol: UInt8; + AnOrigSource, AnOrigDest: TIdInAddr; const AnOrigPayload, APayloadSize: Integer; + var VBuffer: TIdBytes); + +// IGMP +procedure IdRawBuildIgmp(AType, ACode: UInt8; AnIp: TIdInAddr; + const APayload: UInt16; var VBuffer: TIdBytes); + +// IP +procedure IdRawBuildIp(ALen: UInt16; ATos: UInt8; AnId, AFrag: UInt16; + ATtl, AProtocol: UInt8; ASource, ADest: TIdInAddr; const APayload: TIdBytes; + var VBuffer: TIdBytes; const AIdx: Integer = 0); + +// RIP +procedure IdRawBuildRip(const ACommand, AVersion: UInt8; + const ARoutingDomain, AnAddressFamily, ARoutingTag: UInt16; + const AnAddr, AMask, ANextHop, AMetric: UInt32; + const APayload: TIdBytes; var VBuffer: TIdBytes); + +// TCP +procedure IdRawBuildTcp(const ASourcePort, ADestPort: UInt16; + const ASeq, AnAck: UInt32; const AControl: UInt8; + const AWindowSize, AnUrgent: UInt16; const APayload: TIdBytes; + var VBuffer: TIdBytes); + +// UDP +procedure IdRawBuildUdp(const ASourcePort, ADestPort: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); + +implementation + +uses + SysUtils; + +procedure IdRawBuildArp(const AHwAddressFormat, AProtocolFormat: UInt16; + const AHwAddressLen, AProtocolLen: UInt8; const AnOpType: UInt16; + ASenderHw: TIdEtherAddr; ASenderPr: TIdInAddr; ATargetHw: TIdEtherAddr; + ATargetPr: TIdInAddr; const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrArp: TIdArpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_ARP_HSIZE + Length(VBuffer); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrArp := TIdArpHdr.Create; + try + HdrArp.arp_hrd := GStack.HostToNetwork(AHwAddressFormat); + HdrArp.arp_pro := GStack.HostToNetwork(AProtocolFormat); + HdrArp.arp_hln := AHwAddressLen; + HdrArp.arp_pln := AProtocolLen; + HdrArp.arp_op := GStack.HostToNetwork(AnOpType); + HdrArp.arp_sha.CopyFrom(ASenderHw); + HdrArp.arp_spa.s_l := ASenderPr.s_l; + HdrArp.arp_tha.CopyFrom(ATargetHw); + HdrArp.arp_tpa.CopyFrom(ATargetPr); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_ICMP_ECHO_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrArp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrArp); + end; +end; + +procedure IdRawBuildDns(const AnId, AFlags, ANumQuestions, ANumAnswerRecs, + ANumAuthRecs, ANumAddRecs: UInt16; const APayload: TIdBytes; + var VBuffer: TIdBytes); +var + HdrDns: TIdDnsHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Length(APayload) + Id_DNS_HSIZE; + LLen := UInt32(Length(VBuffer)); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrDns := TIdDnsHdr.Create; + try + HdrDns.dns_id := GStack.HostToNetwork(AnId); + HdrDns.dns_flags := GStack.HostToNetwork(AFlags); + HdrDns.dns_num_q := GStack.HostToNetwork(ANumQuestions); + HdrDns.dns_num_answ_rr := GStack.HostToNetwork(ANumAnswerRecs); + HdrDns.dns_num_auth_rr := GStack.HostToNetwork(ANumAuthRecs); + HdrDns.dns_num_addi_rr := GStack.HostToNetwork(ANumAddRecs); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_DNS_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrDns.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrDns); + end; +end; + +procedure IdRawBuildEthernet(ADest, ASource: TIdEtherAddr; AType: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrEth: TIdEthernetHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // make sure VBuffer will be long enough + LIdx := Length(ASource.Data) + Length(ADest.Data) + 2 + Length(APayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrEth := TIdEthernetHdr.Create; + try + HdrEth.ether_dhost.CopyFrom(ADest); + HdrEth.ether_shost.CopyFrom(ASource); + HdrEth.ether_type := GStack.HostToNetwork(AType); + + // copy header + LIdx := 0; + HdrEth.WriteStruct(VBuffer, LIdx); + + // copy payload if present + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, LIdx, Length(APayload)); + end; + finally + FreeAndNil(HdrEth); + end; +end; + +// TODO: check nibbles in IP header +procedure IdRawBuildIp(ALen: UInt16; ATos: UInt8; AnId, AFrag: UInt16; ATtl, AProtocol: UInt8; + ASource, ADest: TIdInAddr; const APayload: TIdBytes; var VBuffer: TIdBytes; + const AIdx: Integer = 0); +var + HdrIp: TIdIpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_IP_HSIZE + Length(APayload) + AIdx; + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIp := TIdIpHdr.Create; + try + HdrIp.ip_verlen := (4 shl 4) + (Id_IP_HSIZE div 4); // IPv4 shl 4, 20 bytes div 4 + HdrIp.ip_tos := ATos; + HdrIp.ip_len := GStack.HostToNetwork(UInt16(ALen + Id_IP_HSIZE)); + HdrIp.ip_id := GStack.HostToNetwork(AnId); + HdrIp.ip_off := GStack.HostToNetwork(AFrag); + HdrIp.ip_ttl := ATtl; + HdrIp.ip_p := AProtocol; + HdrIp.ip_sum := 0; // do checksum later + HdrIp.ip_src.CopyFrom(ASource); + HdrIp.ip_dst.CopyFrom(ADest); + + // copy header + LIdx := AIdx; + HdrIp.WriteStruct(VBuffer, LIdx); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, LIdx, Length(APayload)); + end; + finally + FreeANdNil(HdrIp); + end; +end; + +procedure IdRawBuildIcmpEcho(AType, ACode: UInt8; AnId, ASeq: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrIcmp: TIdIcmpHdr; + LIdx, LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_ECHO_HSIZE + Length(APayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdIcmpHdr.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.echo_id := GStack.HostToNetwork(AnId); + HdrIcmp.icmp_hun.echo_seq := GStack.HostToNetwork(ASeq); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_ICMP_ECHO_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrIcmp); + end; +end; + +type + TIdICMPMask = class(TIdICMPHdr) + protected + Ficmp_mask: UInt32; + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property icmp_mask: UInt32 read Ficmp_mask write Ficmp_mask; + end; + +function TIdICMPMask.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4; +end; + +procedure TIdICMPMask.ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Ficmp_mask := BytesToUInt32(ABytes, VIndex); + Inc(VIndex, 4); +end; + +procedure TIdICMPMask.WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt32(Ficmp_mask, VBytes, VIndex); + Inc(VIndex, 4); +end; + +procedure IdRawBuildIcmpMask(AType, ACode: UInt8; AnId, ASeq: UInt16; AMask: UInt32; + const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrIcmp: TIdICMPMask; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_MASK_HSIZE + Length(APayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdICMPMask.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.echo_id := GStack.HostToNetwork(AnId); + HdrIcmp.icmp_hun.echo_seq := GStack.HostToNetwork(ASeq); + HdrIcmp.icmp_mask := GStack.HostToNetwork(AMask); + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, LIdx, Length(APayload)); + end; + finally + FreeAndNil(HdrIcmp); + end; +end; + +procedure IdRawBuildIcmpUnreach(AType, ACode: UInt8; AnOrigLen: UInt16; + AnOrigTos: UInt8; AnOrigId, AnOrigFrag: UInt16; AnOrigTtl, AnOrigProtocol: UInt8; + AnOrigSource, AnOrigDest: TIdInAddr; const AnOrigPayload, APayloadSize: Integer; + var VBuffer: TIdBytes); +var + HdrIcmp: TIdIcmpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_UNREACH_HSIZE + Id_IP_HSIZE + 2; + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdIcmpHdr.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.echo_id := 0; + HdrIcmp.icmp_hun.echo_seq := 0; + + // attach original header + IdRawBuildIp(0, AnOrigTos, AnOrigId, AnOrigFrag, AnOrigTtl, AnOrigProtocol, + AnOrigSource, AnOrigDest, ToBytes(AnOrigPayload), VBuffer, Id_ICMP_UNREACH_HSIZE); + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrIcmp); + end; +end; + +procedure IdRawBuildIcmpTimeExceed(const AType, ACode: UInt8; const AnOrigLen: UInt16; + const AnOrigTos: UInt8; const AnOrigId, AnOrigFrag: UInt16; + const AnOrigTtl, AnOrigProtocol: UInt8; const AnOrigSource, AnOrigDest: TIdInAddr; + const AnOrigPayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrIcmp: TIdIcmpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_TIMEXCEED_HSIZE + Id_IP_HSIZE + Length(AnOrigPayload); + Llen := Length(VBuffer); + if Llen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdIcmpHdr.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.echo_id := 0; + HdrIcmp.icmp_hun.echo_seq := 0; + + // attach original header + IdRawBuildIp(0, AnOrigTos, AnOrigId, AnOrigFrag, AnOrigTtl, AnOrigProtocol, + AnOrigSource, AnOrigDest, AnOrigPayload, VBuffer, Id_ICMP_TIMEXCEED_HSIZE); + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrIcmp); + end; +end; + +type + TIdIcmpTS = class(TIdIcmpHdr) + protected + Ficmp_dun: TIdicmp_dun; + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property icmp_dun: TIdicmp_dun read Ficmp_dun; + end; + +constructor TIdIcmpTS.Create; +begin + inherited Create; + Ficmp_dun := TIdicmp_dun.Create; +end; + +destructor TIdIcmpTS.Destroy; +begin + Ficmp_dun.Free; + inherited Destroy; +end; + +function TIdIcmpTS.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + Ficmp_dun.BytesLen; +end; + +procedure TIdIcmpTS.ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Ficmp_dun.ReadStruct(ABytes, VIndex); +end; + +procedure TIdIcmpTS.WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + Ficmp_dun.WriteStruct(VBytes, VIndex); +end; + +procedure IdRawBuildIcmpTimestamp(const AType, ACode: UInt8; const AnId, ASeq: UInt16; + const AnOtime, AnRtime, ATtime: TIdNetTime; const APayload: TIdBytes; + var VBuffer: TIdBytes); +var + HdrIcmp: TIdIcmpTS; + LIdx, LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_TS_HSIZE + Length(APayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdIcmpTS.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.echo_id := GStack.HostToNetwork(AnId); + HdrIcmp.icmp_hun.echo_seq := GStack.HostToNetwork(ASeq); + HdrIcmp.icmp_dun.ts_otime := GStack.HostToNetwork(AnOtime); // original timestamp + HdrIcmp.icmp_dun.ts_rtime := GStack.HostToNetwork(AnRtime); // receive timestamp + HdrIcmp.icmp_dun.ts_ttime := GStack.HostToNetwork(ATtime); // transmit timestamp + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, LIdx, Length(APayload)); + end; + finally + FreeAndNil(HdrIcmp); + end; +end; + +procedure IdRawBuildIcmpRedirect(const AType, ACode: UInt8; AGateway: TIdInAddr; + const AnOrigLen: UInt16; const AnOrigTos: UInt8; const AnOrigId, AnOrigFrag: UInt16; + const AnOrigTtl, AnOrigProtocol: UInt8; AnOrigSource, AnOrigDest: TIdInAddr; + const AnOrigPayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrIcmp: TIdIcmpHdr; + LIdx, LLen : UInt32; +begin + // check input + LIdx := Id_ICMP_REDIRECT_HSIZE + Id_IP_HSIZE + Length(AnOrigPayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIcmp := TIdIcmpHdr.Create; + try + HdrIcmp.icmp_type := AType; + HdrIcmp.icmp_code := ACode; + HdrIcmp.icmp_hun.gateway_s_b1 := AGateway.s_l; // gateway address + + // attach original header + IdRawBuildIp(0, AnOrigTos, AnOrigId, AnOrigFrag, AnOrigTtl, AnOrigProtocol, + AnOrigSource, AnOrigDest, AnOrigPayload, VBuffer, Id_ICMP_REDIRECT_HSIZE); + + // copy header + LIdx := 0; + HdrIcmp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrIcmp); + end; +end; + +procedure IdRawBuildIgmp(AType, ACode: UInt8; AnIp: TIdInAddr; + const APayload: UInt16; var VBuffer: TIdBytes); +var + HdrIgmp: TIdIgmpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := 2 + Id_IGMP_HSIZE; + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrIgmp := TIdIgmpHdr.Create; + try + HdrIgmp.igmp_type := AType; + HdrIgmp.igmp_code := ACode; + HdrIgmp.igmp_sum := 0; + HdrIgmp.igmp_group.s_l := AnIp.s_l; // group address or 0 + + // copy payload + CopyTIdUInt16(APayload, VBuffer, Id_IGMP_HSIZE); + + // copy header + LIdx := 0; + HdrIgmp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrIgmp); + end; +end; + +procedure IdRawBuildRip(const ACommand, AVersion: UInt8; + const ARoutingDomain, AnAddressFamily, ARoutingTag: UInt16; + const AnAddr, AMask, ANextHop, AMetric: UInt32; + const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrRip: TIdRipHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_RIP_HSIZE + Length(APayload); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrRip := TIdRipHdr.Create; + try + HdrRip.rip_cmd := ACommand; + HdrRip.rip_ver := AVersion; + HdrRip.rip_rd := GStack.HostToNetwork(ARoutingDomain); + HdrRip.rip_af := GStack.HostToNetwork(AnAddressFamily); + HdrRip.rip_rt := GStack.HostToNetwork(ARoutingTag); + HdrRip.rip_addr := GStack.HostToNetwork(AnAddr); + HdrRip.rip_mask := GStack.HostToNetwork(AMask); + HdrRip.rip_next_hop := GStack.HostToNetwork(ANextHop); + HdrRip.rip_metric := GStack.HostToNetwork(AMetric); + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_RIP_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrRip.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrRip); + end; +end; + +// TODO: check nibbles in TCP header +procedure IdRawBuildTcp(const ASourcePort, ADestPort: UInt16; + const ASeq, AnAck: UInt32; const AControl: UInt8; + const AWindowSize, AnUrgent: UInt16; const APayload: TIdBytes; + var VBuffer: TIdBytes); +var + HdrTcp: TIdTcpHdr; + LIdx, LLen: UInt32; +begin + // check input + LIdx := Id_TCP_HSIZE + Length(VBuffer); + LLen := Length(VBuffer); + if LLen < LIdx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrTcp := TIdTcpHdr.Create; + try + HdrTcp.tcp_sport := GStack.HostToNetwork(ASourcePort); + HdrTcp.tcp_dport := GStack.HostToNetwork(ADestPort); + HdrTcp.tcp_seq := GStack.HostToNetwork(ASeq); + HdrTcp.tcp_ack := GStack.HostToNetwork(AnAck); // acknowledgement number + HdrTcp.tcp_flags := AControl; // control flags + HdrTcp.tcp_x2off := ((Id_TCP_HSIZE div 4) shl 4) + 0; // 20 bytes div 4, x2 unused + HdrTcp.tcp_win := GStack.HostToNetwork(AWindowSize); // window size + HdrTcp.tcp_sum := 0; + HdrTcp.tcp_urp := AnUrgent; // urgent pointer + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_TCP_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrTcp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrTcp); + end; +end; + +procedure IdRawBuildUdp(const ASourcePort, ADestPort: UInt16; + const APayload: TIdBytes; var VBuffer: TIdBytes); +var + HdrUdp: TIdUdpHdr; + LIdx: UInt32; + LLen : UInt32; +begin + // check input + LIdx := Id_UDP_HSIZE + Length(APayload); + LLen := Length(VBuffer); + if LLen < Lidx then begin + SetLength(VBuffer, LIdx); + end; + + // construct header + HdrUdp := TIdUdpHdr.Create; + try + HdrUdp.udp_dport := GStack.HostToNetwork(ASourcePort); + HdrUdp.udp_dport := GStack.HostToNetwork(ADestPort); + //LIdx should be okay here since we set that to the packet length earlier + HdrUdp.udp_ulen := GStack.HostToNetwork(LIdx); + HdrUdp.udp_sum := 0; + + // copy payload + if Length(APayload) > 0 then begin + CopyTIdBytes(APayload, 0, VBuffer, Id_UDP_HSIZE, Length(APayload)); + end; + + // copy header + LIdx := 0; + HdrUdp.WriteStruct(VBuffer, LIdx); + finally + FreeAndNil(HdrUdp); + end; +end; + +end. diff --git a/indy/Core/IdRawHeaders.pas b/indy/Core/IdRawHeaders.pas new file mode 100644 index 0000000..2c6c32d --- /dev/null +++ b/indy/Core/IdRawHeaders.pas @@ -0,0 +1,1746 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 2/8/2004 12:59:40 PM JPMugaas + Start on DotNET port. + + Rev 1.2 10/16/2003 11:05:38 PM SPerry + Reorganization + + Rev 1.1 2003.09.30 1:23:02 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 08:45:44 AM JPMugaas +} + +unit IdRawHeaders; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF DOTNET} + System.Net, + {$ENDIF} + IdGlobal, + IdStruct; +// TODO: research subtypes of ICMP header + +type + //RFC 3542 definitions + //IPv6 Extension Headers + // types redeclared to avoid dependencies on stack declarations + + TIdSunB = class(TIdStruct) + protected + Fs_b1, + Fs_b2, + Fs_b3, + Fs_b4: UInt8; + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property s_b1 : UInt8 read Fs_b1 write Fs_b1; + property s_b2 : UInt8 read Fs_b2 write Fs_b2; + property s_b3 : UInt8 read Fs_b3 write Fs_b3; + property s_b4 : UInt8 read Fs_b4 write Fs_b4; + end; + + TIdSunW = class(TIdStruct) + protected + Fs_w1, Fs_w2: UInt16; + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property s_w1 : UInt16 read Fs_w1 write Fs_w1; + property s_w2 : UInt16 read Fs_w2 write Fs_w2; + end; + + TIdInAddr = class(TIdLongWord) + public + procedure CopyFrom(const ASource : TIdInAddr); + end; +{ PIdInAddr = ^TIdInAddr; + TIdInAddr = record + case integer of + 0: (S_un_b: TIdSunB); + 1: (S_un_w: TIdSunW); + 2: (S_addr: UInt32); + end; } + + TIdNetTime = UInt32; // network byte order + +const +//header sizes----------------------------------------------------------------// + Id_ARP_HSIZE = $1C; // ARP header: 28 bytes + Id_DNS_HSIZE = $0C; // DNS header base: 12 bytes + Id_ETH_HSIZE = $0E; // Etherner header: 14 bytes + Id_ICMP_HSIZE = $04; // ICMP header base: 4 bytes + Id_ICMP_ECHO_HSIZE = $08; // ICMP_ECHO header: 8 bytes + Id_ICMP6_ECHO_HSIZE = $08; // ICMPv6_ECHO header: 8 bytes icmp echo header len excluding time */ + Id_ICMP_MASK_HSIZE = $0C; // ICMP_MASK header: 12 bytes + Id_ICMP_UNREACH_HSIZE = $08; // ICMP_UNREACH header: 8 bytes + Id_ICMP_TIMEXCEED_HSIZE = $08; // ICMP_TIMXCEED header: 8 bytes + Id_ICMP_REDIRECT_HSIZE = $08; // ICMP_REDIRECT header: 8 bytes + Id_ICMP_TS_HSIZE = $14; // ICMP_TIMESTAMP header: 20 bytes + Id_IGMP_HSIZE = $08; // IGMP header: 8 bytes + Id_IP_HSIZE = $14; // IP header: 20 bytes + Id_IPv6_HSIZE = $28; // IPv6 header + Id_RIP_HSIZE = $18; // RIP header base: 24 bytes + Id_TCP_HSIZE = $14; // TCP header: 20 bytes + Id_UDP_HSIZE = $08; // UDP header: 8 bytes + +//fragmentation flags---------------------------------------------------------// + Id_IP_RF = $8000; // reserved fragment flag + Id_IP_DF = $4000; // dont fragment flag + Id_IP_MF = $2000; // more fragments flag + Id_IP_OFFMASK = $1FFF; // mask for fragmenting bits + +//TCP control flags-----------------------------------------------------------// + Id_TCP_FIN = $01; + Id_TCP_SYN = $02; + Id_TCP_RST = $04; + Id_TCP_PUSH = $08; + Id_TCP_ACK = $10; + Id_TCP_URG = $20; + +//ICMP types------------------------------------------------------------------// + Id_ICMP_ECHOREPLY = 0; + Id_ICMP_UNREACH = 3; + Id_ICMP_SOURCEQUENCH = 4; + Id_ICMP_REDIRECT = 5; + Id_ICMP_ECHO = 8; + Id_ICMP_ROUTERADVERT = 9; + Id_ICMP_ROUTERSOLICIT = 10; + Id_ICMP_TIMXCEED = 11; + Id_ICMP_PARAMPROB = 12; + Id_ICMP_TSTAMP = 13; + Id_ICMP_TSTAMPREPLY = 14; + Id_ICMP_IREQ = 15; + Id_ICMP_IREQREPLY = 16; + Id_ICMP_MASKREQ = 17; + Id_ICMP_MASKREPLY = 18; + Id_ICMP_TRACEROUTE = 30; // RFC1393 Traceroute + Id_ICMP_DATAGRAM_CONV = 31; // RFC1475 + Id_ICMP_MOB_HOST_REDIR = 32; // Mobile Host Redirect + Id_ICMP_IPv6_WHERE_ARE_YOU = 33; + Id_ICMP_IPv6_I_AM_HERE = 34; + Id_ICMP_MOB_REG_REQ = 35; + Id_ICMP_MOB_REG_REPLY = 36; + Id_ICMP_SKIP = 39; + Id_ICMP_PHOTURIS = 40; // Photuris [RFC2521] + +//ICMP codes------------------------------------------------------------------// + Id_ICMP_UNREACH_NET = 0; + Id_ICMP_UNREACH_HOST = 1; + Id_ICMP_UNREACH_PROTOCOL = 2; + Id_ICMP_UNREACH_PORT = 3; + Id_ICMP_UNREACH_NEEDFRAG = 4; + Id_ICMP_UNREACH_SRCFAIL = 5; + Id_ICMP_UNREACH_NET_UNKNOWN = 6; + Id_ICMP_UNREACH_HOST_UNKNOWN = 7; + Id_ICMP_UNREACH_ISOLATED = 8; + Id_ICMP_UNREACH_NET_PROHIB = 9; + Id_ICMP_UNREACH_HOST_PROHIB = 10; + Id_ICMP_UNREACH_TOSNET = 11; + Id_ICMP_UNREACH_TOSHOST = 12; + Id_ICMP_UNREACH_FILTER_PROHIB = 13; + Id_ICMP_UNREACH_HOST_PRECEDENCE = 14; + Id_ICMP_UNREACH_PRECEDENCE_CUTOFF = 15; + Id_ICMP_REDIRECT_NET = 0; + Id_ICMP_REDIRECT_HOST = 1; + Id_ICMP_REDIRECT_TOSNET = 2; + Id_ICMP_REDIRECT_TOSHOST = 3; + Id_ICMP_TIMXCEED_INTRANS = 0; + Id_ICMP_TIMXCEED_REASS = 1; + Id_ICMP_PARAMPROB_OPTABSENT = 1; + + // RFC 1393 + Id_ICMP_TRACEROUTE_PACKET_FORWARDED = 0; + Id_ICMP_TRACEROUTE_NO_ROUTE = 1; + + Id_ICMP_BAD_SPI = 0; //security parameter error 40 + Id_ICMP_AUTH_FAILED = 1; + Id_ICMP_DECOMPRESS_FAILED = 2; + Id_ICMP_DECRYPTION_FAILED = 3; + Id_ICMP_NEED_AUTHENTICATION = 4; + Id_ICMP_NEED_AUTHORIZATION = 5; + + // RFC 1475 error codes + // The type for Conversion Failed is 31 + Id_ICMP_CONV_UNSPEC = 0; + Id_ICMP_CONV_DONTCONV_OPTION = 1; + Id_ICMP_CONV_UNKNOWN_MAN_OPTION = 2; + Id_ICMP_CONV_UNKNWON_UNSEP_OPTION = 3; + Id_ICMP_CONV_UNSEP_TRANSPORT = 4; + Id_ICMP_CONV_OVERALL_LENGTH_EXCEEDED = 5; + Id_ICMP_CONV_IP_HEADER_LEN_EXCEEDED = 6; + Id_ICMP_CONV_TRANS_PROT_255 = 7; // transport protocol > 255 + Id_ICMP_CONV_PORT_OUT_OF_RANGE = 8; + Id_ICMP_CONV_TRANS_HEADER_LEN_EXCEEDED = 9; + Id_ICMP_CONV_32BIT_ROLLOVER_AND_ACK = 10; // 32 Bit Rollover missing and ACK set + Id_ICMP_CONV_UNKNOWN_MAN_TRANS_OPTION = 11; + + ICMP_MIN = 8; + +//ICMPv6 types----------------------------------------------------------------// + ICMP6_DST_UNREACH = 1; + ICMP6_PACKET_TOO_BIG = 2; + ICMP6_TIME_EXCEEDED = 3; + ICMP6_PARAM_PROB = 4; + + // Informational Messages + ICMP6_INFOMSG_MASK = $80; //* all informational messages */ + ICMP6_ECHO_REQUEST = 128; + ICMP6_ECHO_REPLY = 129; + + ICMP6_MEMBERSHIP_QUERY = 130; + ICMP6_MEMBERSHIP_REPORT = 131; + ICMP6_MEMBERSHIP_REDUCTION = 132; + +//ICMPv6 codes----------------------------------------------------------------// + ICMP6_DST_UNREACH_NOROUTE = 0; //* no route to destination */ + ICMP6_DST_UNREACH_ADMIN = 1; //* communication with */ + //* destination */ + //* administratively */ + //* prohibited */ + ICMP6_DST_UNREACH_NOTNEIGHBOR = 2; //* not a neighbor */ + ICMP6_DST_UNREACH_ADDR = 3; //* address unreachable */ + ICMP6_DST_UNREACH_NOPORT = 4; //* bad port */ + ICMP6_DST_UNREACH_SOURCE_FILTERING = 5; //source address failed ingress/egress policy + ICMP6_DST_UNREACH_REJCT_DST = 6; //reject route to destination + + ICMP6_TIME_EXCEED_TRANSIT = 0; //* Hop Limit == 0 in transit */ + ICMP6_TIME_EXCEED_REASSEMBLY = 1; //* Reassembly time out */ + + ICMP6_PARAMPROB_HEADER = 0; //* erroneous header field */ + ICMP6_PARAMPROB_NEXTHEADER = 1; //* unrecognized Next Header */ + ICMP6_PARAMPROB_OPTION = 2; //* unrecognized IPv6 option */ + + // ICMPv6 Neighbor Discovery Definitions + ND_ROUTER_SOLICIT = 133; + ND_ROUTER_ADVERT = 134; + ND_NEIGHBOR_SOLICIT = 135; + ND_NEIGHBOR_ADVERT = 136; + ND_REDIRECT = 137; + +//IGMP types------------------------------------------------------------------// + Id_IGMP_MEMBERSHIP_QUERY = $11; // membership query + Id_IGMP_V1_MEMBERSHIP_REPORT = $12; // v1 membership report + Id_IGMP_V2_MEMBERSHIP_REPORT = $16; // v2 membership report + Id_IGMP_LEAVE_GROUP = $17; // leave-group message + +//ethernet packet types-------------------------------------------------------// + Id_ETHERTYPE_PUP = $0200; // PUP protocol + Id_ETHERTYPE_IP = $0800; // IP protocol + Id_ETHERTYPE_ARP = $0806; // ARP protocol + Id_ETHERTYPE_REVARP = $8035; // reverse ARP protocol + Id_ETHERTYPE_VLAN = $8100; // IEEE 802.1Q VLAN tagging + Id_ETHERTYPE_LOOPBACK = $9000; // used to test interfaces + +//hardware address formats----------------------------------------------------// + Id_ARPHRD_ETHER = 1; // ethernet hardware format + +//ARP operation types---------------------------------------------------------// + Id_ARPOP_REQUEST = 1; // req to resolve address + Id_ARPOP_REPLY = 2; // resp to previous request + Id_ARPOP_REVREQUEST = 3; // req protocol address given hardware + Id_ARPOP_REVREPLY = 4; // resp giving protocol address + Id_ARPOP_INVREQUEST = 8; // req to identify peer + Id_ARPOP_INVREPLY = 9; // resp identifying peer + +//RIP commands----------------------------------------------------------------// + Id_RIPCMD_REQUEST = 1; // want info + Id_RIPCMD_RESPONSE = 2; // responding to request + Id_RIPCMD_TRACEON = 3; // turn tracing on + Id_RIPCMD_TRACEOFF = 4; // turn it off + Id_RIPCMD_POLL = 5; // like request, but anyone answers + Id_RIPCMD_POLLENTRY = 6; // like poll, but for entire entry + Id_RIPCMD_MAX = 7; + +//RIP versions----------------------------------------------------------------// + Id_RIPVER_0 = 0; + Id_RIPVER_1 = 1; + Id_RIPVER_2 = 2; + +//----------------------------------------------------------------------------// + Id_MAX_IPOPTLEN = 40; + Id_IP_MAXPACKET = 65535; + Id_ETHER_ADDR_LEN = 6; + +type + +//////////////////////////////////////////////////////////////////////////////// +//ICMP////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + TIdICMPEcho = class(TIdStruct) + protected + Fid: UInt16; // identifier to match requests with replies + Fseq: UInt16; // sequence number to match requests with replies + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property id: UInt16 read Fid write FId; // identifier to match requests with replies + property seq: UInt16 read Fseq write FSeq; // sequence number to match requests with replies + end; + + TIdICMPFrag = class(TIdStruct) + protected + Fpad: UInt16; + Fmtu: UInt16; + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property pad: UInt16 read Fpad write Fpad; + property mtu: UInt16 read Fmtu write Fmtu; + end; + + TIdICMPTs = class(TIdStruct) + protected + Fotime: TIdNetTime; // time message was sent, to calc roundtrip time + Frtime: TIdNetTime; + Fttime: TIdNetTime; + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property otime: TIdNetTime read Fotime write Fotime; // time message was sent, to calc roundtrip time + property rtime: TIdNetTime read Frtime write Frtime; + property ttime: TIdNetTime read Fttime write Fttime; + end; + + { packet header } + TIdicmp_hun = class(TIdUnion) + protected + function Getecho_id: UInt16; + function Getecho_seq: UInt16; + function Getfrag_mtu: UInt16; + function Getfrag_pad: UInt16; + function Getgateway_s_b1: UInt8; + function Getgateway_s_b2: UInt8; + function Getgateway_s_b3: UInt8; + function Getgateway_s_b4: UInt8; + function Getgateway_s_l: UInt32; + function Getgateway_s_w1: UInt16; + function Getgateway_s_w2: UInt16; + procedure Setecho_id(const Value: UInt16); + procedure Setecho_seq(const Value: UInt16); + procedure Setfrag_mtu(const Value: UInt16); + procedure Setfrag_pad(const Value: UInt16); + procedure Setgateway_s_b1(const Value: UInt8); + procedure Setgateway_s_b2(const Value: UInt8); + procedure Setgateway_s_b3(const Value: UInt8); + procedure Setgateway_s_b4(const Value: UInt8); + procedure Setgateway_s_l(const Value: UInt32); + procedure Setgateway_s_w1(const Value: UInt16); + procedure Setgateway_s_w2(const Value: UInt16); + public + constructor Create; override; + property echo_id: UInt16 read Getecho_id write Setecho_id; // identifier to match requests with replies + property echo_seq: UInt16 read Getecho_seq write Setecho_seq; + property gateway_s_b1 : UInt8 read Getgateway_s_b1 write Setgateway_s_b1; + property gateway_s_b2 : UInt8 read Getgateway_s_b2 write Setgateway_s_b2; + property gateway_s_b3 : UInt8 read Getgateway_s_b3 write Setgateway_s_b3; + property gateway_s_b4 : UInt8 read Getgateway_s_b4 write Setgateway_s_b4; + property gateway_s_w1 : UInt16 read Getgateway_s_w1 write Setgateway_s_w1; + property gateway_s_w2 : UInt16 read Getgateway_s_w2 write Setgateway_s_w2; + property gateway_s_l : UInt32 read Getgateway_s_l write Setgateway_s_l; + property frag_pad: UInt16 read Getfrag_pad write Setfrag_pad; + property frag_mtu: UInt16 read Getfrag_mtu write Setfrag_mtu; + end; + + TIdicmp_dun = class(TIdUnion) + protected + function Getdata: UInt8; + function Getmask: UInt32; + procedure setdata(const Value: UInt8); + procedure Setmask(const Value: UInt32); + function Getts_otime: TIdNetTime; + function Getts_rtime: TIdNetTime; + function Getts_ttime: TIdNetTime; + procedure Setts_otime(const Value: TIdNetTime); + procedure Setts_rtime(const Value: TIdNetTime); + procedure Setts_ttime(const Value: TIdNetTime); + public + constructor Create; override; + property ts_otime: TIdNetTime read Getts_otime write Setts_otime; // time message was sent, to calc roundtrip time + property ts_rtime: TIdNetTime read Getts_rtime write Setts_rtime; + property ts_ttime: TIdNetTime read Getts_ttime write Setts_ttime; + property mask : UInt32 read Getmask write Setmask; + property data : UInt8 read Getdata write setdata; + end; + + TIdICMPHdr = class(TIdStruct) + protected + Ficmp_type: UInt8; // message type + Ficmp_code: UInt8; // error code + Ficmp_sum: UInt16; // one's complement checksum {Do not Localize} + Ficmp_hun: TIdicmp_hun; + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property icmp_type: UInt8 read Ficmp_type write Ficmp_type; // message type + property icmp_code: UInt8 read Ficmp_code write Ficmp_code; // error code + property icmp_sum: UInt16 read Ficmp_sum write Ficmp_sum; // one's complement checksum + property icmp_hun: TIdicmp_hun read Ficmp_hun; + end; + + //ICMPv6 + TIdicmp6_un = class(TIdUnion) + protected + function Geticmp6_data16: UInt16; + function Geticmp6_data8: UInt8; + procedure Seticmp6_data16(const Value: UInt16); + procedure Seticmp6_data8(const Value: UInt8); + function Geticmp6_seq: UInt16; + procedure Seticmp6_seq(const Value: UInt16); + function Geticmp6_un_data16(Index: Integer): UInt16; + function Geticmp6_un_data32: UInt32; + function Geticmp6_un_data8(Index: Integer): UInt8; + procedure Seticmp6_un_data16(Index: Integer; const Value: UInt16); + procedure Seticmp6_un_data32(const Value: UInt32); + procedure Seticmp6_un_data8(Index: Integer; const Value: UInt8); +{ + Ficmp6_un_data32 : UInt32; //* type-specific field */ + Ficmp6_un_data16 : array[0..1] of UInt16; //* type-specific field */ + icmp6_un_data8 : array[0..3] of UInt8); //* type-specific field */ +} + public + constructor Create; override; + property icmp6_un_data32 : UInt32 read Geticmp6_un_data32 write Seticmp6_un_data32; //* type-specific field */ + property icmp6_un_data16[Index:Integer] : UInt16 read Geticmp6_un_data16 write Seticmp6_un_data16; //array 0-1 * type-specific field */ + property icmp6_un_data8[Index:Integer] : UInt8 read Geticmp6_un_data8 write Seticmp6_un_data8; //array[0-3] * type-specific field */ + property icmp6_data32 : UInt32 read Geticmp6_un_data32 write Seticmp6_un_data32; + property icmp6_data16 : UInt16 read Geticmp6_data16 write Seticmp6_data16; + property icmp6_data8 : UInt8 read Geticmp6_data8 write Seticmp6_data8; + property icmp6_pptr : UInt32 read Geticmp6_un_data32 write Seticmp6_un_data32; + property icmp6_mtu : UInt32 read Geticmp6_un_data32 write Seticmp6_un_data32; + property icmp6_id : UInt16 read Geticmp6_data16 write Seticmp6_data16; + property icmp6_seq : UInt16 read Geticmp6_seq write Seticmp6_seq; + property icmp6_maxdelay : UInt16 read Geticmp6_data16 write Seticmp6_data16; + end; + + TIdicmp6_hdr = class(TIdStruct) + protected + Ficmp6_type : UInt8; //* type field */ + FIcmp6_code : UInt8; //* code field */ + Ficmp6_cksum : UInt16; //* checksum field */ + Fdata : TIdicmp6_un; + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property icmp6_type : UInt8 read Ficmp6_type write Ficmp6_type; //* type field */ + property icmp6_code : UInt8 read Ficmp6_code write Ficmp6_code; //* code field */ + property icmp6_cksum : UInt16 read Ficmp6_cksum write Ficmp6_cksum; //* checksum field */ + property data : TIdicmp6_un read Fdata; +{ case Integer of + 1: (icmp6_un_data32 : UInt32); //* type-specific field */ + 2: (icmp6_un_data16 : array[0..1] of UInt16); //* type-specific field */ + 3: (icmp6_un_data8 : array[0..3] of UInt8); //* type-specific field */ +} + end; + +//////////////////////////////////////////////////////////////////////////////// +//IP//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { options struct } + TIdIPOptions = class(TIdUnion) + public + constructor Create; override; + + //Delphi outputs warnings such as: + //[Hint] H2368 Visibility of property accessor method TIdIPOptions.get_ipopt_list should match property TIdIPOptions.ipopt_list + //[Hint] H2368 Visibility of property accessor method TIdIPOptions.set_ipopt_list should match property TIdIPOptions.ipopt_list + //if these aren't public + function get_ipopt_list(Index: Integer): UInt8; + procedure set_ipopt_list(Index: Integer; const Value: UInt8); + + property ipopt_list[Index : Integer] : UInt8 read get_ipopt_list write set_ipopt_list; default; //options proper + end; + + { packet header } + TIdIPHdr = class(TIdStruct) + protected + Fip_verlen: UInt8; // 1st nibble version, 2nd nibble header length div 4 (little-endian) + Fip_tos: UInt8; // type of service + Fip_len: UInt16; // total length + Fip_id: UInt16; // identification + Fip_off: UInt16; // 1st nibble flags, next 3 nibbles fragment offset (little-endian) + Fip_ttl: UInt8; // time to live + Fip_p: UInt8; // protocol + Fip_sum: UInt16; // checksum + Fip_src: TIdInAddr; // source address + Fip_dst: TIdInAddr; // dest address + Fip_options: UInt32; // options + padding + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + procedure CopyFrom(const ASource : TIdIPHdr); + property ip_verlen: UInt8 read Fip_verlen write Fip_verlen; // 1st nibble version, 2nd nibble header length div 4 (little-endian) + property ip_tos: UInt8 read Fip_tos write Fip_tos; // type of service + property ip_len: UInt16 read Fip_len write Fip_len; // total length + property ip_id: UInt16 read Fip_id write Fip_id; // identification + property ip_off: UInt16 read Fip_off write Fip_off; // 1st nibble flags, next 3 nibbles fragment offset (little-endian) + property ip_ttl: UInt8 read Fip_ttl write Fip_ttl; // time to live + property ip_p: UInt8 read Fip_p write Fip_p; // protocol + property ip_sum: UInt16 read Fip_sum write Fip_sum; // checksum + property ip_src: TIdInAddr read Fip_src; // source address + property ip_dst: TIdInAddr read Fip_dst; // dest address + property ip_options: UInt32 read Fip_options write Fip_options; // options + padding + end; + +//////////////////////////////////////////////////////////////////////////////// +//TCP/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { options structure } + TIdTCPOptions = class(TIdUnion) + public + constructor Create; override; + + //Delphi outputs warnings such as: + //[Hint] H2368 Visibility of property accessor method TIdTCPOptions.gettcpopt_list should match property TIdTCPOptions.tcpopt_list + //[Hint] H2368 Visibility of property accessor method TIdIPOptions.settcpopt_list should match property TIdTCPOptions.tcpopt_list + //if these aren't public + function gettcpopt_list(Index: Integer): UInt8; + procedure settcpopt_list(Index: Integer; const Value: UInt8); + + property tcpopt_list[Index : Integer] : UInt8 read gettcpopt_list write settcpopt_list; default; + end; + + { packet header } + TIdTCPHdr = class(TIdStruct) + protected + Ftcp_sport: UInt16; // source port + Ftcp_dport: UInt16; // destination port + Ftcp_seq: UInt32; // sequence number + Ftcp_ack: UInt32; // acknowledgement number + Ftcp_x2off: UInt8; // data offset + Ftcp_flags: UInt8; // control flags + Ftcp_win: UInt16; // window + Ftcp_sum: UInt16; // checksum + Ftcp_urp: UInt16; // urgent pointer + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property tcp_sport: UInt16 read Ftcp_sport write Ftcp_sport; // source port + property tcp_dport: UInt16 read Ftcp_dport write Ftcp_dport; // destination port + property tcp_seq: UInt32 read Ftcp_seq write Ftcp_seq; // sequence number + property tcp_ack: UInt32 read Ftcp_ack write Ftcp_ack; // acknowledgement number + property tcp_x2off: UInt8 read Ftcp_x2off write Ftcp_x2off; // data offset + property tcp_flags: UInt8 read Ftcp_flags write Ftcp_flags; // control flags + property tcp_win: UInt16 read Ftcp_win write Ftcp_win; // window + property tcp_sum: UInt16 read Ftcp_sum write Ftcp_sum; // checksum + property tcp_urp: UInt16 read Ftcp_urp write Ftcp_urp; // urgent pointer + end; + +//////////////////////////////////////////////////////////////////////////////// +//UDP/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { packet header } + TIdUDPHdr = class(TIdStruct) + protected + Fudp_sport: UInt16; // source port + Fudp_dport: UInt16; // destination port + Fudp_ulen: UInt16; // length + Fudp_sum: UInt16; // checksum + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + property udp_sport: UInt16 read Fudp_sport write Fudp_sport; // source port + property udp_dport: UInt16 read Fudp_dport write Fudp_dport; // destination port + property udp_ulen: UInt16 read Fudp_ulen write Fudp_ulen; // length + property udp_sum: UInt16 read Fudp_sum write Fudp_sum; // checksum + end; + +//////////////////////////////////////////////////////////////////////////////// +//IGMP////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { packet header } + + TIdIGMPHdr = class(TIdStruct) + protected + Figmp_type: UInt8; + Figmp_code: UInt8; + Figmp_sum: UInt16; + Figmp_group: TIdInAddr; + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property igmp_type: UInt8 read Figmp_type write Figmp_type; + property igmp_code: UInt8 read Figmp_code write Figmp_code; + property igmp_sum: UInt16 read Figmp_sum write Figmp_sum; + property igmp_group: TIdInAddr read Figmp_group; + end; + +//////////////////////////////////////////////////////////////////////////////// +//ETHERNET////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + TIdEtherAddr = class(TIdUnion) + public + constructor Create; override; + + procedure CopyFrom(const ASource : TIdEtherAddr); + procedure SetData(const Value: TIdBytes); + + //Delphi outputs warnings such as: + //[Hint] H2368 Visibility of property accessor method TIdEtherAddr.getether_addr_octet should match property TIdEtherAddr.ether_addr_octet + //[Hint] H2368 Visibility of property accessor method TIdEtherAddr.setether_addr_octet should match property TIdEtherAddr.ether_addr_octet + //if these aren't public + function getether_addr_octet(Index: Integer): UInt8; + procedure setether_addr_octet(Index: Integer; const Value: UInt8); + + property ether_addr_octet[Index: Integer] : UInt8 read getether_addr_octet write setether_addr_octet; default; + property Data: TIdBytes read FBuffer write SetData; + end; + + { packet header } + TIdEthernetHdr = class(TIdStruct) + protected + Fether_dhost: TIdEtherAddr; // destination ethernet address + Fether_shost: TIdEtherAddr; // source ethernet address + Fether_type: UInt16; // packet type ID + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure CopyFrom(const ASource : TIdEthernetHdr); + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property ether_dhost: TIdEtherAddr read Fether_dhost; // destination ethernet address + property ether_shost: TIdEtherAddr read Fether_shost; // source ethernet address + property ether_type: UInt16 read Fether_type write Fether_type; // packet type ID + end; + +//////////////////////////////////////////////////////////////////////////////// +//ARP/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { packet header } + TIdARPHdr = class(TIdStruct) + protected + Farp_hrd: UInt16; // format of hardware address + Farp_pro: UInt16; // format of protocol address + Farp_hln: UInt8; // length of hardware address + Farp_pln: UInt8; // length of protocol addres + Farp_op: UInt16; // operation type + // following hardcoded for ethernet/IP + Farp_sha: TIdEtherAddr; // sender hardware address + Farp_spa: TIdInAddr; // sender protocol address + Farp_tha: TIdEtherAddr; // target hardware address + Farp_tpa: TIdInAddr; // target protocol address + function GetBytesLen: UInt32; override; + public + constructor Create; override; + destructor Destroy; override; + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property arp_hrd: UInt16 read Farp_hrd write Farp_hrd; // format of hardware address + property arp_pro: UInt16 read Farp_pro write Farp_pro; // format of protocol address + property arp_hln: UInt8 read Farp_hln write Farp_hln; // length of hardware address + property arp_pln: UInt8 read Farp_pln write Farp_pln; // length of protocol addres + property arp_op: UInt16 read Farp_op write Farp_op; // operation type + // following hardcoded for ethernet/IP + property arp_sha: TIdEtherAddr read Farp_sha; // sender hardware address + property arp_spa: TIdInAddr read Farp_spa; // sender protocol address + property arp_tha: TIdEtherAddr read Farp_tha; // target hardware address + property arp_tpa: TIdInAddr read Farp_tpa; // target protocol address + end; + +//////////////////////////////////////////////////////////////////////////////// +//DNS/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { header } + + TIdDNSHdr = class(TIdStruct) + protected + Fdns_id: UInt16; // DNS packet ID + Fdns_flags: UInt16; // DNS flags + Fdns_num_q: UInt16; // number of questions + Fdns_num_answ_rr: UInt16; // number of answer resource records + Fdns_num_auth_rr: UInt16; // number of authority resource records + Fdns_num_addi_rr: UInt16; // number of additional resource records + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property dns_id: UInt16 read Fdns_id write Fdns_id; // DNS packet ID + property dns_flags: UInt16 read Fdns_flags write Fdns_flags; // DNS flags + property dns_num_q: UInt16 read Fdns_num_q write Fdns_num_q; // number of questions + property dns_num_answ_rr: UInt16 read Fdns_num_answ_rr write Fdns_num_answ_rr; // number of answer resource records + property dns_num_auth_rr: UInt16 read Fdns_num_auth_rr write Fdns_num_auth_rr; // number of authority resource records + property dns_num_addi_rr: UInt16 read Fdns_num_addi_rr write Fdns_num_addi_rr; // number of additional resource records + end; + +//////////////////////////////////////////////////////////////////////////////// +//RIP/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + { header } + TIdRIPHdr = class(TIdStruct) + protected + Frip_cmd: UInt8; // RIP command + Frip_ver: UInt8; // RIP version + Frip_rd: UInt16; // zero (v1) or routing domain (v2) + Frip_af: UInt16; // address family + Frip_rt: UInt16; // zero (v1) or route tag (v2) + Frip_addr: UInt32; // IP address + Frip_mask: UInt32; // zero (v1) or subnet mask (v2) + Frip_next_hop: UInt32; // zero (v1) or next hop IP address (v2) + Frip_metric: UInt32; // metric + function GetBytesLen: UInt32; override; + public + procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override; + procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override; + + property rip_cmd: UInt8 read Frip_cmd write Frip_cmd; // RIP command + property rip_ver: UInt8 read Frip_ver write Frip_ver; // RIP version + property rip_rd: UInt16 read Frip_rd write Frip_rd; // zero (v1) or routing domain (v2) + property rip_af: UInt16 read Frip_af write Frip_af; // address family + property rip_rt: UInt16 read Frip_rt write Frip_rt; // zero (v1) or route tag (v2) + property rip_addr: UInt32 read Frip_addr write Frip_addr; // IP address + property rip_mask: UInt32 read Frip_mask write Frip_mask; // zero (v1) or subnet mask (v2) + property rip_next_hop: UInt32 read Frip_next_hop write Frip_next_hop; // zero (v1) or next hop IP address (v2) + property rip_metric: UInt32 read Frip_metric write Frip_metric; // metric + end; + + +//////////////////////////////////////////////////////////////////////////////// + +implementation + +uses + SysUtils; + +{ TIdSunB } + +function TIdSunB.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4; +end; + +procedure TIdSunB.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fs_b1 := ABytes[VIndex]; + Inc(VIndex); + Fs_b2 := ABytes[VIndex]; + Inc(VIndex); + Fs_b3 := ABytes[VIndex]; + Inc(VIndex); + Fs_b4 := ABytes[VIndex]; + Inc(VIndex); +end; + +procedure TIdSunB.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Fs_b1; + Inc(VIndex); + VBytes[VIndex] := Fs_b2; + Inc(VIndex); + VBytes[VIndex] := Fs_b3; + Inc(VIndex); + VBytes[VIndex] := Fs_b4; + Inc(VIndex); +end; + +{ TIdSunW } + +function TIdSunW.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4; +end; + +procedure TIdSunW.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fs_w1 := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + Fs_w2 := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); +end; + +procedure TIdSunW.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(HostToLittleEndian(Fs_w1), VBytes, VIndex); + Inc(VIndex, 2); + CopyTIdUInt16(HostToLittleEndian(Fs_w2), VBytes, VIndex); + Inc(VIndex, 2); +end; + +{ TIdICMPEcho } + +function TIdICMPEcho.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4; +end; + +procedure TIdICMPEcho.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fid := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + seq := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); +end; + +procedure TIdICMPEcho.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(HostToLittleEndian(Fid), VBytes, VIndex); + Inc(VIndex, 2); + CopyTIdUInt16(HostToLittleEndian(seq), VBytes, VIndex); + Inc(VIndex, 2); +end; + +{ TIdICMPFrag } + +function TIdICMPFrag.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4; +end; + +procedure TIdICMPFrag.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fpad := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + Fmtu := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); +end; + +procedure TIdICMPFrag.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(HostToLittleEndian(Fpad), VBytes, VIndex); + Inc(VIndex, 2); + CopyTIdUInt16(HostToLittleEndian(Fmtu), VBytes, VIndex); + Inc(VIndex, 2); +end; + +{ TIdICMPTs } + +function TIdICMPTs.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 12; +end; + +procedure TIdICMPTs.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fotime := BytesToUInt32(ABytes, VIndex); // time message was sent, to calc roundtrip time + Inc(VIndex, 4); + Frtime := BytesToUInt32(ABytes, VIndex); + Inc(VIndex, 4); + Fttime := BytesToUInt32(ABytes, VIndex); + Inc(VIndex, 4); +end; + +procedure TIdICMPTs.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(HostToLittleEndian(Fotime), VBytes, VIndex); // time message was sent, to calc roundtrip time + Inc(VIndex, 4); + CopyTIdUInt16(HostToLittleEndian(Frtime), VBytes, VIndex); + Inc(VIndex, 4); + CopyTIdUInt16(HostToLittleEndian(Fttime), VBytes, VIndex); + Inc(VIndex, 4); +end; + +{ TIdicmp_hun } + +constructor TIdicmp_hun.Create; +begin + inherited Create; + SetBytesLen(4); +end; + +function TIdicmp_hun.Getecho_id: UInt16; +begin + Result := Getgateway_s_w1; +end; + +procedure TIdicmp_hun.Setecho_id(const Value: UInt16); +begin + Setgateway_s_w1(Value); +end; + +function TIdicmp_hun.Getecho_seq: UInt16; +begin + Result := Getgateway_s_w2; +end; + +procedure TIdicmp_hun.Setecho_seq(const Value: UInt16); +begin + Setgateway_s_w2(Value); +end; + +function TIdicmp_hun.Getgateway_s_w1: UInt16; +begin + Result := BytesToUInt32(FBuffer, 0); +end; + +procedure TIdicmp_hun.Setgateway_s_w1(const Value: UInt16); +begin + CopyTIdUInt32(Value, FBuffer, 0); +end; + +function TIdicmp_hun.Getgateway_s_w2: UInt16; +begin + Result := BytesToUInt16(FBuffer, 2); +end; + +procedure TIdicmp_hun.Setgateway_s_w2(const Value: UInt16); +begin + CopyTIdUInt16(HostToLittleEndian(Value), FBuffer, 2); +end; + +function TIdicmp_hun.Getgateway_s_b1: UInt8; +begin + Result := FBuffer[0]; +end; + +procedure TIdicmp_hun.Setgateway_s_b1(const Value: UInt8); +begin + FBuffer[0] := Value; +end; + +function TIdicmp_hun.Getgateway_s_b2: UInt8; +begin + Result := FBuffer[1]; +end; + +procedure TIdicmp_hun.Setgateway_s_b2(const Value: UInt8); +begin + FBuffer[1] := Value; +end; + +function TIdicmp_hun.Getgateway_s_b3: UInt8; +begin + Result := FBuffer[2]; +end; + +procedure TIdicmp_hun.Setgateway_s_b3(const Value: UInt8); +begin + FBuffer[2] := Value; +end; + +function TIdicmp_hun.Getgateway_s_b4: UInt8; +begin + Result := FBuffer[3]; +end; + +procedure TIdicmp_hun.Setgateway_s_b4(const Value: UInt8); +begin + FBuffer[3] := Value; +end; + +function TIdicmp_hun.Getgateway_s_l: UInt32; +begin + Result := BytesToUInt32(FBuffer, 0); +end; + +procedure TIdicmp_hun.Setgateway_s_l(const Value: UInt32); +begin + CopyTIdUInt32(Value, FBuffer, 0); +end; + +function TIdicmp_hun.Getfrag_pad: UInt16; +begin + Result := Getgateway_s_w1; +end; + +procedure TIdicmp_hun.Setfrag_pad(const Value: UInt16); +begin + Setgateway_s_w1(Value); +end; + +function TIdicmp_hun.Getfrag_mtu: UInt16; +begin + Result := Getgateway_s_w2; +end; + +procedure TIdicmp_hun.Setfrag_mtu(const Value: UInt16); +begin + Setgateway_s_w2(Value); +end; + +{ TIdicmp_dun } + +constructor TIdicmp_dun.Create; +begin + inherited Create; + SetBytesLen(12); +end; + +function TIdicmp_dun.Getts_otime: TIdNetTime; +begin + Result := BytesToUInt32(FBuffer, 0); +end; + +procedure TIdicmp_dun.Setts_otime(const Value: TIdNetTime); +begin + CopyTIdUInt32(Value, FBuffer, 0); +end; + +function TIdicmp_dun.Getts_rtime: TIdNetTime; +begin + Result := BytesToUInt32(FBuffer, 4); +end; + +procedure TIdicmp_dun.Setts_rtime(const Value: TIdNetTime); +begin + CopyTIdUInt32(Value, FBuffer, 4); +end; + +function TIdicmp_dun.Getts_ttime: TIdNetTime; +begin + Result := BytesToUInt32(FBuffer, 4); +end; + +procedure TIdicmp_dun.Setts_ttime(const Value: TIdNetTime); +begin + CopyTIdUInt32(Value, FBuffer, 8); +end; + +function TIdicmp_dun.Getmask: UInt32; +begin + Result := Getts_otime; +end; + +procedure TIdicmp_dun.Setmask(const Value: UInt32); +begin + Setts_otime(Value); +end; + +function TIdicmp_dun.Getdata: UInt8; +begin + Result := FBuffer[0]; +end; + +procedure TIdicmp_dun.setdata(const Value: UInt8); +begin + FBuffer[0] := Value; +end; + +{ TIdICMPHdr } + +constructor TIdICMPHdr.Create; +begin + inherited Create; + Ficmp_hun := TIdicmp_hun.Create; +end; + +destructor TIdICMPHdr.Destroy; +begin + FreeAndNil(Ficmp_hun); + inherited Destroy; +end; + +function TIdICMPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4 + Ficmp_hun.BytesLen; +end; + +procedure TIdICMPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Ficmp_type := ABytes[VIndex]; + Inc(VIndex); + Ficmp_code := ABytes[Vindex]; + Inc(VIndex); + Ficmp_sum := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + Ficmp_hun.ReadStruct(ABytes, VIndex); +end; + +procedure TIdICMPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Ficmp_type; + Inc(VIndex); + VBytes[Vindex] := Ficmp_code; + Inc(VIndex); + CopyTIdUInt16(Ficmp_sum, VBytes, VIndex); + Inc(VIndex, 2); + Ficmp_hun.WriteStruct(VBytes, VIndex); +end; + +{ TIdIPOptions } + +constructor TIdIPOptions.Create; +begin + inherited Create; + SetBytesLen(Id_MAX_IPOPTLEN); +end; + +function TIdIPOptions.get_ipopt_list(Index: Integer): UInt8; +begin + Assert(Index < Id_MAX_IPOPTLEN, 'Out of range'); {do not localize} + Result := FBuffer[Index]; +end; + +procedure TIdIPOptions.set_ipopt_list(Index: Integer; const Value: UInt8); +begin + Assert(Index < Id_MAX_IPOPTLEN, 'Out of range'); {do not localize} + FBuffer[Index] := Value; +end; + +{ TIdIPHdr } + +constructor TIdIPHdr.Create; +begin + inherited Create; + Fip_src := TIdInAddr.Create; + Fip_dst := TIdInAddr.Create; +end; + +destructor TIdIPHdr.Destroy; +begin + FreeAndNil(Fip_src); + FreeAndNil(Fip_dst); + inherited Destroy; +end; + +function TIdIPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 12 + Fip_src.BytesLen + Fip_dst.BytesLen + 4; +end; + +procedure TIdIPHdr.CopyFrom(const ASource: TIdIPHdr); +begin + Fip_verlen := ASource.ip_verlen; + Fip_tos := ASource.ip_tos; + Fip_len := ASource.ip_len; + Fip_id := ASource.ip_id; + Fip_off := ASource.ip_off; + Fip_ttl := ASource.ip_ttl; + Fip_p := ASource.ip_p; + Fip_sum := ASource.ip_sum; + Fip_src.CopyFrom(ASource.ip_src); + Fip_dst.CopyFrom(ASource.ip_dst); + Fip_options := ASource.ip_options; +end; + +procedure TIdIPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +var LIpHeaderLen : UInt32; +begin + inherited ReadStruct(ABytes, VIndex); + Fip_verlen := ABytes[VIndex]; // 1st nibble version, 2nd nibble header length div 4 (little-endian) + Inc(VIndex); + LIpHeaderLen := (Fip_verlen and $0F) * 4; + Fip_tos := ABytes[VIndex]; // type of service + Inc(VIndex); + Fip_len := BytesToUInt16(ABytes, VIndex); // total length + Inc(VIndex, 2); + Fip_id := BytesToUInt16(ABytes, VIndex); // identification + Inc(VIndex, 2); + Fip_off := BytesToUInt16(ABytes, VIndex); // 1st nibble flags, next 3 nibbles fragment offset (little-endian) + Inc(VIndex, 2); + Fip_ttl := ABytes[VIndex]; // time to live + Inc(VIndex); + Fip_p := ABytes[VIndex]; // protocol + Inc(VIndex); + Fip_sum := BytesToUInt16(ABytes, VIndex); // checksum + Inc(VIndex, 2); + Fip_src.ReadStruct(ABytes, VIndex); // source address + Fip_dst.ReadStruct(ABytes, VIndex); // dest address + //Fip_options may not be present in the packet + if VIndex >= LIpHeaderLen then + begin + Fip_options := BytesToUInt32(ABytes, VIndex); // options + padding + end; + //be sure that we indicate we read the entire packet in case + //the size varies. + VIndex := LIpHeaderLen; +end; + +procedure TIdIPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); + +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Fip_verlen; // 1st nibble version, 2nd nibble header length div 4 (little-endian) + Inc(VIndex); + VBytes[VIndex] := Fip_tos; // type of service + Inc(VIndex); + CopyTIdUInt16(Fip_len, VBytes, VIndex); // total length + Inc(VIndex, 2); + CopyTIdUInt16(Fip_id, VBytes, VIndex); // identification + Inc(VIndex, 2); + CopyTIdUInt16(Fip_off, VBytes, VIndex); // 1st nibble flags, next 3 nibbles fragment offset (little-endian) + Inc(VIndex, 2); + Fip_ttl := VBytes[VIndex]; // time to live + Inc(VIndex); + Fip_p := VBytes[VIndex]; // protocol + Inc(VIndex); + CopyTIdUInt16(Fip_sum, VBytes, VIndex); // checksum + Inc(VIndex, 2); + Fip_src.WriteStruct(VBytes, VIndex); // source address + Fip_dst.WriteStruct(VBytes, VIndex); // dest address + CopyTIdUInt32(Fip_options, VBytes, VIndex); // options + padding + Inc(VIndex, 4); +end; + +{ TIdTCPOptions } + +constructor TIdTCPOptions.Create; +begin + inherited Create; + SetBytesLen(Id_MAX_IPOPTLEN); +end; + +function TIdTCPOptions.gettcpopt_list(Index: Integer): UInt8; +begin + Assert(Index < Id_MAX_IPOPTLEN, 'Out of range'); + Result := FBuffer[Index]; +end; + +procedure TIdTCPOptions.settcpopt_list(Index: Integer; const Value: UInt8); +begin + Assert(Index < Id_MAX_IPOPTLEN, 'Out of range'); + FBuffer[Index] := Value; +end; + +{ TIdTCPHdr } + +function TIdTCPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 20; +end; + +procedure TIdTCPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Ftcp_sport := BytesToUInt16(ABytes, VIndex); // source port + Inc(VIndex, 2); + Ftcp_dport := BytesToUInt16(ABytes, VIndex); // destination port + Inc(VIndex, 2); + Ftcp_seq := BytesToUInt32(ABytes, VIndex); // sequence number + Inc(VIndex, 4); + Ftcp_ack := BytesToUInt32(ABytes, VIndex); // acknowledgement number + Inc(VIndex, 4); + Ftcp_x2off := ABytes[VIndex]; // data offset + Inc(VIndex); + Ftcp_flags := ABytes[VIndex]; // control flags + Inc(VIndex); + Ftcp_win := BytesToUInt16(ABytes, VIndex); // window + Inc(VIndex, 2); + Ftcp_sum := BytesToUInt16(ABytes, VIndex); // checksum + Inc(VIndex, 2); + Ftcp_urp := BytesToUInt16(ABytes, VIndex); // urgent pointer + Inc(VIndex, 2); +end; + +procedure TIdTCPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(Ftcp_sport, VBytes, VIndex); // source port + Inc(VIndex, 2); + CopyTIdUInt16(Ftcp_dport, VBytes, VIndex); // destination port + Inc(VIndex, 2); + CopyTIdUInt32(Ftcp_seq, VBytes, VIndex); // sequence number + Inc(VIndex, 4); + CopyTIdUInt32(Ftcp_ack, VBytes, VIndex); // acknowledgement number + Inc(VIndex, 4); + VBytes[VIndex] := Ftcp_x2off; // data offset + Inc(VIndex); + VBytes[VIndex] := Ftcp_flags; // control flags + Inc(VIndex); + CopyTIdUInt16(Ftcp_win, VBytes, VIndex); // window + Inc(VIndex, 2); + CopyTIdUInt16(Ftcp_sum, VBytes, VIndex); // checksum + Inc(VIndex, 2); + CopyTIdUInt16(Ftcp_urp, VBytes, VIndex); // urgent pointer + Inc(VIndex, 2); +end; + +{ TIdUDPHdr } + +function TIdUDPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 8; +end; + +procedure TIdUDPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fudp_sport := BytesToUInt16(ABytes, VIndex); // source port + Inc(VIndex, 2); + Fudp_dport := BytesToUInt16(ABytes, VIndex); // destination port + Inc(VIndex, 2); + Fudp_ulen := BytesToUInt16(ABytes, VIndex); // length + Inc(VIndex, 2); + Fudp_sum := BytesToUInt16(ABytes, VIndex); // checksum + Inc(VIndex, 2); +end; + +procedure TIdUDPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(Fudp_sport, VBytes, VIndex); // source port + Inc(VIndex, 2); + CopyTIdUInt16(Fudp_dport, VBytes, VIndex); // destination port + Inc(VIndex, 2); + CopyTIdUInt16(Fudp_ulen, VBytes, VIndex); // length + Inc(VIndex, 2); + CopyTIdUInt16(Fudp_sum, VBytes, VIndex); // checksum + Inc(VIndex, 2); +end; + +{ TIdIGMPHdr } + +constructor TIdIGMPHdr.Create; +begin + inherited Create; + Figmp_group := TIdInAddr.Create; +end; + +destructor TIdIGMPHdr.Destroy; +begin + FreeAndNil(Figmp_group); + inherited Destroy; +end; + +function TIdIGMPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4 + Figmp_group.BytesLen; +end; + +procedure TIdIGMPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Figmp_type := ABytes[VIndex]; + Inc(VIndex); + Figmp_code := ABytes[VIndex]; + Inc(VIndex); + Figmp_sum := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + Figmp_group.ReadStruct(ABytes, VIndex); +end; + +procedure TIdIGMPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Figmp_type; + Inc(VIndex); + VBytes[VIndex] := Figmp_code; + Inc(VIndex); + CopyTIdUInt16(Figmp_sum, VBytes, VIndex); + Inc(VIndex, 2); + Figmp_group.WriteStruct(VBytes, VIndex); +end; + +{ TIdEtherAddr } + +constructor TIdEtherAddr.Create; +begin + inherited Create; + SetBytesLen(Id_ETHER_ADDR_LEN); +end; + +procedure TIdEtherAddr.setether_addr_octet(Index: Integer; const Value: UInt8); +begin + Assert(Index < Id_ETHER_ADDR_LEN, 'Out of range'); + FBuffer[Index] := Value; +end; + +function TIdEtherAddr.getether_addr_octet(Index: Integer): UInt8; +begin + Assert(Index < Id_ETHER_ADDR_LEN, 'Out of range'); + Result := FBuffer[Index]; +end; + +procedure TIdEtherAddr.CopyFrom(const ASource: TIdEtherAddr); +begin + SetData(ASource.Data); +end; + +procedure TIdEtherAddr.SetData(const Value: TIdBytes); +begin + CopyTIdBytes(Value, 0, FBuffer, 0, Id_ETHER_ADDR_LEN); +end; + +{ TIdEthernetHdr } + +constructor TIdEthernetHdr.Create; +begin + inherited Create; + Fether_dhost := TIdEtherAddr.Create; + Fether_shost := TIdEtherAddr.Create; +end; + +destructor TIdEthernetHdr.Destroy; +begin + FreeAndNil(Fether_dhost); + FreeAndNil(Fether_shost); + inherited Destroy; +end; + +function TIdEthernetHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + Fether_dhost.BytesLen + Fether_shost.BytesLen + 2; +end; + +procedure TIdEthernetHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fether_dhost.ReadStruct(ABytes, VIndex); // destination ethernet address + Fether_shost.ReadStruct(ABytes, VIndex); // source ethernet address + Fether_type := BytesToUInt16(ABytes, VIndex); // packet type ID + Inc(VIndex, 2); +end; + +procedure TIdEthernetHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + Fether_dhost.WriteStruct(VBytes, VIndex); // destination ethernet address + Fether_shost.WriteStruct(VBytes, VIndex); // source ethernet address + CopyTIdUInt16(Fether_type, VBytes, VIndex); // packet type ID + Inc(VIndex, 2); +end; + +procedure TIdEthernetHdr.CopyFrom(const ASource: TIdEthernetHdr); +begin + Fether_dhost.CopyFrom(ASource.Fether_dhost); + Fether_shost.CopyFrom(ASource.Fether_shost); + Fether_type := ASource.Fether_type; +end; + +{ TIdARPHdr } + +constructor TIdARPHdr.Create; +begin + inherited Create; + Farp_sha := TIdEtherAddr.Create; // sender hardware address + Farp_spa := TIdInAddr.Create; // sender protocol address + Farp_tha := TIdEtherAddr.Create; // target hardware address + Farp_tpa := TIdInAddr.Create; // target protocol address +end; + +destructor TIdARPHdr.Destroy; +begin + FreeAndNil(Farp_sha); + FreeAndNil(Farp_spa); + FreeAndNil(Farp_tha); + FreeAndNil(Farp_tpa); + inherited Destroy; +end; + +function TIdARPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 8 + Farp_sha.BytesLen + Farp_spa.BytesLen + Farp_tha.BytesLen + Farp_tpa.BytesLen; +end; + +procedure TIdARPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Farp_hrd := BytesToUInt16(ABytes, VIndex); // format of hardware address + Inc(VIndex, 2); + Farp_pro := BytesToUInt16(ABytes, VIndex); // format of protocol address + Inc(VIndex, 2); + Farp_hln := ABytes[VIndex]; // length of hardware address + Inc(VIndex); + Farp_pln := ABytes[VIndex]; // length of protocol addres + Inc(VIndex); + Farp_op := BytesToUInt16(ABytes, VIndex); // operation type + Inc(VIndex, 2); + // following hardcoded for ethernet/IP + Farp_sha.ReadStruct(ABytes, VIndex); // sender hardware address + Farp_spa.ReadStruct(ABytes, VIndex); // sender protocol address + Farp_tha.ReadStruct(ABytes, VIndex); // target hardware address + Farp_tpa.ReadStruct(ABytes, VIndex); // target protocol address +end; + +procedure TIdARPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(Farp_hrd, VBytes, VIndex); // format of hardware address + Inc(VIndex, 2); + CopyTIdUInt16(Farp_pro, VBytes, VIndex); // format of protocol address + Inc(VIndex, 2); + VBytes[VIndex] := Farp_hln; // length of hardware address + Inc(VIndex); + VBytes[VIndex] := Farp_pln; // length of protocol addres + Inc(VIndex); + CopyTIdUInt16(Farp_op, VBytes, VIndex); // operation type + Inc(VIndex, 2); + // following hardcoded for ethernet/IP + Farp_sha.WriteStruct(VBytes, VIndex); // sender hardware address + Farp_spa.WriteStruct(VBytes, VIndex); // sender protocol address + Farp_tha.WriteStruct(VBytes, VIndex); // target hardware address + Farp_tpa.WriteStruct(VBytes, VIndex); // target protocol address +end; + +{ TIdDNSHdr } + +function TIdDNSHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 12; +end; + +procedure TIdDNSHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Fdns_id := BytesToUInt16(ABytes, VIndex); // DNS packet ID + Inc(VIndex, 2); + Fdns_flags := BytesToUInt16(ABytes, VIndex); // DNS flags + Inc(VIndex, 2); + Fdns_num_q := BytesToUInt16(ABytes, VIndex); // number of questions + Inc(VIndex, 2); + Fdns_num_answ_rr := BytesToUInt16(ABytes, VIndex);// number of answer resource records + Inc(VIndex, 2); + Fdns_num_auth_rr := BytesToUInt16(ABytes, VIndex); // number of authority resource records + Inc(VIndex, 2); + Fdns_num_addi_rr := BytesToUInt16(ABytes, VIndex); // number of additional resource records + Inc(VIndex, 2); +end; + +procedure TIdDNSHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + CopyTIdUInt16(Fdns_id, VBytes, VIndex); // DNS packet ID + Inc(VIndex, 2); + CopyTIdUInt16(Fdns_flags, VBytes, VIndex); // DNS flags + Inc(VIndex, 2); + CopyTIdUInt16(Fdns_num_q, VBytes, VIndex); // number of questions + Inc(VIndex, 2); + CopyTIdUInt16(Fdns_num_answ_rr, VBytes, VIndex); // number of answer resource records + Inc(VIndex, 2); + CopyTIdUInt16(Fdns_num_auth_rr, VBytes, VIndex); // number of authority resource records + Inc(VIndex, 2); + CopyTIdUInt16(Fdns_num_addi_rr, VBytes, VIndex); // number of additional resource records + Inc(VIndex, 2); +end; + +{ TIdRIPHdr } + +function TIdRIPHdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 24; +end; + +procedure TIdRIPHdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Frip_cmd := ABytes[VIndex]; // RIP command + Inc(VIndex); + Frip_ver := ABytes[VIndex]; // RIP version + Inc(VIndex); + Frip_rd := BytesToUInt16(ABytes, VIndex); // zero (v1) or routing domain (v2) + Inc(VIndex, 2); + Frip_af := BytesToUInt16(ABytes, VIndex); // address family + Inc(VIndex, 2); + Frip_rt := BytesToUInt16(ABytes, VIndex); // zero (v1) or route tag (v2) + Inc(VIndex, 2); + Frip_addr := BytesToUInt32(ABytes, VIndex); // IP address + Inc(VIndex, 4); + Frip_mask := BytesToUInt32(ABytes, VIndex); // zero (v1) or subnet mask (v2) + Inc(VIndex, 4); + Frip_next_hop := BytesToUInt32(ABytes, VIndex); // zero (v1) or next hop IP address (v2) + Inc(VIndex, 4); + Frip_metric := BytesToUInt32(ABytes, VIndex); // metric + Inc(VIndex, 4); +end; + +procedure TIdRIPHdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Frip_cmd; // RIP command + Inc(VIndex); + VBytes[VIndex] := Frip_ver; // RIP version + Inc(VIndex); + CopyTIdUInt16(Frip_rd, VBytes, VIndex); // zero (v1) or routing domain (v2) + Inc(VIndex, 2); + CopyTIdUInt16(Frip_af, VBytes, VIndex); // address family + Inc(VIndex, 2); + CopyTIdUInt16(Frip_rt, VBytes, VIndex); // zero (v1) or route tag (v2) + Inc(VIndex, 2); + CopyTIdUInt32(Frip_addr, VBytes, VIndex); // IP address + Inc(VIndex, 4); + CopyTIdUInt32(Frip_mask, VBytes, VIndex); // zero (v1) or subnet mask (v2) + Inc(VIndex, 4); + CopyTIdUInt32(Frip_next_hop, VBytes, VIndex); // zero (v1) or next hop IP address (v2) + Inc(VIndex, 4); + CopyTIdUInt32(Frip_metric, VBytes, VIndex); // metric + Inc(VIndex, 4); +end; + +{ TIdInAddr } + +procedure TIdInAddr.CopyFrom(const ASource: TIdInAddr); +begin + s_l := ASource.s_l; +end; + +{ TIdicmp6_un } + +constructor TIdicmp6_un.Create; +begin + inherited Create; + SetBytesLen(4); +end; + +function TIdicmp6_un.Geticmp6_un_data16(Index: Integer): UInt16; +begin + Result := 0; + case Index of + 0 : Result := BytesToUInt16(FBuffer, 0); + 1 : Result := BytesToUInt16(FBuffer, 2); + end; +end; + +procedure TIdicmp6_un.Seticmp6_un_data16(Index: Integer; const Value: UInt16); +begin + case Index of + 0 : CopyTIdUInt16(Value, FBuffer, 0); + 1 : CopyTIdUInt16(Value, FBuffer, 2); + end; +end; + +function TIdicmp6_un.Geticmp6_un_data32: UInt32; +begin + Result := BytesToUInt32(FBuffer, 0); +end; + +procedure TIdicmp6_un.Seticmp6_un_data32(const Value: UInt32); +begin + CopyTIdUInt32(Value, FBuffer, 0); +end; + +function TIdicmp6_un.Geticmp6_un_data8(Index: Integer): UInt8; +begin + Assert((Index>-1) and (Index<4), 'Out of range'); + Result := FBuffer[Index]; +end; + +procedure TIdicmp6_un.Seticmp6_un_data8(Index: Integer; const Value: UInt8); +begin + Assert((Index>-1) and (Index<4), 'Out of range'); + FBuffer[Index] := Value; +end; + +function TIdicmp6_un.Geticmp6_data8: UInt8; +begin + Result := FBuffer[0]; +end; + +procedure TIdicmp6_un.Seticmp6_data8(const Value: UInt8); +begin + FBuffer[0] := Value; +end; + +function TIdicmp6_un.Geticmp6_data16: UInt16; +begin + Result := BytesToUInt16(FBuffer, 0); +end; + +procedure TIdicmp6_un.Seticmp6_data16(const Value: UInt16); +begin + CopyTIdUInt16(Value, FBuffer, 0); +end; + +function TIdicmp6_un.Geticmp6_seq: UInt16; +begin + Result := Geticmp6_un_data16(1); +end; + +procedure TIdicmp6_un.Seticmp6_seq(const Value: UInt16); +begin + Seticmp6_un_data16(1, Value); +end; + +{ TIdicmp6_hdr } + +constructor TIdicmp6_hdr.Create; +begin + inherited Create; + Fdata := TIdicmp6_un.Create; +end; + +destructor TIdicmp6_hdr.Destroy; +begin + FreeAndNil(Fdata); + inherited Destroy; +end; + +function TIdicmp6_hdr.GetBytesLen: UInt32; +begin + Result := inherited GetBytesLen + 4 + Fdata.BytesLen; +end; + +procedure TIdicmp6_hdr.ReadStruct(const ABytes: TIdBytes; var VIndex: UInt32); +begin + inherited ReadStruct(ABytes, VIndex); + Ficmp6_type := ABytes[VIndex]; + Inc(VIndex); + FIcmp6_code := ABytes[VIndex]; + Inc(VIndex); + Ficmp6_cksum := BytesToUInt16(ABytes, VIndex); + Inc(VIndex, 2); + Fdata.ReadStruct(ABytes, VIndex); +end; + +procedure TIdicmp6_hdr.WriteStruct(var VBytes: TIdBytes; var VIndex: UInt32); +begin + inherited WriteStruct(VBytes, VIndex); + VBytes[VIndex] := Ficmp6_type; + Inc(VIndex); + VBytes[VIndex] := FIcmp6_code; + Inc(VIndex); + CopyTIdUInt16(Ficmp6_cksum, VBytes, VIndex); + Inc(VIndex, 2); + Fdata.WriteStruct(VBytes, VIndex); +end; + +end. + diff --git a/indy/Core/IdRegisterCore.lrs b/indy/Core/IdRegisterCore.lrs new file mode 100644 index 0000000..244c6f3 --- /dev/null +++ b/indy/Core/IdRegisterCore.lrs @@ -0,0 +1,675 @@ +LazarusResources.Add('TIdTCPClient','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'03030303030303030303030303030315151515150404",'#13#10'"05041111111111111111' + +'1111111111110315151515150404",'#13#10'"050411111111111111111111111111031515' + +'151515150404",'#13#10'"050403030303030303030311111103151515151515150404",' + +#13#10'"050415151515151515031111110303030303030303030404",'#13#10'"050415151' + +'515151503111111111111111111111111110404",'#13#10'"0504151515151503111111111' + +'11111111111111111110404",'#13#10'"05041515151515030303030303030303030303030' + +'3030404",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10 + +'"050415151515151515151515151515151515151515150404",'#13#10'"050415000000000' + +'000150000000000150000000000150404",'#13#10'"0504151515000015151500001515151' + +'50000150000150404",'#13#10'"05041515150000151515000015151515000015000015040' + +'4",'#13#10'"050415151500001515150000151515150000000000150404",'#13#10'"0504' + +'15151500001515150000151515150000151515150404",'#13#10'"05041515150000151515' + +'0000151515150000151515150404",'#13#10'"050415151500001515150000000000150000' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdUDPClient','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'08080808080808080808080808080815151515150404",'#13#10'"03040707070707070707' + +'0707070707070815151515150404",'#13#10'"030407070707070707070707070707081515' + +'151515150404",'#13#10'"030408080808080808080807070708151515151515150404",' + +#13#10'"030415151515151515080707070808080808080808080404",'#13#10'"030415151' + +'515151508070707070707070707070707070404",'#13#10'"0304151515151508070707070' + +'70707070707070707070404",'#13#10'"03041515151515080808080808080808080808080' + +'8080404",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10 + +'"030415151515151515151515151515151515151515150404",'#13#10'"030415000015150' + +'000150000000015150000000000150404",'#13#10'"0304150000151500001500001500001' + +'50000150000150404",'#13#10'"03041500001515000015000015000015000015000015040' + +'4",'#13#10'"030415000015150000150000150000150000000000150404",'#13#10'"0304' + +'15000015150000150000150000150000151515150404",'#13#10'"03041500001515000015' + +'0000150000150000151515150404",'#13#10'"030415150000000015150000000015150000' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdCmdTCPClient','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'15151515151515151515151515151515151515150404",'#13#10'"03041515151515151515' + +'1515151515151515151515150404",'#13#10'"030415151515151515151515151515151515' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415070000000015000008150800000000000007150404",'#13#10'"030400000' + +'815080015150007150700151500000800000404",'#13#10'"0304000015151515151500001' + +'50000151500001500000404",'#13#10'"03040000151515151515000707070015150000150' + +'0000404",'#13#10'"030400001515151515150008000800151500001500000404",'#13#10 + +'"030400000815080015150015071500151500000800000404",'#13#10'"030415070000000' + +'815000000150000000000000007150404",'#13#10'"0304151515151515151515151515151' + +'51515151515150404",'#13#10'"03041515151515151515151515151515151515151515040' + +'4",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'15151515151515151515151515151515151515150404",'#13#10'"03041515151515151515' + +'1515151515151515151515150404",'#13#10'"030415151515151515151515151515151515' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdIPMCastClient','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515080808080808151515151515150404",'#13#10'"0504' + +'15151515150808150708070707080715151515150404",'#13#10'"05041515151507070708' + +'0707070708070707151515150404",'#13#10'"050415151515080707070708080815080708' + +'151515150404",'#13#10'"050415151508150815080715070707150815081515150404",' + +#13#10'"050415151508070708150708080707080707081515150404",'#13#10'"050415151' + +'508080708070807070707080707081515150404",'#13#10'"0504151515080815080707080' + +'70815080708081515150404",'#13#10'"05041515150807070815070808150708070708151' + +'5150404",'#13#10'"050415151508150807081500001508150815081515150404",'#13#10 + +'"050415151507070707150800000715070708071515150404",'#13#10'"050415151515080' + +'708070800000708081508151515150404",'#13#10'"0504151515151508071500000008150' + +'70815151515150404",'#13#10'"05041515151515151507000808001507151515151515040' + +'4",'#13#10'"050415151515151515080000000015151515151515150404",'#13#10'"0504' + +'15151515151515000008070007151515151515150404",'#13#10'"05041515151515150700' + +'0700080008151515151515150404",'#13#10'"050415151515151507000800080008151515' + +'151515150404",'#13#10'"050415151515151508000808150000071515151515150404",' + +#13#10'"050415151515151500080800080800071515151515150404",'#13#10'"050404040' + +'404040700070800001500080404040404040404",'#13#10'"0505040404040407000808080' + +'70008080404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdIOHandlerStack','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050008000004040004040004040000070404040404040' + +'5",'#13#10'"050408080708121212121212121212040815151515150704",'#13#10'"0504' + +'08080700120412041204120412040815151515150404",'#13#10'"05040007080808080808' + +'0808080808030815151515150404",'#13#10'"050408151515151515151515151515080815' + +'151515150704",'#13#10'"050408151507070707070708151515080815151515150404",' + +#13#10'"050408150707070708070708081515080815151515150404",'#13#10'"050408070' + +'707070800080707080815080815151515150704",'#13#10'"0504081507070708080807070' + +'80815080815151515150404",'#13#10'"05040807070708080707000708081508081515151' + +'5150704",'#13#10'"050408150707080807080807030807080815151515150404",'#13#10 + +'"050408150707080807080808080707000807151515150704",'#13#10'"050408071508080' + +'807080808081515070008070715150404",'#13#10'"0504081515150800080008080407150' + +'80808150807150404",'#13#10'"05040815151515151515151508000708070707070708040' + +'4",'#13#10'"050408070707070707070707070800070707070707080704",'#13#10'"0504' + +'08000800080008000803040308070707070707080804",'#13#10'"05041515151507151515' + +'0715070808080707070707080704",'#13#10'"050415151515151515151515150708080707' + +'070707000704",'#13#10'"050415151515151515151515151508080808080807080704",' + +#13#10'"050415151515151515151515151515080800080008070804",'#13#10'"050404040' + +'404040404040404040404040707070708070704",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdIOHandlerStream','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505000000050' + +'50505050505050505",'#13#10'"05050404040404040404040008040404040404040404040' + +'5",'#13#10'"050415151515151515150306061515151515151515150404",'#13#10'"0504' + +'15151515151515151506060615151515151515150404",'#13#10'"05041515151515151515' + +'1500061206151515151515150404",'#13#10'"050415151515150715151504060606081515' + +'151515150404",'#13#10'"050415151515150800150006040604071515151515150404",' + +#13#10'"050415151515151508000604060600151515151515150404",'#13#10'"050415151' + +'515150006060606040015151515151515150404",'#13#10'"0504151515150406061206060' + +'60015151515151515150404",'#13#10'"05041515150006060606061206081515151515151' + +'5150404",'#13#10'"050415151506060406120606060300151515151515150404",'#13#10 + +'"050415150004060406060606120015151515151515150404",'#13#10'"050415150603060' + +'400061207070015001508151515150404",'#13#10'"0504151506080606060604060600031' + +'50002031515150404",'#13#10'"05041515000604061206060606060608000015151515040' + +'4",'#13#10'"050415150006060406060604061206060000151515150404",'#13#10'"0504' + +'15151504060806060606060606060606000815150404",'#13#10'"05041515150006080006' + +'0406040006040612060615150404",'#13#10'"050415151515060002060408060604000606' + +'060615150404",'#13#10'"050415151515030606020006060604000604040615150404",' + +#13#10'"050415151515150302000008040606061206061515150404",'#13#10'"050404040' + +'404040404000606060604040608040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdServerIOHandlerStack','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505000000000000000505050505050' + +'50505050505050505",'#13#10'"05050015151515150004040004040000070404040404040' + +'5",'#13#10'"050400151414141500121212121212040815151515150704",'#13#10'"0504' + +'00151414141500041204120412040815151515150404",'#13#10'"05040015080808080008' + +'0808080808030815151515150404",'#13#10'"050400000000000000151515151515080815' + +'151515150704",'#13#10'"000000151507070700000008151515080815151515150404",' + +#13#10'"001515070707070707080008081515080815151515150404",'#13#10'"001508080' + +'808080808080007080815080815151515150704",'#13#10'"0000000000000000000000070' + +'80815080815151515150404",'#13#10'"05040807070708080707000708081508081515151' + +'5150704",'#13#10'"050408150707080807080807030807080815151515150404",'#13#10 + +'"050408150707080807080808080707000807151515150704",'#13#10'"050408071508080' + +'807080808081515070008070715150404",'#13#10'"0504081515150800080008080407150' + +'80808150807150404",'#13#10'"05040815151515151515151508000708070707070708040' + +'4",'#13#10'"050408070707070707070707070800070707070707080704",'#13#10'"0504' + +'08000800080008000803040308070707070707080804",'#13#10'"05041515151507151515' + +'0715070808080707070707080704",'#13#10'"050415151515151515151515150708080707' + +'070707000704",'#13#10'"050415151515151515151515151508080808080807080704",' + +#13#10'"050415151515151515151515151515080800080008070804",'#13#10'"050404040' + +'404040404040404040404040707070708070704",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdConnectionIntercept','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515150215151515151515151515151515150404",'#13#10'"05040303030302020303' + +'0303030303030303030303030404",'#13#10'"050415151502020215151515151515151515' + +'151515150404",'#13#10'"050415150202020202020202020202151515151515150404",' + +#13#10'"050415150202020202020202020202021515151515150404",'#13#10'"050415151' + +'502020215151515151515020215151515150404",'#13#10'"0504151515150202151515151' + +'51515150202151515150404",'#13#10'"05041515151515021515151515151515150202151' + +'5150404",'#13#10'"050415151515151515151509090909090909090909150404",'#13#10 + +'"050415151515151515151509090909090909090909150404",'#13#10'"050415150215151' + +'515151515151515151502021515150404",'#13#10'"0504151502021515151515151515151' + +'50202151515150404",'#13#10'"05041515020202151515151515151502021515151515040' + +'4",'#13#10'"050415150202020202020202020202021515151515150404",'#13#10'"0504' + +'15150202020202020202020202151515151515150404",'#13#10'"05041515020202151515' + +'1515151515151515151515150404",'#13#10'"050415150202151515151515151515151515' + +'151515150404",'#13#10'"050415150215151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdInterceptSimLog','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515151515151515151515151515151515150404",'#13#10'"05040303030303000300' + +'0300030003000303030303030404",'#13#10'"050415151515001500080008000800080015' + +'151515150404",'#13#10'"050415151500151500080008000800080015151515150404",' + +#13#10'"050415151500150000150015001500150000151515150404",'#13#10'"050415151' + +'500150015151515151515151500151515150404",'#13#10'"0504151515001500151414141' + +'41414141500151515150404",'#13#10'"05041515150015001515080815081515150015151' + +'5150404",'#13#10'"050415151500150015141414141414141500151515150404",'#13#10 + +'"050415151500150015150815080808151500151515150404",'#13#10'"050415151500150' + +'015141414141414141500151515150404",'#13#10'"0504151515001500151508080815151' + +'51500151515150404",'#13#10'"05041515150015001514141414141414150015151515040' + +'4",'#13#10'"050415151500150015151515151515151500151515150404",'#13#10'"0504' + +'15151500150015141414141414141500151515150404",'#13#10'"05041515151500001515' + +'1515151515151500151515150404",'#13#10'"050415151515151500000000000000000015' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdInterceptThrottler','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050707070707070707070707070' + +'70707070708050505",'#13#10'"05050407151515151515151515151515151515150704040' + +'5",'#13#10'"050415071507071507071507071507071507071507150404",'#13#10'"0504' + +'15071508070708070807070707070707081507150404",'#13#10'"05040307150708070808' + +'0708080708070815070707030404",'#13#10'"050415071508080707150708070708070708' + +'081507150404",'#13#10'"050415071515151515151515151515151515151507150404",' + +#13#10'"050415071515151515151515151507150715151507150404",'#13#10'"050415071' + +'515070715080807070808070807151507150404",'#13#10'"0504150715151507150807080' + +'70807150815151507150404",'#13#10'"05041507151507080708070807080715081515150' + +'7150404",'#13#10'"050415071515151515151515151515151515151507150404",'#13#10 + +'"050415071515150804030407150700080815151507150404",'#13#10'"050415071515150' + +'007070715150015070015151507150404",'#13#10'"0504150715151500151515150708151' + +'50808151507150404",'#13#10'"05041507151507000800081508081515080815150715040' + +'4",'#13#10'"050415071515150815150007080815150808151507150404",'#13#10'"0504' + +'15071515151515150808080815150808151507150404",'#13#10'"05041507151508081515' + +'0808070015150808151507150404",'#13#10'"050415071515150008080015150808080015' + +'151507150404",'#13#10'"050415071515151508071515151508071515151507150404",' + +#13#10'"050415071515151515151515151515151515151507150404",'#13#10'"050404071' + +'515151515151515151515151515151507040404",'#13#10'"0505040707070707070707070' + +'70707070707070707040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdLogDebug','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515151515151515151515151515151515150404",'#13#10'"05040303030303030303' + +'0303030303030303030303030404",'#13#10'"050415151515151507151515151515151515' + +'151515150404",'#13#10'"050415151515151507070715150707151515151515150404",' + +#13#10'"050415151515151507080808080708151515151515150404",'#13#10'"050415151' + +'515151507080803030808071515151515150404",'#13#10'"0504151515151515070901010' + +'10108071515151515150404",'#13#10'"05041515151515070808080909010108151515151' + +'5150404",'#13#10'"050415151507080708010109010101080807151515150404",'#13#10 + +'"050415151508070801010109010101080708151515150404",'#13#10'"050415150707070' + +'808000909090100030708071515150404",'#13#10'"0504151515151508080101010101080' + +'80807071515150404",'#13#10'"05041515151515080708000000000807080815151515040' + +'4",'#13#10'"050415151515080707150708070807151508151515150404",'#13#10'"0504' + +'15151515150707151515151515151515151515150404",'#13#10'"05041515151515151515' + +'1515151515151515151515150404",'#13#10'"050415151515151515151515151515151515' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdLogEvent','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505051215150' + +'50505050505050505",'#13#10'"05050404040404040404040412151504040404040404040' + +'5",'#13#10'"050411111111111111111111121515111111111111110404",'#13#10'"0504' + +'11111111111111111111151512111111111111110404",'#13#10'"05040303030303030303' + +'0303151512030303030303030404",'#13#10'"050411111111111111111212151512111111' + +'111111110404",'#13#10'"050411111111111111121215151515111111111111110404",' + +#13#10'"050412121111111111121515121215121212111111110404",'#13#10'"050412121' + +'212111112121512111215151515121111110404",'#13#10'"0504121412151215151512121' + +'11112151212151515110404",'#13#10'"05041215151515151215120111111215151212121' + +'2110404",'#13#10'"050412151212121212151211111111121515121311110404",'#13#10 + +'"050411111111111214121111111111121512151212110404",'#13#10'"050411111111121' + +'215121201111111121512151515121204",'#13#10'"0504111111121515151512121111111' + +'21512121215151212",'#13#10'"05041111111212121112151511111215151511121512131' + +'2",'#13#10'"050411121214120111121215111112151215111215110404",'#13#10'"0504' + +'11121515121111121415111112121215111215120404",'#13#10'"05041215121201121215' + +'1512111215121415121212151512",'#13#10'"050412121201111215151212111215121212' + +'121112121515",'#13#10'"121214120111111215121201111215111112120811121212",' + +#13#10'"121512121111111215111111111111111112151212110404",'#13#10'"121512010' + +'404041215040404040404040404151512010404",'#13#10'"0515120404040412150404040' + +'40404040404121512040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdLogFile','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515151515151515151515151515151515150404",'#13#10'"05040303030000090909' + +'0909090909090000030303030404",'#13#10'"050415150000001515151515151515150000' + +'001515150404",'#13#10'"050415150000001515151515151515150008001515150404",' + +#13#10'"050415150000001515151515151515150000001515150404",'#13#10'"050415150' + +'000001515151515151515150000001515150404",'#13#10'"0504151500000015151515151' + +'51515150000001515150404",'#13#10'"05041515000000151515151515151515000000151' + +'5150404",'#13#10'"050415150000001515151515151515150000001515150404",'#13#10 + +'"050415150000000000000000000000000000001515150404",'#13#10'"050415150000000' + +'000000000000000000000001515150404",'#13#10'"0504151500000000000807070707070' + +'70000001515150404",'#13#10'"05041515000000000008070000070707000000151515040' + +'4",'#13#10'"050415150000000000080700000707070000001515150404",'#13#10'"0504' + +'15150000000000080707070707070000001515150404",'#13#10'"05041515070000000008' + +'0808080808080000071515150404",'#13#10'"050415151515151515151515151515151515' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdLogStream','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505000000050' + +'50505050505050505",'#13#10'"05050404040404040404040008040404040404040404040' + +'5",'#13#10'"050411111111111111110306061111111111111111110404",'#13#10'"0504' + +'11111111111111111106060611111111111111110404",'#13#10'"05040303030303030303' + +'0300061206030303030303030404",'#13#10'"050411111111110711111104060606081111' + +'111111110404",'#13#10'"050411111111110800110006040604071111111111110404",' + +#13#10'"050411111111111108000604060600111111111111110404",'#13#10'"050411111' + +'111110006060606040011111111111111110404",'#13#10'"0504111111110406061206060' + +'60011111111111111110404",'#13#10'"05041111110006060606061206081111111111111' + +'1110404",'#13#10'"050411111106060406120606060300111111111111110404",'#13#10 + +'"050411110004060406060606120011111111111111110404",'#13#10'"050411110603060' + +'400061207070011001108111111110404",'#13#10'"0504111106080606060604060600031' + +'50002031111110404",'#13#10'"05041111000604061206060606060608000011111111040' + +'4",'#13#10'"050411110006060406060604061206060000111111110404",'#13#10'"0504' + +'11111104060806060606060606060606000811110404",'#13#10'"05041111110006080006' + +'0406040006040612060611110404",'#13#10'"050411111111060002060408060604000606' + +'060611110404",'#13#10'"050411111111030606020006060604000604040611110404",' + +#13#10'"050411111111110302000008040606061206061111110404",'#13#10'"050404040' + +'404040404000606060604040608040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdUDPServer','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303000000000000000303030303030' + +'30303030303030303",'#13#10'"03030015151515150004040404040404040404040404040' + +'3",'#13#10'"030400151414141500151515151515151515151515150404",'#13#10'"0304' + +'00151414141500151515151515151515151515150404",'#13#10'"03040015080808080008' + +'0808080808080808080815150404",'#13#10'"030400000000000000070707070707070707' + +'070815150404",'#13#10'"000000151507070700000007070707070707081515150404",' + +#13#10'"001515070707070707080008080808070708080808080404",'#13#10'"001508080' + +'808080808080015150807070707070707070404",'#13#10'"0000000000000000000000150' + +'80707070707070707070404",'#13#10'"03041515151515151515151508080808080808080' + +'8080404",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10 + +'"030415151515151515151515151515151515151515150404",'#13#10'"030415000015150' + +'000150000000015150000000000150404",'#13#10'"0304150000151500001500001500001' + +'50000150000150404",'#13#10'"03041500001515000015000015000015000015000015040' + +'4",'#13#10'"030415000015150000150000150000150000000000150404",'#13#10'"0304' + +'15000015150000150000150000150000151515150404",'#13#10'"03041500001515000015' + +'0000150000150000151515150404",'#13#10'"030415150000000015150000000015150000' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdCmdTCPServer','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'15151515151515151515151515151515151515150404",'#13#10'"03041515151515151515' + +'1515151515151515151515150404",'#13#10'"030415151515151515151515151515151515' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415070000000015000008150800000000000007150404",'#13#10'"030400000' + +'815080015150007150700151500000800000404",'#13#10'"0304000015151515151500001' + +'50000151500001500000404",'#13#10'"03040000151515151515000707070015150000150' + +'0000404",'#13#10'"030400001515151515150008000800151500001500000404",'#13#10 + +'"030400000815080015150015071500151500000800000404",'#13#10'"030415070000000' + +'815000000150000000000000007150404",'#13#10'"0304151515151515151515151515151' + +'51515151515150404",'#13#10'"03041515151515151515151515151515151515151515040' + +'4",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'15151515151515151515151515151515151515150404",'#13#10'"03041515151515151515' + +'1515151515151515151515150404",'#13#10'"030415151515151515151515151515151515' + +'151515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdSimpleServer','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505000000000000000505050505050' + +'50505050505050505",'#13#10'"05050015151515150004040404040404040404040404040' + +'5",'#13#10'"050400151414141500151515151515151515151515150404",'#13#10'"0504' + +'00151414141500151515151515151515151515150404",'#13#10'"05040015080808080015' + +'1515151515151515151515150404",'#13#10'"050400000000000000151515151515151515' + +'151515150404",'#13#10'"000000151507070700000015151515151515151515150404",' + +#13#10'"001515070707070707080015151515151515151515150404",'#13#10'"001508080' + +'808080808080015151515151515151515150404",'#13#10'"0000000000000000000000151' + +'51515151515151515150404",'#13#10'"05041515151515151515151515151515151515151' + +'5150404",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10 + +'"050415151515151515151515151515151515151515150404",'#13#10'"050415000000000' + +'000150000000000150000000000150404",'#13#10'"0504151515000015151500001515151' + +'50000150000150404",'#13#10'"05041515150000151515000015151515000015000015040' + +'4",'#13#10'"050415151500001515150000151515150000000000150404",'#13#10'"0504' + +'15151500001515150000151515150000151515150404",'#13#10'"05041515150000151515' + +'0000151515150000151515150404",'#13#10'"050415151500001515150000000000150000' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdTCPServer','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505000000000000000505050505050' + +'50505050505050505",'#13#10'"05050015151515150004040404040404040404040404040' + +'5",'#13#10'"050400151414141500151515151515151515151515150404",'#13#10'"0504' + +'00151414141500151515151515151515151515150404",'#13#10'"05040015080808080003' + +'0303030303030303030315150404",'#13#10'"050400000000000000111111111111111111' + +'110315150404",'#13#10'"000000151507070700000011111111111111031515150404",' + +#13#10'"001515070707070707080003030303111103030303030404",'#13#10'"001508080' + +'808080808080015150311111111111111110404",'#13#10'"0000000000000000000000150' + +'31111111111111111110404",'#13#10'"05041515151515151515151503030303030303030' + +'3030404",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10 + +'"050415151515151515151515151515151515151515150404",'#13#10'"050415000000000' + +'000150000000000150000000000150404",'#13#10'"0504151515000015151500001515151' + +'50000150000150404",'#13#10'"05041515150000151515000015151515000015000015040' + +'4",'#13#10'"050415151500001515150000151515150000000000150404",'#13#10'"0504' + +'15151500001515150000151515150000151515150404",'#13#10'"05041515150000151515' + +'0000151515150000151515150404",'#13#10'"050415151500001515150000000000150000' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdIPMCastServer','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505000000000000000505050505050' + +'50505050505050505",'#13#10'"05050015151515150004040404040404040404040404040' + +'5",'#13#10'"050400151414141500080808080808151515151515150404",'#13#10'"0504' + +'00151414141500150708070707080715151515150404",'#13#10'"05040015080808080008' + +'0707070708070707151515150404",'#13#10'"050400000000000000070708080815080708' + +'151515150404",'#13#10'"000000151507070700000015070707150815081515150404",' + +#13#10'"001515070707070707080008080707080707081515150404",'#13#10'"001508080' + +'808080808080007070707080707081515150404",'#13#10'"0000000000000000000000080' + +'70815080708081515150404",'#13#10'"05041515150807070815070808150708070708151' + +'5150404",'#13#10'"050415151508150807081500001508150815081515150404",'#13#10 + +'"050415151507070707150800000715070708071515150404",'#13#10'"050415151515080' + +'708070800000708081508151515150404",'#13#10'"0504151515151508071500000008150' + +'70815151515150404",'#13#10'"05041515151515151507000808001507151515151515040' + +'4",'#13#10'"050415151515151515080000000015151515151515150404",'#13#10'"0504' + +'15151515151515000008070007151515151515150404",'#13#10'"05041515151515150700' + +'0700080008151515151515150404",'#13#10'"050415151515151507000800080008151515' + +'151515150404",'#13#10'"050415151515151508000808150000071515151515150404",' + +#13#10'"050415151515151500080800080800071515151515150404",'#13#10'"050404040' + +'404040700070800001500080404040404040404",'#13#10'"0505040404040407000808080' + +'70008080404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdSocksInfo','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13 + +#10'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c none",'#13#10'"10 c ' + +'green",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13 + +#10'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0909090907080000000003080807' + +'09090909090909090909",'#13#10'"09090407000311111111110303030004040404040404' + +'0409",'#13#10'"090415070011111111111111110300081515151515150404",'#13#10'"0' + +'90415150003111111111111110300071515151515150404",'#13#10'"09041515000311031' + +'1030311110300151515151515150404",'#13#10'"090415150000000204020802000808151' + +'515151515150404",'#13#10'"090415150700081008100614020115151515151515150404"' + +','#13#10'"090415150700061410071008020815151515151515150404",'#13#10'"090415' + +'150700100808101408020415151515151515150404",'#13#10'"0904151507000810140810' + +'06020815151515151515150404",'#13#10'"09041515070014081006081004031515151515' + +'1515150404",'#13#10'"090415150800100810071008020815151515151515150404",'#13 + +#10'"090415150800061008101406020515151515151515150404",'#13#10'"090415150803' + +'030006081008100407151515151515150404",'#13#10'"0904151508001111000208100810' + +'08081515151515150404",'#13#10'"09041515000311110300061408061008020708151515' + +'0404",'#13#10'"090415150703111111000210081008060600000115150404",'#13#10'"0' + +'90415151500111103000806101408020303020007150404",'#13#10'"09041515151500110' + +'3000610070202031111030007150404",'#13#10'"090415151515070000020806100011111' + +'111110008150404",'#13#10'"090415151515150700061008060311111111030007150404"' + +','#13#10'"090415151515151515070800031111111111000815150404",'#13#10'"090404' + +'040404040404040407000303111101000704040404",'#13#10'"0909040404040404040404' + +'04040708000006040404040409"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdAntiFreeze','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13 + +#10'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c none",'#13#10'"10 c ' + +'green",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13 + +#10'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0909090909090909090909090909' + +'09090909090909090909",'#13#10'"09090404040404000000040404040404040404040404' + +'0409",'#13#10'"090415151515150000001511111111111115151515150404",'#13#10'"0' + +'90415151515151111111115151515151111151515150404",'#13#10'"09041515151511111' + +'1111515151515151111151515150404",'#13#10'"090415151515111111111515151511111' + +'111111515150404",'#13#10'"090415151515111111111111111111111111111515150404"' + +','#13#10'"090415151511111111111111111111111111111515150404",'#13#10'"090415' + +'151511080101010101010101030103111515150404",'#13#10'"0904151515110800000000' + +'00000000000001111515150404",'#13#10'"09041515151108000000000000000000000111' + +'1515150404",'#13#10'"090415151511080000000000000000000003111515150404",'#13 + +#10'"090415151511080000000000000000000003111515150404",'#13#10'"090415151511' + +'080808080808080808080803111515150404",'#13#10'"0904151515110808080808080808' + +'08080003111515150404",'#13#10'"09041515151108080000000808080008000311151515' + +'0404",'#13#10'"090415151511080000000000000000000003111515150404",'#13#10'"0' + +'90415151511080000000000000000000003111515150404",'#13#10'"09041515151108000' + +'0000000000000000003111515150404",'#13#10'"090415151511080000000000000000000' + +'103111515150404",'#13#10'"090415151511110808080808080808070711111515150404"' + +','#13#10'"090415151511111111111111111111111111111515150404",'#13#10'"090404' + +'040404111111111111111111111111040404040404",'#13#10'"0909040404040404040404' + +'04040404040404040404040409"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdSchedulerOfThreadDefault','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030409141409141414141414141414141414091414090404",'#13#10'"0304' + +'14091409141414141414141414141414091409140404",'#13#10'"03041414091409141414' + +'1414141414141409140914140404",'#13#10'"030409091409140914141414141414140914' + +'091409090404",'#13#10'"030414140914091409141414141414091409140914140404",' + +#13#10'"030414140009000909000000000000090900090014140404",'#13#10'"030414140' + +'007090909090707070709090909070014140404",'#13#10'"0304141400070707090909070' + +'70909090707070014140404",'#13#10'"03041414000707070709090707090907070707001' + +'4140404",'#13#10'"030414140007070707070709090707070707070014140404",'#13#10 + +'"030414140007070707070709090707070707070014140404",'#13#10'"030414140007070' + +'707090907070909070707070014140404",'#13#10'"0304141400070707090909070709090' + +'90707070014140404",'#13#10'"03041414000709090909070707070909090907001414040' + +'4",'#13#10'"030414140009000909000000000000090900090014140404",'#13#10'"0304' + +'14140914091409141414141414091409140914140404",'#13#10'"03040909140914091414' + +'1414141414140914091409090404",'#13#10'"030414140914091414141414141414141409' + +'140914140404",'#13#10'"030414091409141414141414141414141414091409140404",' + +#13#10'"030409141409141414141414141414141414091414090404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdSchedulerOfThreadPool','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030405050514141414141414141414141414140505050404",'#13#10'"0304' + +'14141405050505141414141414050505051414140404",'#13#10'"03041414141414141405' + +'0505050505141414141414140404",'#13#10'"030405050514141414141414141414141414' + +'140505050404",'#13#10'"030414141405050505141414141414050505051414140404",' + +#13#10'"030414140000000000050505050505000000000014140404",'#13#10'"030405050' + +'507070707070707070707070707070505050404",'#13#10'"0304141400050505050707070' + +'70707050505050014140404",'#13#10'"03041414000707070705050505050507070707001' + +'4140404",'#13#10'"030414140007070707070707070707070707070014140404",'#13#10 + +'"030414140007070707070707070707070707070014140404",'#13#10'"030414140007070' + +'707050505050505070707070014140404",'#13#10'"0304141400050505050707070707070' + +'50505050014140404",'#13#10'"03040505050707070707070707070707070707050505040' + +'4",'#13#10'"030414140000000000050505050505000000000014140404",'#13#10'"0304' + +'14141405050505141414141414050505051414140404",'#13#10'"03040505051414141414' + +'1414141414141414140505050404",'#13#10'"030414141414141414050505050505141414' + +'141414140404",'#13#10'"030414141405050505141414141414050505051414140404",' + +#13#10'"030405050514141414141414141414141414140505050404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIdThreadComponent','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c #808000' + +'",'#13#10'"04 c #000080",'#13#10'"05 c none",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0505050505050505050505050505050' + +'50505050505050505",'#13#10'"05050404040404040404040404040404040404040404040' + +'5",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515151515151515151515151515151515150404",'#13#10'"05041515151515151515' + +'1515151515151515151515150404",'#13#10'"050415151515151515151515151515151515' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050415151' + +'515151515151515151515151515151515150404",'#13#10'"1313131313131313131313131' + +'31313131313131313131313",'#13#10'"13131313131313131313131313131313131313131' + +'3131313",'#13#10'"131313131313131313131313131313131313131313131313",'#13#10 + +'"050415151515151515151515151515151515151515150404",'#13#10'"050415151515151' + +'515151515151515151515151515150404",'#13#10'"0504151515151515151515151515151' + +'51515151515150404",'#13#10'"05041515151515151515151515151515151515151515040' + +'4",'#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"0504' + +'15151515151515151515151515151515151515150404",'#13#10'"05041515151515151515' + +'1515151515151515151515150404",'#13#10'"050415151515151515151515151515151515' + +'151515150404",'#13#10'"050415151515151515151515151515151515151515150404",' + +#13#10'"050415151515151515151515151515151515151515150404",'#13#10'"050404040' + +'404040404040404040404040404040404040404",'#13#10'"0505040404040404040404040' + +'40404040404040404040405"'#13#10'};'#13#10 +]); +LazarusResources.Add('TIDICMPCLIENT','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"24 24 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #C0C0C0",'#13#10'"08 c #808080",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'30303030303030303",'#13#10'"03030404040404040404040404040404040404040404040' + +'3",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"0304' + +'05050505050505050505050505050515151515150404",'#13#10'"03041313131313131313' + +'1313131313130515151515150404",'#13#10'"030413131313131313131313131313051515' + +'151515150404",'#13#10'"030405050505050505050513131305151515151515150404",' + +#13#10'"030415151515151515051313130505050505050505050404",'#13#10'"030415151' + +'515151505131313131313131313131313130404",'#13#10'"0304151515151505131313131' + +'31313131313131313130404",'#13#10'"03041515151515050505050505050505050505050' + +'5050404",'#13#10'"030415151515151515151515151515151515151515150404",'#13#10 + +'"030415151515151515151515151515151515151515150404",'#13#10'"030415000015000' + +'000001500001500001500000000150404",'#13#10'"0304150000150000151515000000000' + +'01500001500150404",'#13#10'"03041500001500001515150015001500150000150015040' + +'4",'#13#10'"030415000015000015151500150015001500000000150404",'#13#10'"0304' + +'15000015000015151500150015001500001515150404",'#13#10'"03041500001500001515' + +'1500150015001500001515150404",'#13#10'"030415000015000000001500150015001500' + +'001515150404",'#13#10'"030415151515151515151515151515151515151515150404",' + +#13#10'"030415151515151515151515151515151515151515150404",'#13#10'"030404040' + +'404040404040404040404040404040404040404",'#13#10'"0303040404040404040404040' + +'40404040404040404040403"'#13#10'};'#13#10 +]); diff --git a/indy/Core/IdRegisterCore.pas b/indy/Core/IdRegisterCore.pas new file mode 100644 index 0000000..4d6d9ef --- /dev/null +++ b/indy/Core/IdRegisterCore.pas @@ -0,0 +1,668 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 2/8/2004 1:35:40 PM JPMugaas + IdSocks is now in DotNET. + + Rev 1.0 2/3/2004 12:28:06 PM JPMugaas + Kudzu wanted this renamed. + + Rev 1.27 2004.01.01 2:40:02 PM czhower + Removed test ifdef + + Rev 1.26 1/1/2004 3:32:30 PM BGooijen + Added icons for .Net + + Rev 1.25 2003.12.31 11:02:50 PM czhower + New components now registered for .net. + + Rev 1.24 2003.12.25 6:55:20 PM czhower + TCPServer + + Rev 1.23 11/22/2003 11:49:52 PM BGooijen + Icons for DotNet + + Rev 1.22 17/11/2003 16:00:22 ANeillans + Fix Delphi compile errors. + + Rev 1.21 11/8/2003 8:09:24 PM BGooijen + fix, i mixed up some stuff + + Rev 1.20 11/8/2003 7:27:10 PM BGooijen + DotNet + + Rev 1.19 2003.10.19 1:35:32 PM czhower + Moved Borland define to .inc + + Rev 1.18 2003.10.18 11:32:42 PM czhower + Changed throttler to intercept + + Rev 1.17 2003.10.17 6:18:50 PM czhower + TIdInterceptSimLog + + Rev 1.16 2003.10.14 1:26:42 PM czhower + Uupdates + Intercept support + + Rev 1.15 9/21/2003 01:10:40 AM JPMugaas + Added IdThreadCOmponent to the registration in Core. + + Rev 1.14 2003.08.19 11:06:34 PM czhower + Fixed names of scheduler units. + + Rev 1.13 8/19/2003 01:25:08 AM JPMugaas + Unnecessary junk removed. + + Rev 1.12 8/15/2003 12:02:48 AM JPMugaas + Incremented version number. + Moved some units to new IndySuperCore package in D7. + Made sure package titles are uniform in the IDE and in the .RES files. + + Rev 1.11 7/24/2003 03:22:00 AM JPMugaas + Removed some old files. + + Rev 1.10 7/18/2003 4:33:12 PM SPerry + Added TIdCmdTCPClient + + Rev 1.7 4/17/2003 05:02:26 PM JPMugaas + + Rev 1.6 4/11/2003 01:09:50 PM JPMugaas + + Rev 1.5 3/25/2003 11:12:54 PM BGooijen + TIdChainEngineStack added. + + Rev 1.4 3/25/2003 05:02:00 PM JPMugaas + TCmdTCPServer added. + + Rev 1.3 3/22/2003 10:14:54 PM BGooijen + Added TIdServerIOHandlerChain to the palette + + Rev 1.2 3/22/2003 02:20:48 PM JPMugaas + Updated registration. + + Rev 1.1 1/17/2003 04:18:44 PM JPMugaas + Now compiles with new packages. + + Rev 1.0 11/13/2002 08:41:42 AM JPMugaas +} + +unit IdRegisterCore; + +interface + +uses + Classes; + +// Procedures + + procedure Register; + +implementation + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF FMX} + Controls, + {$ENDIF} + {$IFDEF FPC} + LResources, + {$ENDIF} + IdSocks, + {$IFDEF HAS_TSelectionEditor} + {$IFDEF FPC} + PropEdits, + ComponentEditors, + {$ELSE} + DesignIntf, + DesignEditors, + {$ENDIF} + TypInfo, + {$IFDEF VCL_2010_OR_ABOVE} + Rtti, + {$ENDIF} + SysUtils, + IdGlobal, + {$ENDIF} + + IdBaseComponent, + IdComponent, + IdDsnCoreResourceStrings, + IdAntiFreeze, + IdCmdTCPClient, + IdCmdTCPServer, + IdIOHandlerStream, + {$IFNDEF DOTNET} + IdIcmpClient, + {$ENDIF} + IdInterceptSimLog, + IdInterceptThrottler, + IdIPMCastClient, + IdIPMCastServer, + IdLogDebug, + IdLogEvent, + IdLogFile, + IdLogStream, + IdSchedulerOfThread, + IdSchedulerOfThreadDefault, + IdSchedulerOfThreadPool, + IdServerIOHandlerSocket, + IdServerIOHandlerStack, + IdSimpleServer, + IdThreadComponent, + {$IFNDEF DOTNET} + IdTraceRoute, + {$ENDIF} + IdUDPClient, + IdUDPServer, + IdIOHandlerSocket, + IdIOHandlerStack, + IdIntercept, + IdTCPServer, + IdTCPClient; + +{$IFDEF DOTNET} + {$R IconsDotNet\TIdAntiFreeze.bmp} + {$R IconsDotNet\TIdCmdTCPClient.bmp} + {$R IconsDotNet\TIdCmdTCPServer.bmp} + {$R IconsDotNet\TIdConnectionIntercept.bmp} + {$R IconsDotNet\TIdICMPClient.bmp} + {$R IconsDotNet\TIdInterceptSimLog.bmp} + {$R IconsDotNet\TIdInterceptThrottler.bmp} + {$R IconsDotNet\TIdIOHandlerStack.bmp} + {$R IconsDotNet\TIdIOHandlerStream.bmp} + {$R IconsDotNet\TIdLogDebug.bmp} + {$R IconsDotNet\TIdLogEvent.bmp} + {$R IconsDotNet\TIdLogFile.bmp} + {$R IconsDotNet\TIdLogStream.bmp} + {$R IconsDotNet\TIdSchedulerOfThreadDefault.bmp} + {$R IconsDotNet\TIdSchedulerOfThreadPool.bmp} + {$R IconsDotNet\TIdServerIOHandlerStack.bmp} + {$R IconsDotNet\TIdSimpleServer.bmp} + {$R IconsDotNet\TIdTCPClient.bmp} + {$R IconsDotNet\TIdTCPServer.bmp} + {$R IconsDotNet\TIdThreadComponent.bmp} + {$R IconsDotNet\TIdUDPClient.bmp} + {$R IconsDotNet\TIdUDPServer.bmp} + {$R IconsDotNet\TIdIPMCastClient.bmp} + {$R IconsDotNet\TIdIPMCastServer.bmp} + {$R IconsDotNet\TIdSocksInfo.bmp} +{$ELSE} + {$IFNDEF FPC} + {$IFDEF BORLAND} + {$R IdCoreRegister.dcr} + {$ELSE} + {$R IdCoreRegisterCool.dcr} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF HAS_TSelectionEditor} + +// TIdComponentSelectionEditor is called at design-time when saving/compiling a +// project. It enumerates the data types of all parameters and return values of +// every event handler assigned to any Indy component, extracting the unit names +// of those data types and passing them to the IDE so it can insert them into +// 'uses' clauses as needed. + +procedure SendUnitNameToProc(const AUnitName: String; Proc: TGetStrProc); +begin + // Do not return the 'System' unit, otherwise it will + // cause an "Identifier redeclared" compiler error! + if (AUnitName <> '') and (not TextIsSame(AUnitName, 'System')) then begin {do not localize} + Proc(AUnitName); + end; +end; + +{$IFDEF VCL_XE2_OR_ABOVE} + +// in Delphi XE2 and later, TRttiInvokableType is used to enumerate parameters +// and return values, and TRttiType reports fully qualified type names, so +// finding a given type's unit name is very easy... + +function GetUnitNameForType(const AType: TRttiType): String; +begin + // TRttiType.UnitName returns the unit that declares TRttiType itself + // (System.Rtti), so parse the TRttiType.QualifiedName value instead... + if AType <> nil then begin + Result := AType.QualifiedName; + SetLength(Result, Length(Result) - Length(AType.Name) - 1); + end else begin + Result := ''; + end; +end; + +{$ELSE} + +// in Delphi prior to XE2, as well as in FreePascal, TRttiInvokableType is not +// available, so we have to use TypInfo RTTI to enumerating parameters and +// return values, but only certain versions implement rich enough RTTI to allow +// that. Let's try to pull out what we can... + +{$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE HAS_tkEnumeration_UnitName} + {$DEFINE HAS_tkMethod_ParamTypeInfo} +{$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE HAS_tkEnumeration_UnitName} + {$ENDIF} + {$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE HAS_tkMethod_ParamTypeInfo} + {$ENDIF} +{$ENDIF} + +procedure SkipShortString(var P: PByte); +begin + Inc(P, 1 + Integer(P^)); +end; + +function ReadShortString(var P: PByte): String; +begin + {$IFDEF VCL_2009_OR_ABOVE} + Result := UTF8ToString(PShortString(P)^); + {$ELSE} + Result := PShortString(P)^; + {$ENDIF} + SkipShortString(P); +end; + +{$IFDEF FPC_2_6_0_OR_ABOVE} +function NextShortString(PS: PShortString): PShortString; +begin + Result := PShortString(Pointer(PS)+PByte(PS)^+1); +end; +{$ENDIF} + +function GetUnitNameFromTypeName(const ATypeName: String): String; +var + K: Integer; +begin + // check if the type is qualified + K := LastDelimiter('.', ATypeName); + if K <> 0 then begin + Result := Copy(ATypeName, 1, K-1); + end else begin + // TODO: enumerate package units and find the typename... + Result := ''; + end; +end; + +function GetUnitNameFromTypeInfo(const ATypeInfo: PPTypeInfo): String; +var + LTypeData: PTypeData; + {$IFDEF HAS_tkEnumeration_UnitName} + {$IFDEF FPC} + PS, PSLast: PShortString; + {$ELSE} + LBaseTypeData: PTypeData; + Value: Integer; + P: PByte; + {$ENDIF} + {$ENDIF} +begin + Result := ''; + if ATypeInfo = nil then begin + Exit; + end; + if ATypeInfo^ = nil then begin + Exit; + end; + LTypeData := GetTypeData(ATypeInfo^); + case ATypeInfo^.Kind of + {$IFDEF HAS_tkEnumeration_UnitName} + tkEnumeration: begin + {$IFDEF FPC} + // the unit name iss the last string in the name list + PS := @(LTypeData^.NameList); + PSLast := nil; + while PByte(PS)^ <> 0 do begin + PSLast := PS; + PS := NextShortString(PS); + end; + if PSLast <> nil then begin + Result := PSLast^; + end; + {$ELSE} + // the unit name follows after the name list + LBaseTypeData := GetTypeData(LTypeData^.BaseType^); + P := PByte(@(LBaseTypeData^.NameList)); + // LongBool/WordBool/ByteBool have MinValue < 0 and arbitrary + // content in Value; Boolean has Value in [0, 1] } + if (ATypeInfo^ = System.TypeInfo(Boolean)) or (LBaseTypeData^.MinValue < 0) then + begin + for Value := 0 to 1 do begin + SkipShortString(P); + end; + end else + begin + for Value := LBaseTypeData^.MinValue to LBaseTypeData^.MaxValue do begin + SkipShortString(P); + end; + end; + Result := ReadShortString(P); + {$ENDIF} + end; + {$ENDIF} + tkSet: begin + Result := GetUnitNameFromTypeInfo(LTypeData^.CompType); + end; + {$IFDEF VCL_5_OR_ABOVE} + tkClass: begin + {$IFDEF VCL_2009_OR_ABOVE} + Result := UTF8ToString(LTypeData^.UnitName); + {$ELSE} + Result := LTypeData^.UnitName; + {$ENDIF} + end; + {$ENDIF} + {$IFDEF FPC_2_6_0_OR_ABOVE} + tkHelper: begin + Result := LTypeData^.HelperUnit; + end; + {$ENDIF} + {$IFDEF VCL_5_OR_ABOVE} + tkInterface: begin + {$IFDEF VCL_2009_OR_ABOVE} + Result := UTF8ToString(LTypeData^.IntfUnit); + {$ELSE} + Result := LTypeData^.IntfUnit; + {$ENDIF} + end; + {$ENDIF} + {$IFDEF FPC_2_2_2_OR_ABOVE} // TODO: when was tkInterfaceRaw added? + tkInterfaceRaw: begin + Result := LTypeData^.RawIntfUnit; + end; + {$ENDIF} + {$IFDEF VCL_6_OR_ABOVE} + tkDynArray: begin + {$IFDEF VCL_2009_OR_ABOVE} + Result := UTF8ToString(LTypeData^.DynUnitName); + {$ELSE} + Result := LTypeData^.DynUnitName; + {$ENDIF} + if Result = '' then begin + Result := GetUnitNameFromTypeInfo(LTypeData^.elType2); + end; + end; + {$ENDIF} + end; +end; + +procedure GetUnitNamesForMethodType(const ATypeInfo: PTypeInfo; Proc: TGetStrProc); +type + PPPTypeInfo = ^PPTypeInfo; +var + LTypeData: PTypeData; + LTypeDataPtr: PByte; + K: Integer; + UnitName: string; +begin + if ATypeInfo = nil then begin + Exit; + end; + LTypeData := GetTypeData(ATypeInfo); + LTypeDataPtr := PByte(@(LTypeData^.ParamList)); + + if LTypeData^.ParamCount > 0 then + begin + for K := 0 to LTypeData^.ParamCount-1 do + begin + Inc(LTypeDataPtr, SizeOf(TParamFlags)); + SkipShortString(LTypeDataPtr); + {$IFDEF HAS_tkMethod_ParamTypeInfo} + // handled further below... + SkipShortString(LTypeDataPtr); + {$ELSE} + UnitName := GetUnitNameFromTypeName(ReadShortString(LTypeDataPtr)); + SendUnitNameToProc(UnitName, Proc); + {$ENDIF} + end; + end; + + if LTypeData^.MethodKind = mkFunction then + begin + {$IFDEF HAS_tkMethod_ParamTypeInfo} + SkipShortString(LTypeDataPtr); + UnitName := GetUnitNameFromTypeInfo(PPPTypeInfo(LTypeDataPtr)^); + Inc(LTypeDataPtr, SizeOf(PPTypeInfo)); + {$ELSE} + UnitName := GetUnitNameFromTypeName(ReadShortString(LTypeDataPtr)); + {$ENDIF} + SendUnitNameToProc(UnitName, Proc); + end; + + {$IFDEF HAS_tkMethod_ParamTypeInfo} + if LTypeData^.ParamCount > 0 then + begin + Inc(LTypeDataPtr, SizeOf(TCallConv)); + for K := 0 to LTypeData^.ParamCount-1 do + begin + UnitName := GetUnitNameFromTypeInfo(PPPTypeInfo(LTypeDataPtr)^); + SendUnitNameToProc(UnitName, Proc); + Inc(LTypeDataPtr, SizeOf(PPTypeInfo)); + end; + end; + {$ENDIF} +end; + +{$ENDIF} + +type + TIdBaseComponentSelectionEditor = class(TSelectionEditor) + public + procedure RequiresUnits(Proc: TGetStrProc); override; + end; + +procedure TIdBaseComponentSelectionEditor.RequiresUnits(Proc: TGetStrProc); +var + Comp: TIdBaseComponent; + I: Integer; + {$IFDEF VCL_2010_OR_ABOVE} + Ctx: TRttiContext; + PropInfo: TRttiProperty; + PropValue: TValue; + {$IFDEF VCL_XE2_OR_ABOVE} + PropType: TRttiMethodType; + Param: TRttiParameter; + {$ENDIF} + {$ELSE} + PropList: PPropList; + PropCount: Integer; + PropInfo: PPropInfo; + J: Integer; + {$ENDIF} +begin + inherited RequiresUnits(Proc); + if (Designer = nil) or (Designer.Root = nil) then Exit; + + for I := 0 to Designer.Root.ComponentCount - 1 do + begin + if Designer.Root.Components[i] is TIdBaseComponent then + begin + Comp := TIdBaseComponent(Designer.Root.Components[i]); + + {$IFDEF VCL_2010_OR_ABOVE} + + Ctx := TRttiContext.Create; + for PropInfo in Ctx.GetType(Comp.ClassType).GetProperties do + begin + // only interested in *assigned* event handlers + + // NOTE: Delphi 2010 has a problem with checking the TValue.IsEmpty + // property inlined like below. It causes a "F2084 Internal Error C13394" + // compiler error. So splitting up the comparison to use a local TValue + // variable to work around that... + { + if (PropInfo.PropertyType.TypeKind = tkMethod) and + (not PropInfo.GetValue(Comp).IsEmpty) then + } + if PropInfo.PropertyType.TypeKind = tkMethod then + begin + PropValue := PropInfo.GetValue(Comp); + if not PropValue.IsEmpty then + begin + // although the System.Rtti unit was introduced in Delphi 2010, + // the TRttiInvokableType class was not added to it until XE2 + {$IFDEF VCL_XE2_OR_ABOVE} + PropType := PropInfo.PropertyType as TRttiMethodType; + for Param in PropType.GetParameters do begin + SendUnitNameToProc(GetUnitNameForType(Param.ParamType), Proc); + end; + SendUnitNameToProc(GetUnitNameForType(PropType.ReturnType), Proc); + {$ELSE} + // use the System.TypInfo unit to access the parameters and return type + GetUnitNamesForMethodType(PropInfo.PropertyType.Handle, Proc); + {$ENDIF} + end; + end; + end; + + {$ELSE} + + PropCount := GetPropList(Comp, PropList); + if PropCount > 0 then + begin + try + for J := 0 to PropCount-1 do + begin + PropInfo := PropList^[J]; + // only interested in *assigned* event handlers + if (PropInfo^.PropType^.Kind = tkMethod) and + (GetMethodProp(Comp, PropInfo).Code <> nil) then + begin + GetUnitNamesForMethodType(PropInfo^.PropType^, Proc); + end; + end; + finally + FreeMem(PropList); + end; + end; + + {$ENDIF} + end; + end; +end; +{$ENDIF} + +procedure Register; +begin + {$IFNDEF FPC} + RegisterComponents(RSRegIndyClients, [ + TIdTCPClient + ,TIdUDPClient + ,TIdCmdTCPClient + ,TIdIPMCastClient + {$IFNDEF DOTNET} + ,TIdIcmpClient + ,TIdTraceRoute + {$ENDIF} + ]); + RegisterComponents(RSRegIndyServers, [ + TIdUDPServer, + TIdCmdTCPServer, + TIdSimpleServer, + TIdTCPServer, + TIdIPMCastServer + ]); + RegisterComponents(RSRegIndyIOHandlers,[ + TIdIOHandlerStack + ,TIdIOHandlerStream + ,TIdServerIOHandlerStack + ]); + RegisterComponents(RSRegIndyIntercepts, [ + TIdConnectionIntercept + ,TIdInterceptSimLog + ,TIdInterceptThrottler + ,TIdLogDebug + ,TIdLogEvent + ,TIdLogFile + ,TIdLogStream + ]); + + {$IFDEF FMX} + // RLebeau 8/1/2011 - FireMonkey has problems resolving references to + // TIdAntiFreeze correctly because it is implemented in a design-time + // package and not a run-time package. Until we can fix that properly, + // we'll group TIdAntiFreeze with TControl so the IDE can filter out + // TIdAntiFreeze from appearing at design-time in FireMoney projects. + // Users will have to instantiate TIdAntiFreeze in code. This does not + // affect VCL projects. + GroupDescendentsWith(TIdAntiFreeze, TControl); + {$ENDIF} + + RegisterComponents(RSRegIndyMisc, [ + TIdSocksInfo, + TIdAntiFreeze, + TIdSchedulerOfThreadDefault, + TIdSchedulerOfThreadPool, + TIdThreadComponent + ]); + {$ELSE} + //This is a tempoary workaround for components not fitting on the palette + //in Lazarus. Unlike Delphi, Lazarus still does not have the ability to + //scroll through a palette page. + RegisterComponents(RSRegIndyClients+CoreSuffix, [ + TIdTCPClient + ,TIdUDPClient + ,TIdCmdTCPClient + ,TIdIPMCastClient + {$IFNDEF DOTNET} + ,TIdIcmpClient + ,TIdTraceRoute + {$ENDIF} + ]); + RegisterComponents(RSRegIndyServers+CoreSuffix, [ + TIdUDPServer, + TIdCmdTCPServer, + TIdSimpleServer, + TIdTCPServer, + TIdIPMCastServer + ]); + RegisterComponents(RSRegIndyIOHandlers+CoreSuffix,[ + TIdIOHandlerStack + ,TIdIOHandlerStream + ,TIdServerIOHandlerStack + ]); + RegisterComponents(RSRegIndyIntercepts+CoreSuffix, [ + TIdConnectionIntercept + ,TIdInterceptSimLog + ,TIdInterceptThrottler + ,TIdLogDebug + ,TIdLogEvent + ,TIdLogFile + ,TIdLogStream + ]); + RegisterComponents(RSRegIndyMisc+CoreSuffix, [ + TIdSocksInfo, + TIdAntiFreeze, + TIdSchedulerOfThreadDefault, + TIdSchedulerOfThreadPool, + TIdThreadComponent + ]); + {$ENDIF} + + {$IFDEF HAS_TSelectionEditor} + RegisterSelectionEditor(TIdBaseComponent, TIdBaseComponentSelectionEditor); + {$ENDIF} +end; + +{$IFDEF FPC} +initialization +{$i IdRegisterCore.lrs} +{$ENDIF} +end. diff --git a/indy/Core/IdReply.pas b/indy/Core/IdReply.pas new file mode 100644 index 0000000..6ec2a68 --- /dev/null +++ b/indy/Core/IdReply.pas @@ -0,0 +1,409 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.27 2/3/05 12:16:46 AM RLebeau + Bug fix for UpdateText() + + Rev 1.25 1/15/2005 6:02:02 PM JPMugaas + These should compile again. + + Rev 1.24 1/15/05 2:03:20 PM RLebeau + Added AIgnore parameter to TIdReplies.Find() + + Updated TIdReply.SetNumericCode() to call SetCode() rather than assigning the + FCode member directly. + + Updated TIdReply.SetCode() to call Clear() before assigning the FCode member. + + Updated TIdReplies.UpdateText() to ignore the TIdReply that was passed in + when looking for a TIdReply to extract Text from. + + Rev 1.23 12/29/04 1:36:44 PM RLebeau + Bug fix for when descendant constructors are called twice during creation + + Rev 1.22 10/26/2004 8:43:00 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.21 6/11/2004 8:48:24 AM DSiders + Added "Do not Localize" comments. + + Rev 1.20 2004.03.01 7:10:34 PM czhower + Change for .net compat + + Rev 1.19 2004.03.01 5:12:34 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.18 2004.02.29 8:16:54 PM czhower + Bug fix to fix AV at design time when adding reply texts to CmdTCPServer. + + Rev 1.17 2004.02.03 4:17:10 PM czhower + For unit name changes. + + Rev 1.16 2004.01.29 12:02:32 AM czhower + .Net constructor problem fix. + + Rev 1.15 1/3/2004 8:06:20 PM JPMugaas + Bug fix: Sometimes, replies will appear twice due to the way functionality + was enherited. + + Rev 1.14 1/1/2004 9:33:24 PM BGooijen + the abstract class TIdReply was created sometimes, fixed that + + Rev 1.13 2003.10.18 9:33:28 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.12 10/15/2003 7:49:38 PM DSiders + Added IdResourceStringsCore to implementation uses clause. + + Rev 1.11 10/15/2003 7:46:42 PM DSiders + Added formatted resource string for the exception raised in + TIdReply.SetCode. + + Rev 1.10 2003.09.06 1:30:30 PM czhower + Removed abstract modifier from a class method so that C++ Builder can compile + again. + + Rev 1.9 2003.06.05 10:08:50 AM czhower + Extended reply mechanisms to the exception handling. Only base and RFC + completed, handing off to J Peter. + + Rev 1.8 2003.05.30 10:25:56 PM czhower + Implemented IsEndMarker + + Rev 1.7 2003.05.30 10:06:08 PM czhower + Changed code property mechanisms. + + Rev 1.6 5/26/2003 04:29:56 PM JPMugaas + Removed GenerateReply and ParseReply. Those are now obsolete duplicate + functions in the new design. + + Rev 1.5 5/26/2003 12:19:54 PM JPMugaas + + Rev 1.4 2003.05.26 11:38:18 AM czhower + + Rev 1.3 2003.05.25 10:23:44 AM czhower + + Rev 1.2 5/20/2003 12:43:46 AM BGooijen + changeable reply types + + Rev 1.1 5/19/2003 05:54:58 PM JPMugaas + + Rev 1.0 5/19/2003 12:26:16 PM JPMugaas + Base class for reply format objects. +} + +unit IdReply; + +interface + +{$I IdCompilerDefines.inc} +//we need to put this in Delphi mode to work + +uses + Classes, + IdException; + +type + TIdReplies = class; + //TODO: a streamed write only property will be registered to convert old DFMs + // into the new one for old TextCode and to ignore NumericCode which has been + // removed + TIdReply = class(TCollectionItem) + protected + FCode: string; + FFormattedReply: TStrings; + FReplyTexts: TIdReplies; + FText: TStrings; + // + procedure AssignTo(ADest: TPersistent); override; + procedure CommonInit; + function GetFormattedReplyStrings: TStrings; virtual; + function CheckIfCodeIsValid(const ACode: string): Boolean; virtual; + function GetDisplayName: string; override; + function GetFormattedReply: TStrings; virtual; + function GetNumericCode: Integer; + procedure SetCode(const AValue: string); + procedure SetFormattedReply(const AValue: TStrings); virtual; abstract; + procedure SetText(const AValue: TStrings); + procedure SetNumericCode(const AValue: Integer); + public + procedure Clear; virtual; + //Temp workaround for compiler bug + constructor Create(ACollection: TCollection); override; + constructor CreateWithReplyTexts(ACollection: TCollection; AReplyTexts: TIdReplies); virtual; + // Both creates are necessary. This base one is called by the collection editor at design time + // constructor Create(ACollection: TCollection); overload; override; + // constructor Create(ACollection: TCollection; AReplyTexts: TIdReplies); reintroduce; overload; virtual; + destructor Destroy; override; + // Is not abstract because C++ cannot compile abstract class methods + class function IsEndMarker(const ALine: string): Boolean; virtual; + procedure RaiseReplyError; virtual; abstract; + function ReplyExists: Boolean; virtual; + procedure SetReply(const ACode: Integer; const AText: string); overload; virtual; + procedure SetReply(const ACode: string; const AText: string); overload; virtual; + procedure UpdateText; + // + property FormattedReply: TStrings read GetFormattedReply write SetFormattedReply; + property NumericCode: Integer read GetNumericCode write SetNumericCode; + published + //warning: setting Code has a side-effect of calling Clear; + property Code: string read FCode write SetCode; + property Text: TStrings read FText write SetText; + end; + + TIdReplyClass = class of TIdReply; + + TIdReplies = class(TOwnedCollection) + protected + function GetItem(Index: Integer): TIdReply; + procedure SetItem(Index: Integer; const Value: TIdReply); + public + function Add: TIdReply; overload; + function Add(const ACode: Integer; const AText: string): TIdReply; overload; + function Add(const ACode, AText: string): TIdReply; overload; + constructor Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); reintroduce; virtual; + function Find(const ACode: string; AIgnore: TIdReply = nil): TIdReply; virtual; + procedure UpdateText(AReply: TIdReply); virtual; + // + property Items[Index: Integer]: TIdReply read GetItem write SetItem; default; + end; + + TIdRepliesClass = class of TIdReplies; + EIdReplyError = class(EIdException); + +implementation + +uses + IdGlobal, IdResourceStringsCore, SysUtils; + +{ TIdReply } + +procedure TIdReply.AssignTo(ADest: TPersistent); +var + LR : TIdReply; +begin + if ADest is TIdReply then begin + LR := TIdReply(ADest); + //set code first as it possibly clears the reply + LR.Code := Code; + LR.Text.Assign(Text); + end else begin + inherited AssignTo(ADest); + end; +end; + +procedure TIdReply.Clear; +begin + FText.Clear; + FCode := ''; +end; + +constructor TIdReply.CreateWithReplyTexts(ACollection: TCollection; AReplyTexts: TIdReplies); +begin + inherited Create(ACollection); + FReplyTexts := AReplyTexts; + CommonInit; +end; + +constructor TIdReply.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + CommonInit; +end; + +destructor TIdReply.Destroy; +begin + FreeAndNil(FText); + FreeAndNil(FFormattedReply); + inherited Destroy; +end; + +procedure TIdReply.CommonInit; +begin + FFormattedReply := TStringList.Create; + FText := TStringList.Create; +end; + +function TIdReply.GetDisplayName: string; +begin + if Text.Count > 0 then begin + Result := Code + ' ' + Text[0]; + end else begin + Result := Code; + end; +end; + +function TIdReply.ReplyExists: Boolean; +begin + Result := Code <> ''; +end; + +procedure TIdReply.SetNumericCode(const AValue: Integer); +begin + Code := IntToStr(AValue); +end; + +procedure TIdReply.SetText(const AValue: TStrings); +begin + FText.Assign(AValue); +end; + +procedure TIdReply.SetReply(const ACode: Integer; const AText: string); +begin + SetReply(IntToStr(ACode), AText); +end; + +function TIdReply.GetNumericCode: Integer; +begin + Result := IndyStrToInt(Code, 0); +end; + +procedure TIdReply.SetCode(const AValue: string); +var + LMatchedReply: TIdReply; +begin + if FCode <> AValue then begin + if not CheckIfCodeIsValid(AValue) then begin + raise EIdException.CreateFmt(RSReplyInvalidCode, [AValue]); + end; + // Only check for duplicates if we are in a collection. NormalReply etc are not in collections + // Also dont check FReplyTexts, as non members can be duplicates of members + if Collection <> nil then begin + LMatchedReply := TIdReplies(Collection).Find(AValue); + if Assigned(LMatchedReply) then begin + raise EIdException.CreateFmt(RSReplyCodeAlreadyExists, [AValue]); + end; + end; + Clear; + FCode := AValue; + end; +end; + +procedure TIdReply.SetReply(const ACode, AText: string); +begin + Code := ACode; + FText.Text := AText; +end; + +function TIdReply.CheckIfCodeIsValid(const ACode: string): Boolean; +begin + Result := True; +end; + +class function TIdReply.IsEndMarker(const ALine: string): Boolean; +begin + Result := False; +end; + +function TIdReply.GetFormattedReply: TStrings; +begin + // Overrides must call GetFormattedReplyStrings instead. This is just a base implementation + // This is done this way because otherwise double generations can occur if more than one + // ancestor overrides. Example: Reply--> RFC --> FTP. Calling inherited would cause both + // FTP and RFC to generate. + Result := GetFormattedReplyStrings; +end; + +function TIdReply.GetFormattedReplyStrings: TStrings; +begin + FFormattedReply.Clear; + Result := FFormattedReply; +end; + +procedure TIdReply.UpdateText; +begin + if FReplyTexts <> nil then begin + FReplyTexts.UpdateText(Self); + end; +end; + +{ TIdReplies } + +function TIdReplies.Add: TIdReply; +begin + Result := TIdReply(inherited Add); +end; + +function TIdReplies.Add(const ACode: Integer; const AText: string): TIdReply; +begin + Result := Add(IntToStr(ACode), AText); +end; + +function TIdReplies.Add(const ACode, AText: string): TIdReply; +begin + Result := Add; + try + Result.SetReply(ACode, AText); + except + FreeAndNil(Result); + raise; + end; +end; + +constructor TIdReplies.Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); +begin + inherited Create(AOwner, AReplyClass); +end; + +function TIdReplies.Find(const ACode: string; AIgnore: TIdReply = nil): TIdReply; +var + i: Integer; +begin + Result := nil; + // Never return match on '' + if ACode <> '' then begin + for i := 0 to Count - 1 do begin + if Items[i].Code = ACode then begin + if not (Items[i] = AIgnore) then begin + Result := Items[i]; + Exit; + end; + end; + end; + end; +end; + +function TIdReplies.GetItem(Index: Integer): TIdReply; +begin + Result := TIdReply(inherited Items[Index]); +end; + +procedure TIdReplies.SetItem(Index: Integer; const Value: TIdReply); +begin + inherited SetItem(Index, Value); +end; + +procedure TIdReplies.UpdateText(AReply: TIdReply); +var + LReply: TIdReply; +begin + // If text is blank, get it from the ReplyTexts + if AReply.Text.Count = 0 then begin + // RLebeau - ignore AReply, it doesn't have any text + // to assign, or else the code wouldn't be this far + LReply := Find(AReply.Code, AReply); + if LReply <> nil then begin + AReply.Text.Assign(LReply.Text); + end; + end; +end; + +end. diff --git a/indy/Core/IdReplyRFC.pas b/indy/Core/IdReplyRFC.pas new file mode 100644 index 0000000..baf47dd --- /dev/null +++ b/indy/Core/IdReplyRFC.pas @@ -0,0 +1,312 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.29 1/15/05 2:28:28 PM RLebeau + Added local variables to TIdReplyRFC.GetFormattedReply() to reduce the number + of repeated string operations that were being performed. + + Updated TIdRepliesRFC.UpdateText() to ignore the TIdReply that was passed in + when looking for a TIdReply to extract Text from. + + Rev 1.28 10/26/2004 8:43:00 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.27 6/11/2004 8:48:28 AM DSiders + Added "Do not Localize" comments. + + Rev 1.26 18/05/2004 23:17:18 CCostelloe + Bug fix + + Rev 1.25 5/18/04 2:39:02 PM RLebeau + Added second constructor to TIdRepliesRFC + + Rev 1.24 5/17/04 9:50:08 AM RLebeau + Changed TIdRepliesRFC constructor to use 'reintroduce' instead + + Rev 1.23 5/16/04 5:12:04 PM RLebeau + Added construvtor to TIdRepliesRFC class + + Rev 1.22 2004.03.01 5:12:36 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.21 2004.02.29 8:17:20 PM czhower + Minor cosmetic changes to code. + + Rev 1.20 2004.02.03 4:16:50 PM czhower + For unit name changes. + + Rev 1.19 1/3/2004 8:06:18 PM JPMugaas + Bug fix: Sometimes, replies will appear twice due to the way functionality + was enherited. + + Rev 1.18 2003.10.18 9:33:28 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.17 9/20/2003 10:01:04 AM JPMugaas + Minor change. WIll now accept all 3 digit numbers (not just ones below 600). + The reason is that developers may want something in 600-999 range. RFC 2228 + defines a 6xx reply range for protected replies. + + Rev 1.16 2003.09.20 10:33:14 AM czhower + Bug fix to allow clearing code field (Return to default value) + + Rev 1.15 2003.06.05 10:08:52 AM czhower + Extended reply mechanisms to the exception handling. Only base and RFC + completed, handing off to J Peter. + + Rev 1.14 6/3/2003 04:09:30 PM JPMugaas + class function TIdReplyRFC.IsEndMarker(const ALine: string): Boolean had the + wrong parameters causing FTP to freeze. It probably effected other stuff. + + Rev 1.13 5/30/2003 8:37:42 PM BGooijen + Changed virtual to override + + Rev 1.12 2003.05.30 10:25:58 PM czhower + Implemented IsEndMarker + + Rev 1.11 2003.05.30 10:06:08 PM czhower + Changed code property mechanisms. + + Rev 1.10 2003.05.26 10:48:12 PM czhower + 1) Removed deprecated code. + 2) Removed POP3 bastardizations as they are now in IdReplyPOP3. + + Rev 1.9 5/26/2003 12:19:52 PM JPMugaas + + Rev 1.8 2003.05.26 11:38:20 AM czhower + + Rev 1.7 5/25/2003 03:16:54 AM JPMugaas + + Rev 1.6 2003.05.25 10:23:46 AM czhower + + Rev 1.5 5/21/2003 08:43:38 PM JPMugaas + Overridable hook for the SMTP Reply object. + + Rev 1.4 5/20/2003 12:43:48 AM BGooijen + changeable reply types + + Rev 1.3 5/19/2003 12:26:50 PM JPMugaas + Now uses base class. + + Rev 1.2 11/05/2003 23:29:04 CCostelloe + IMAP-specific code moved up to TIdIMAP4.pas + + Rev 1.1 11/14/2002 02:51:54 PM JPMugaas + Added FormatType property. If it is rfIndentMidLines, it will accept + properly parse reply lines that begin with a space. Setting this to + rfIndentMidLines will also cause the reply object to generate lines that + start with a space if the Text.Line starts with a space. This should + accommodate the FTP MLSD and FEAT commands on both the client and server. + + Rev 1.0 11/13/2002 08:45:50 AM JPMugaas +} + +unit IdReplyRFC; + +interface +{$I IdCompilerDefines.inc} +uses + Classes, + IdReply; + +type + TIdReplyRFC = class(TIdReply) + protected + procedure AssignTo(ADest: TPersistent); override; + function CheckIfCodeIsValid(const ACode: string): Boolean; override; + function GetFormattedReply: TStrings; override; + procedure SetFormattedReply(const AValue: TStrings); override; + public + class function IsEndMarker(const ALine: string): Boolean; override; + procedure RaiseReplyError; override; + function ReplyExists: Boolean; override; + end; + + TIdRepliesRFC = class(TIdReplies) + public + constructor Create(AOwner: TPersistent); reintroduce; overload; virtual; + constructor Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); overload; override; + procedure UpdateText(AReply: TIdReply); override; + end; + + // This exception is for protocol errors such as 404 HTTP error and also + // SendCmd / GetResponse + EIdReplyRFCError = class(EIdReplyError) + protected + FErrorCode: Integer; + public + // Params must be in this order to avoid conflict with CreateHelp + // constructor in CBuilder as CB does not differentiate constructors + // by name as Delphi does + constructor CreateError(const AErrorCode: Integer; + const AReplyMessage: string); reintroduce; virtual; + // + property ErrorCode: Integer read FErrorCode; + end; + +implementation + +uses + IdGlobal, + SysUtils; + +{ TIdReplyRFC } + +procedure TIdReplyRFC.AssignTo(ADest: TPersistent); +var + LR: TIdReplyRFC; +begin + if ADest is TIdReplyRFC then begin + LR := TIdReplyRFC(ADest); + //set code first as it possibly clears the reply + LR.NumericCode := NumericCode; + LR.Text.Assign(Text); + end else begin + inherited AssignTo(ADest); + end; +end; + +function TIdReplyRFC.CheckIfCodeIsValid(const ACode: string): Boolean; +var + LCode: Integer; +begin + LCode := IndyStrToInt(ACode, 0); + {Replaced 600 with 999 because some developers may want 6xx, 7xx, and 8xx reply + codes for their protocols. It also turns out that RFC 2228 defines 6xx reply codes. + + From RFC 2228 + + A new class of reply types (6yz) is also introduced for protected + replies. + } + Result := ((LCode >= 100) and (LCode < 1000)) or (Trim(ACode) = ''); +end; + +function TIdReplyRFC.GetFormattedReply: TStrings; +var + I, LCode: Integer; + LCodeStr: String; +begin + Result := GetFormattedReplyStrings; + LCode := NumericCode; + if LCode > 0 then begin + LCodeStr := IntToStr(LCode); + if Text.Count > 0 then begin + for I := 0 to Text.Count - 1 do begin + if I < Text.Count - 1 then begin + Result.Add(LCodeStr + '-' + Text[I]); + end else begin + Result.Add(LCodeStr + ' ' + Text[I]); + end; + end; + end else begin + Result.Add(LCodeStr); + end; + end else if FText.Count > 0 then begin + Result.AddStrings(FText); + end; +end; + +class function TIdReplyRFC.IsEndMarker(const ALine: string): Boolean; +begin + if Length(ALine) >= 4 then begin + Result := ALine[4] = ' '; + end else begin + Result := True; + end; +end; + +procedure TIdReplyRFC.RaiseReplyError; +begin + raise EIdReplyRFCError.CreateError(NumericCode, Text.Text); +end; + +function TIdReplyRFC.ReplyExists: Boolean; +begin + Result := (NumericCode > 0) or (FText.Count > 0); +end; + +procedure TIdReplyRFC.SetFormattedReply(const AValue: TStrings); +// Just parse and put in items, no need to store after parse +var + i: Integer; + s: string; +begin + Clear; + if AValue.Count > 0 then begin + s := Trim(Copy(AValue[0], 1, 3)); + Code := s; + for i := 0 to AValue.Count - 1 do begin + Text.Add(Copy(AValue[i], 5, MaxInt)); + end; + end; +end; + +{ EIdReplyRFCError } + +constructor EIdReplyRFCError.CreateError(const AErrorCode: Integer; + const AReplyMessage: string); +begin + inherited Create(AReplyMessage); + FErrorCode := AErrorCode; +end; + +{ TIdReplies } + +constructor TIdRepliesRFC.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdReplyRFC); +end; + +constructor TIdRepliesRFC.Create(AOwner: TPersistent; const AReplyClass: TIdReplyClass); +begin + inherited Create(AOwner, AReplyClass); +end; + +procedure TIdRepliesRFC.UpdateText(AReply: TIdReply); +var + LGenericNumCode: Integer; + LReply: TIdReply; +begin + inherited UpdateText(AReply); + // If text is still blank after inherited see if we can find a generic version + if AReply.Text.Count = 0 then begin + LGenericNumCode := (AReply.NumericCode div 100) * 100; + // RLebeau - in cases where the AReply.Code is the same as the + // generic code, ignore the AReply as it doesn't have any text + // to assign, or else the code wouldn't be this far + LReply := Find(IntToStr(LGenericNumCode), AReply); + if LReply = nil then begin + // If no generic was found, then use defaults. + case LGenericNumCode of + 100: AReply.Text.Text := 'Information'; {do not localize} + 200: AReply.Text.Text := 'Ok'; {do not localize} + 300: AReply.Text.Text := 'Temporary Error'; {do not localize} + 400: AReply.Text.Text := 'Permanent Error'; {do not localize} + 500: AReply.Text.Text := 'Unknown Internal Error'; {do not localize} + end; + end else begin + AReply.Text.Assign(LReply.Text); + end; + end; +end; + +end. diff --git a/indy/Core/IdResourceStringsCore.pas b/indy/Core/IdResourceStringsCore.pas new file mode 100644 index 0000000..26bc87c --- /dev/null +++ b/indy/Core/IdResourceStringsCore.pas @@ -0,0 +1,288 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 12/2/2004 9:26:44 PM JPMugaas + Bug fix. + + Rev 1.4 11/11/2004 10:25:24 PM JPMugaas + Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions + from the UDP client with SOCKS. You must call OpenProxy before using + RecvFrom or SendTo. When you are finished, you must use CloseProxy to close + any connection to the Proxy. Connect and disconnect also call OpenProxy and + CloseProxy. + + Rev 1.3 11/11/2004 3:42:52 AM JPMugaas + Moved strings into RS. Socks will now raise an exception if you attempt to + use SOCKS4 and SOCKS4A with UDP. Those protocol versions do not support UDP + at all. + + Rev 1.2 2004.05.20 11:39:12 AM czhower + IdStreamVCL + + Rev 1.1 6/4/2004 5:13:26 PM SGrobety + EIdMaxCaptureLineExceeded message string + + Rev 1.0 2004.02.03 4:19:50 PM czhower + Rename + + Rev 1.15 10/24/2003 4:21:56 PM DSiders + Addes resource string for stream read exception. + + Rev 1.14 2003.10.16 11:25:22 AM czhower + Added missing ; + + Rev 1.13 10/15/2003 11:11:06 PM DSiders + Added resource srting for exception raised in TIdTCPServer.SetScheduler. + + Rev 1.12 10/15/2003 11:03:00 PM DSiders + Added resource string for circular links from transparent proxy. + Corrected spelling errors. + + Rev 1.11 10/15/2003 10:41:34 PM DSiders + Added resource strings for TIdStream and TIdStreamProxy exceptions. + + Rev 1.10 10/15/2003 8:48:56 PM DSiders + Added resource strings for exceptions raised when setting thread component + properties. + + Rev 1.9 10/15/2003 8:35:28 PM DSiders + Added resource string for exception raised in TIdSchedulerOfThread.NewYarn. + + Rev 1.8 10/15/2003 8:04:26 PM DSiders + Added resource strings for exceptions raised in TIdLogFile, TIdReply, and + TIdIOHandler. + + Rev 1.7 10/15/2003 1:03:42 PM DSiders + Created resource strings for TIdBuffer.Find exceptions. + + Rev 1.6 2003.10.14 1:26:44 PM czhower + Uupdates + Intercept support + + Rev 1.5 10/1/2003 10:49:02 PM GGrieve + Rework buffer for Octane Compability + + Rev 1.4 7/1/2003 8:32:32 PM BGooijen + Added RSFibersNotSupported + + Rev 1.3 7/1/2003 02:31:34 PM JPMugaas + Message for invalid IP address. + + Rev 1.2 5/14/2003 6:40:22 PM BGooijen + RS for transparent proxy + + Rev 1.1 1/17/2003 05:06:04 PM JPMugaas + Exceptions for scheduler string. + + Rev 1.0 11/13/2002 08:42:02 AM JPMugaas +} + +unit IdResourceStringsCore; + +interface + +{$i IdCompilerDefines.inc} + +resourcestring + RSNoBindingsSpecified = 'No bindings specified.'; + RSCannotAllocateSocket = 'Cannot allocate socket.'; + RSSocksUDPNotSupported = 'UDP is not support in this SOCKS version.'; + RSSocksRequestFailed = 'Request rejected or failed.'; + RSSocksRequestServerFailed = 'Request rejected because SOCKS server cannot connect.'; + RSSocksRequestIdentFailed = 'Request rejected because the client program and identd report different user-ids.'; + RSSocksUnknownError = 'Unknown socks error.'; + RSSocksServerRespondError = 'Socks server did not respond.'; + RSSocksAuthMethodError = 'Invalid socks authentication method.'; + RSSocksAuthError = 'Authentication error to socks server.'; + RSSocksServerGeneralError = 'General SOCKS server failure.'; + RSSocksServerPermissionError = 'Connection not allowed by ruleset.'; + RSSocksServerNetUnreachableError = 'Network unreachable.'; + RSSocksServerHostUnreachableError = 'Host unreachable.'; + RSSocksServerConnectionRefusedError = 'Connection refused.'; + RSSocksServerTTLExpiredError = 'TTL expired.'; + RSSocksServerCommandError = 'Command not supported.'; + RSSocksServerAddressError = 'Address type not supported.'; + RSInvalidIPAddress = 'Invalid IP Address'; + RSInterceptCircularLink = '%s: Circular links are not allowed'; + + RSNotEnoughDataInBuffer = 'Not enough data in buffer. (%d/%d)'; + RSTooMuchDataInBuffer = 'Too much data in buffer.'; + RSCapacityTooSmall = 'Capacity cannot be smaller than Size.'; + RSBufferIsEmpty = 'No bytes in buffer.'; + RSBufferRangeError = 'Index out of bounds.'; + + RSFileNotFound = 'File "%s" not found'; + RSNotConnected = 'Not Connected'; + RSObjectTypeNotSupported = 'Object type not supported.'; + RSIdNoDataToRead = 'No data to read.'; + RSReadTimeout = 'Read timed out.'; + RSReadLnWaitMaxAttemptsExceeded = 'Max line read attempts exceeded.'; + RSAcceptTimeout = 'Accept timed out.'; + RSReadLnMaxLineLengthExceeded = 'Max line length exceeded.'; + RSRequiresLargeStream = 'Set LargeStream to True to send streams greater than 2GB'; + RSDataTooLarge = 'Data is too large for stream'; + RSConnectTimeout = 'Connect timed out.'; + RSICMPNotEnoughtBytes = 'Not enough bytes received'; + RSICMPNonEchoResponse = 'Non-echo type response received'; + RSThreadTerminateAndWaitFor = 'Cannot call TerminateAndWaitFor on FreeAndTerminate threads'; + RSAlreadyConnected = 'Already connected.'; + RSTerminateThreadTimeout = 'Terminate Thread Timeout'; + RSNoExecuteSpecified = 'No execute handler found.'; + RSNoCommandHandlerFound = 'No command handler found.'; + RSCannotPerformTaskWhileServerIsActive = 'Cannot perform task while server is active.'; + RSThreadClassNotSpecified = 'Thread Class Not Specified.'; + RSMaximumNumberOfCaptureLineExceeded = 'Maximum number of line allowed exceeded'; // S.G. 6/4/2004: IdIOHandler.DoCapture + RSNoCreateListeningThread = 'Cannot create listening thread.'; + RSInterceptIsDifferent = 'The IOHandler already has a different Intercept assigned'; + + //scheduler + RSchedMaxThreadEx = 'The maximum number of threads for this scheduler is exceeded.'; + //transparent proxy + RSTransparentProxyCannotBind = 'Transparent proxy cannot bind.'; + RSTransparentProxyCanNotSupportUDP = 'UDP Not supported by this proxy.'; + //Fibers + RSFibersNotSupported = 'Fibers are not supported on this system.'; + // TIdICMPCast + RSIPMCastInvalidMulticastAddress = 'The supplied IP address is not a valid multicast address [224.0.0.0 to 239.255.255.255].'; + RSIPMCastNotSupportedOnWin32 = 'This function is not supported on Win32.'; + RSIPMCastReceiveError0 = 'IP Broadcast Receive Error = 0.'; + + // Log strings + RSLogConnected = 'Connected.'; + RSLogDisconnected = 'Disconnected.'; + RSLogEOL = ''; // End of Line + RSLogCR = ''; // Carriage Return + RSLogLF = ''; // Line feed + RSLogRecv = 'Recv '; // Receive + RSLogSent = 'Sent '; // Send + RSLogStat = 'Stat '; // Status + + RSLogFileAlreadyOpen = 'Unable to set Filename while log file is open.'; + + RSBufferMissingTerminator = 'Buffer terminator must be specified.'; + RSBufferInvalidStartPos = 'Buffer start position is invalid.'; + + RSIOHandlerCannotChange = 'Cannot change a connected IOHandler.'; + RSIOHandlerTypeNotInstalled = 'No IOHandler of type %s is installed.'; + + RSReplyInvalidCode = 'Reply Code is not valid: %s'; + RSReplyCodeAlreadyExists = 'Reply Code already exists: %s'; + + RSThreadSchedulerThreadRequired = 'Thread must be specified for the scheduler.'; + RSNoOnExecute = 'You must have an OnExecute event.'; + RSThreadComponentLoopAlreadyRunning = 'Cannot set Loop property when the Thread is already running.'; + RSThreadComponentThreadNameAlreadyRunning = 'Cannot set ThreadName when the Thread is already running.'; + + RSStreamProxyNoStack = 'A Stack has not been created for converting the data type.'; + + RSTransparentProxyCyclic = 'Transparent Proxy Cyclic error.'; + + RSTCPServerSchedulerAlreadyActive = 'Cannot change the scheduler while the server is Active.'; + RSUDPMustUseProxyOpen = 'You must use proxyOpen'; + +//ICMP stuff + RSICMPTimeout = 'Timeout'; +//Destination Address -3 + RSICMPNetUnreachable = 'net unreachable;'; + RSICMPHostUnreachable = 'host unreachable;'; + RSICMPProtUnreachable = 'protocol unreachable;'; + RSICMPPortUnreachable = 'Port Unreachable'; + RSICMPFragmentNeeded = 'Fragmentation Needed and Don''t Fragment was Set'; + RSICMPSourceRouteFailed = 'Source Route Failed'; + RSICMPDestNetUnknown = 'Destination Network Unknown'; + RSICMPDestHostUnknown = 'Destination Host Unknown'; + RSICMPSourceIsolated = 'Source Host Isolated'; + RSICMPDestNetProhibitted = 'Communication with Destination Network is Administratively Prohibited'; + RSICMPDestHostProhibitted = 'Communication with Destination Host is Administratively Prohibited'; + RSICMPTOSNetUnreach = 'Destination Network Unreachable for Type of Service'; + RSICMPTOSHostUnreach = 'Destination Host Unreachable for Type of Service'; + RSICMPAdminProhibitted = 'Communication Administratively Prohibited'; + RSICMPHostPrecViolation = 'Host Precedence Violation'; + RSICMPPrecedenceCutoffInEffect = 'Precedence cutoff in effect'; + //for IPv6 + RSICMPNoRouteToDest = 'no route to destination'; + RSICMPAAdminDestProhibitted = 'communication with destination administratively prohibited'; + RSICMPSourceFilterFailed = 'source address failed ingress/egress policy'; + RSICMPRejectRoutToDest = 'reject route to destination'; + // Destination Address - 11 + RSICMPTTLExceeded = 'time to live exceeded in transit'; + RSICMPHopLimitExceeded = 'hop limit exceeded in transit'; + RSICMPFragAsmExceeded = 'fragment reassembly time exceeded.'; +//Parameter Problem - 12 + RSICMPParamError = 'Parameter Problem (offset %d)'; + //IPv6 + RSICMPParamHeader = 'erroneous header field encountered (offset %d)'; + RSICMPParamNextHeader = 'unrecognized Next Header type encountered (offset %d)'; + RSICMPUnrecognizedOpt = 'unrecognized IPv6 option encountered (offset %d)'; +//Source Quench Message -4 + RSICMPSourceQuenchMsg = 'Source Quench Message'; +//Redirect Message + RSICMPRedirNet = 'Redirect datagrams for the Network.'; + RSICMPRedirHost = 'Redirect datagrams for the Host.'; + RSICMPRedirTOSNet = 'Redirect datagrams for the Type of Service and Network.'; + RSICMPRedirTOSHost = 'Redirect datagrams for the Type of Service and Host.'; +//echo + RSICMPEcho = 'Echo'; +//timestamp + RSICMPTimeStamp = 'Timestamp'; +//information request + RSICMPInfoRequest = 'Information Request'; +//mask request + RSICMPMaskRequest = 'Address Mask Request'; +// Traceroute + RSICMPTracePacketForwarded = 'Outbound Packet successfully forwarded'; + RSICMPTraceNoRoute = 'No route for Outbound Packet; packet discarded'; +//conversion errors + RSICMPConvUnknownUnspecError = 'Unknown/unspecified error'; + RSICMPConvDontConvOptPresent = 'Don''t Convert option present'; + RSICMPConvUnknownMandOptPresent = 'Unknown mandatory option present'; + RSICMPConvKnownUnsupportedOptionPresent = 'Known unsupported option present'; + RSICMPConvUnsupportedTransportProtocol = 'Unsupported transport protocol'; + RSICMPConvOverallLengthExceeded = 'Overall length exceeded'; + RSICMPConvIPHeaderLengthExceeded = 'IP header length exceeded'; + RSICMPConvTransportProtocol_255 = 'Transport protocol > 255'; + RSICMPConvPortConversionOutOfRange = 'Port conversion out of range'; + RSICMPConvTransportHeaderLengthExceeded = 'Transport header length exceeded'; + RSICMPConv32BitRolloverMissingAndACKSet = '32 Bit Rollover missing and ACK set'; + RSICMPConvUnknownMandatoryTransportOptionPresent = 'Unknown mandatory transport option present'; +//mobile host redirect + RSICMPMobileHostRedirect = 'Mobile Host Redirect'; +//IPv6 - Where are you + RSICMPIPv6WhereAreYou = 'IPv6 Where-Are-You'; +//IPv6 - I am here + RSICMPIPv6IAmHere = 'IPv6 I-Am-Here'; +// Mobile Regestration request + RSICMPMobReg = 'Mobile Registration Request'; +//Skip + RSICMPSKIP = 'SKIP'; +//Security + RSICMPSecBadSPI = 'Bad SPI'; + RSICMPSecAuthenticationFailed = 'Authentication Failed'; + RSICMPSecDecompressionFailed = 'Decompression Failed'; + RSICMPSecDecryptionFailed = 'Decryption Failed'; + RSICMPSecNeedAuthentication = 'Need Authentication'; + RSICMPSecNeedAuthorization = 'Need Authorization'; +//IPv6 Packet Too Big + RSICMPPacketTooBig = 'Packet Too Big (MTU = %d)'; +{ TIdCustomIcmpClient } + + // TIdSimpleServer + RSCannotUseNonSocketIOHandler = 'Cannot use a non-socket IOHandler'; + +implementation + +end. diff --git a/indy/Core/IdScheduler.pas b/indy/Core/IdScheduler.pas new file mode 100644 index 0000000..12c1d5d --- /dev/null +++ b/indy/Core/IdScheduler.pas @@ -0,0 +1,195 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.14 4/8/2004 11:55:30 AM BGooijen + Fix for D5 + + Rev 1.13 2004.03.01 5:12:38 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.12 2004.01.20 10:03:30 PM czhower + InitComponent + + Rev 1.11 2003.10.21 12:18:58 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.10 2003.10.14 11:18:08 PM czhower + Fix for AV on shutdown and other bugs + + Rev 1.9 2003.10.11 5:49:24 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.8 2003.09.19 10:11:16 PM czhower + Next stage of fiber support in servers. + + Rev 1.7 2003.09.19 11:54:30 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.6 2003.09.18 4:10:24 PM czhower + Preliminary changes for Yarn support. + + Rev 1.5 3/27/2003 5:15:36 PM BGooijen + Moved some code from subclasses here, made MaxThreads published + + Rev 1.4 3/13/2003 10:18:36 AM BGooijen + Server side fibers, bug fixes + + Rev 1.1 1/23/2003 11:06:04 AM BGooijen + + Rev 1.0 1/17/2003 03:41:48 PM JPMugaas + Scheduler base class. +} + +unit IdScheduler; + +interface + +{$i IdCompilerDefines.inc} + +uses + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ELSE} + {$IFDEF VCL_XE3_OR_ABOVE} + System.Classes, + {$ELSE} + Classes, + {$ENDIF} + {$ENDIF} + IdBaseComponent, IdThread, IdTask, IdYarn, IdThreadSafe; + +type + {$IFDEF HAS_GENERICS_TThreadList} + TIdYarnThreadList = TIdThreadSafeObjectList; + TIdYarnList = TList; + {$ELSE} + // TODO: flesh out to match TIdThreadSafeObjectList and TList for non-Generics compilers + TIdYarnThreadList = TIdThreadSafeObjectList; + TIdYarnList = TList; + {$ENDIF} + + TIdScheduler = class(TIdBaseComponent) + protected + FActiveYarns: TIdYarnThreadList; + // + procedure InitComponent; override; + public + destructor Destroy; override; + function AcquireYarn: TIdYarn; virtual; abstract; + procedure Init; virtual; + // ReleaseYarn is to remove a yarn from the list that has already been + // terminated (usually self termination); + procedure ReleaseYarn(AYarn: TIdYarn); virtual; + procedure StartYarn(AYarn: TIdYarn; ATask: TIdTask); virtual; abstract; + // TerminateYarn is to terminate a yarn explicitly and remove it also + procedure TerminateYarn(AYarn: TIdYarn); virtual; abstract; + procedure TerminateAllYarns; virtual; + // + property ActiveYarns: TIdYarnThreadList read FActiveYarns; + end; + +implementation + +uses + //facilitate inlining only. + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + {$ENDIF} + {$ENDIF} + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Collections} + {$IFDEF VCL_XE3_OR_ABOVE} + System.Classes, + System.Types, + {$ELSE} + Classes, + {$ENDIF} + {$ELSE} + {$IFDEF VCL_XE3_OR_ABOVE} + System.Types, //here to facilitate inlining + {$ENDIF} + {$ENDIF} + IdGlobal, SysUtils; + +{ TIdScheduler } + +destructor TIdScheduler.Destroy; +begin + FreeAndNil(FActiveYarns); + inherited Destroy; +end; + +procedure TIdScheduler.Init; +begin +end; + +procedure TIdScheduler.InitComponent; +begin + inherited InitComponent; + FActiveYarns := TIdYarnThreadList.Create; +end; + +procedure TIdScheduler.ReleaseYarn(AYarn: TIdYarn); +begin + ActiveYarns.Remove(AYarn); +end; + +procedure TIdScheduler.TerminateAllYarns; +var + i: Integer; + LList: TIdYarnList; +begin + Assert(FActiveYarns<>nil); + + while True do begin + // Must unlock each time to allow yarns that are terminating to remove themselves from the list + LList := FActiveYarns.LockList; + try + if LList.Count = 0 then begin + Break; + end; + for i := LList.Count - 1 downto 0 do begin + TerminateYarn( + {$IFDEF HAS_GENERICS_TList}LList.Items[i]{$ELSE}TIdYarn(LList.Items[i]){$ENDIF} + ); + end; + finally + FActiveYarns.UnlockList; + end; + //TODO: Put terminate timeout check back + IndySleep(500); // Wait a bit before looping to prevent thrashing + end; +end; + +end. diff --git a/indy/Core/IdSchedulerOfThread.pas b/indy/Core/IdSchedulerOfThread.pas new file mode 100644 index 0000000..8de9ae5 --- /dev/null +++ b/indy/Core/IdSchedulerOfThread.pas @@ -0,0 +1,264 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.32 3/23/2005 8:20:18 AM JPMugaas + Temp fix for a double-free problem causing an AV. I will explain on Core. + + Rev 1.31 6/11/2004 8:48:32 AM DSiders + Added "Do not Localize" comments. + + Rev 1.30 2004.03.01 5:12:38 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.29 2004.02.03 4:17:04 PM czhower + For unit name changes. + + Rev 1.28 2004.01.22 5:59:14 PM czhower + IdCriticalSection + + Rev 1.27 2004.01.20 10:03:32 PM czhower + InitComponent + + Rev 1.26 6/11/2003 8:28:42 PM GGrieve + remove wrong call to inherited StartYarn + + Rev 1.25 2003.10.24 12:59:18 PM czhower + Name change + + Rev 1.24 2003.10.21 12:19:00 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.23 10/15/2003 8:35:30 PM DSiders + Added resource string for exception raised in TIdSchedulerOfThread.NewYarn. + + Rev 1.22 2003.10.14 11:18:10 PM czhower + Fix for AV on shutdown and other bugs + + Rev 1.21 2003.10.11 5:49:32 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.20 2003.09.19 10:11:18 PM czhower + Next stage of fiber support in servers. + + Rev 1.19 2003.09.19 11:54:30 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.18 2003.09.18 4:43:18 PM czhower + -Removed IdBaseThread + -Threads now have default names + + Rev 1.17 2003.09.18 4:10:26 PM czhower + Preliminary changes for Yarn support. + + Rev 1.16 2003.07.17 1:08:04 PM czhower + Fixed warning + + Rev 1.15 7/6/2003 8:04:06 PM BGooijen + Renamed IdScheduler* to IdSchedulerOf* + + Rev 1.14 7/5/2003 11:49:06 PM BGooijen + Cleaned up and fixed av in threadpool + + Rev 1.13 2003.06.30 9:39:44 PM czhower + Comments and small change. + + Rev 1.12 6/25/2003 3:54:02 PM BGooijen + Destructor waits now until all threads are terminated + + Rev 1.11 2003.06.25 4:27:02 PM czhower + Fixed some formatting and fixed one line ifs. + + Rev 1.10 4/11/2003 6:35:28 PM BGooijen + + Rev 1.9 3/27/2003 5:17:22 PM BGooijen + Moved some code to TIdScheduler, made ThreadPriority published + + Rev 1.8 3/22/2003 1:49:38 PM BGooijen + Fixed warnings (.ShouldStop) + + Rev 1.7 3/13/2003 10:18:30 AM BGooijen + Server side fibers, bug fixes + + Rev 1.6 1/23/2003 11:55:24 PM BGooijen + + Rev 1.5 1/23/2003 8:32:40 PM BGooijen + Added termination handler + + Rev 1.3 1/23/2003 11:05:58 AM BGooijen + + Rev 1.2 1-17-2003 23:22:16 BGooijen + added MaxThreads property + + Rev 1.1 1/17/2003 03:43:04 PM JPMugaas + Updated to use new class. + + Rev 1.0 1/17/2003 03:29:50 PM JPMugaas + Renamed from ThreadMgr for new design. + + Rev 1.0 11/13/2002 09:01:32 AM JPMugaas + + 02 Oct 2001 - Allen O'Neill + Added support for thread priority - new property Threadpriority, + new line added to OnCreate +} + +unit IdSchedulerOfThread; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException, IdBaseComponent, IdGlobal, IdScheduler, + IdThread, IdTask, IdYarn; + +type + TIdYarnOfThread = class(TIdYarn) + protected + // TODO: should these be [Weak] on ARC systems? + FScheduler: TIdScheduler; + FThread: TIdThreadWithTask; + public + constructor Create(AScheduler: TIdScheduler; AThread: TIdThreadWithTask); reintroduce; + destructor Destroy; override; + // + property Thread: TIdThreadWithTask read FThread; + end; + + TIdSchedulerOfThread = class(TIdScheduler) + protected + FMaxThreads: Integer; + FThreadPriority: TIdThreadPriority; + FThreadClass: TIdThreadWithTaskClass; + // + procedure InitComponent; override; + public + destructor Destroy; override; + function NewThread: TIdThreadWithTask; virtual; + function NewYarn(AThread: TIdThreadWithTask = nil): TIdYarnOfThread; + procedure StartYarn(AYarn: TIdYarn; ATask: TIdTask); override; + procedure TerminateYarn(AYarn: TIdYarn); override; + property ThreadClass: TIdThreadWithTaskClass read FThreadClass write FThreadClass; + published + property MaxThreads: Integer read FMaxThreads write FMaxThreads; + property ThreadPriority: TIdThreadPriority read FThreadPriority write FThreadPriority default tpNormal; + end; + +implementation + +uses + {$IFDEF KYLIXCOMPAT} + Libc, + {$ENDIF} + IdResourceStringsCore, IdTCPServer, IdThreadSafe, IdExceptionCore, SysUtils; + +{ TIdSchedulerOfThread } + +destructor TIdSchedulerOfThread.Destroy; +begin + TerminateAllYarns; + inherited Destroy; +end; + +procedure TIdSchedulerOfThread.StartYarn(AYarn: TIdYarn; ATask: TIdTask); +var + LThread: TIdThreadWithTask; +begin + LThread := TIdYarnOfThread(AYarn).Thread; + LThread.Task := ATask; + LThread.Start; +end; + +function TIdSchedulerOfThread.NewThread: TIdThreadWithTask; +begin + Assert(FThreadClass<>nil); + if (FMaxThreads <> 0) and (not ActiveYarns.IsCountLessThan(FMaxThreads + 1)) then begin + raise EIdSchedulerMaxThreadsExceeded.Create(RSchedMaxThreadEx); + end; + Result := FThreadClass.Create(nil, IndyFormat('%s User', [Name])); {do not localize} + if ThreadPriority <> tpNormal then begin + IndySetThreadPriority(Result, ThreadPriority); + end; +end; + +function TIdSchedulerOfThread.NewYarn(AThread: TIdThreadWithTask): TIdYarnOfThread; +begin + if not Assigned(AThread) then begin + raise EIdException.Create(RSThreadSchedulerThreadRequired); + end; + // Create Yarn + Result := TIdYarnOfThread.Create(Self, AThread); +end; + +procedure TIdSchedulerOfThread.TerminateYarn(AYarn: TIdYarn); +var + LYarn: TIdYarnOfThread; +begin + Assert(AYarn<>nil); + LYarn := TIdYarnOfThread(AYarn); + if (LYarn.Thread <> nil) and (not LYarn.Thread.Suspended) then begin + // Is still running and will free itself + LYarn.Thread.Stop; + // Dont free the yarn. The thread frees it (IdThread.pas) + end else + begin + // If suspended, was created but never started + // ie waiting on connection accept + + // RLebeau: free the yarn here as well. This allows TIdSchedulerOfThreadPool + // to put the suspended thread, if present, back in the pool. + + IdDisposeAndNil(LYarn); + end; +end; + +procedure TIdSchedulerOfThread.InitComponent; +begin + inherited InitComponent; + FThreadPriority := tpNormal; + FMaxThreads := 0; + FThreadClass := TIdThreadWithTask; +end; + +{ TIdYarnOfThread } + +constructor TIdYarnOfThread.Create( + AScheduler: TIdScheduler; + AThread: TIdThreadWithTask + ); +begin + inherited Create; + FScheduler := AScheduler; + FThread := AThread; + AThread.Yarn := Self; +end; + +destructor TIdYarnOfThread.Destroy; +begin + FScheduler.ReleaseYarn(Self); + inherited Destroy; +end; + +end. diff --git a/indy/Core/IdSchedulerOfThreadDefault.pas b/indy/Core/IdSchedulerOfThreadDefault.pas new file mode 100644 index 0000000..31dff32 --- /dev/null +++ b/indy/Core/IdSchedulerOfThreadDefault.pas @@ -0,0 +1,145 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.12 2004.02.03 4:17:06 PM czhower + For unit name changes. + + Rev 1.11 2003.10.24 12:59:20 PM czhower + Name change + + Rev 1.10 2003.10.21 12:19:00 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.9 2003.10.11 5:49:40 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.8 2003.09.19 10:11:18 PM czhower + Next stage of fiber support in servers. + + Rev 1.7 2003.09.19 11:54:30 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.6 2003.09.18 4:10:26 PM czhower + Preliminary changes for Yarn support. + + Rev 1.5 7/6/2003 8:04:06 PM BGooijen + Renamed IdScheduler* to IdSchedulerOf* + + Rev 1.4 7/5/2003 11:49:06 PM BGooijen + Cleaned up and fixed av in threadpool + + Rev 1.3 2003.06.25 4:26:40 PM czhower + Removed unecessary code in RemoveThread + + Rev 1.2 3/13/2003 10:18:32 AM BGooijen + Server side fibers, bug fixes + + Rev 1.1 1/23/2003 11:06:02 AM BGooijen + + Rev 1.0 1/17/2003 03:29:54 PM JPMugaas + Renamed from ThreadMgr for new design. + + Rev 1.0 11/13/2002 09:01:40 AM JPMugaas +} + +unit IdSchedulerOfThreadDefault; + +interface +{$i IdCompilerDefines.inc} + +uses + IdThread, IdSchedulerOfThread, IdScheduler, IdYarn, IdContext; + +type + TIdSchedulerOfThreadDefault = class(TIdSchedulerOfThread) + public + function AcquireYarn: TIdYarn; override; + procedure ReleaseYarn(AYarn: TIdYarn); override; + function NewThread: TIdThreadWithTask; override; + end; + +implementation + +uses +{$IFDEF USE_VCL_POSIX} +{$ENDIF} + IdGlobal; + +{ TIdSchedulerOfThreadDefault } + +function TIdSchedulerOfThreadDefault.AcquireYarn: TIdYarn; +begin + Result := NewYarn(NewThread); + ActiveYarns.Add(Result); +end; + +type + TIdYarnOfThreadAccess = class(TIdYarnOfThread) + end; + +procedure TIdSchedulerOfThreadDefault.ReleaseYarn(AYarn: TIdYarn); +//only gets called from YarnOf(Fiber/Thread).Destroy +var + LThread: TIdThreadWithTask; +begin + //take posession of the thread + LThread := TIdYarnOfThread(AYarn).Thread; + TIdYarnOfThreadAccess(AYarn).FThread := nil; + //Currently LThread can =nil. Is that a valid condition? + //Assert(LThread<>nil); + + // inherited removes from ActiveYarns list + inherited ReleaseYarn(AYarn); + + if LThread <> nil then begin + // need to destroy the thread + LThread.Yarn := nil; // Yarn is being destroyed, de-couple it from the thread + LThread.Terminate; + // RLebeau - ReleaseYarn() can be called in the context of + // the yarn's thread (when TIdThread.Cleanup() destroys the + // yarn between connnections), so have to check which context + // we're in here so as not to deadlock the thread! + if IsCurrentThread(LThread) then begin + LThread.FreeOnTerminate := True; + end else begin + {$IFDEF DEPRECATED_TThread_SuspendResume} + LThread.Suspended := False; + {$ELSE} + LThread.Resume; + {$ENDIF} + LThread.WaitFor; + LThread.Free; + end; + end; +end; + +function TIdSchedulerOfThreadDefault.NewThread: TIdThreadWithTask; +begin + Result := inherited NewThread; + // RLebeau 2/25/2010: do not let the thread free itself on termination yet. + // It can cause crashes during Yarn shutdown, so let the Scheduler decide + // what to do with the thread later... + //Result.FreeOnTerminate := True; +end; + +end. + diff --git a/indy/Core/IdSchedulerOfThreadPool.pas b/indy/Core/IdSchedulerOfThreadPool.pas new file mode 100644 index 0000000..4183cd2 --- /dev/null +++ b/indy/Core/IdSchedulerOfThreadPool.pas @@ -0,0 +1,270 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.12 2004.02.03 4:17:06 PM czhower + For unit name changes. + + Rev 1.11 2003.10.24 12:59:20 PM czhower + Name change + + Rev 1.10 2003.10.21 12:19:00 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.9 2003.10.11 5:49:50 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.8 2003.09.19 10:11:20 PM czhower + Next stage of fiber support in servers. + + Rev 1.7 2003.09.19 11:54:32 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.6 2003.09.18 4:10:26 PM czhower + Preliminary changes for Yarn support. + + Rev 1.5 7/6/2003 8:04:08 PM BGooijen + Renamed IdScheduler* to IdSchedulerOf* + + Rev 1.4 7/5/2003 11:49:06 PM BGooijen + Cleaned up and fixed av in threadpool + + Rev 1.3 4/15/2003 10:56:08 PM BGooijen + fixes + + Rev 1.2 3/13/2003 10:18:34 AM BGooijen + Server side fibers, bug fixes + + Rev 1.1 1/23/2003 7:28:46 PM BGooijen + + Rev 1.0 1/17/2003 03:29:58 PM JPMugaas + Renamed from ThreadMgr for new design. + + Rev 1.0 11/13/2002 09:01:46 AM JPMugaas + + 2002-06-23 -Andrew P.Rybin + -2 deadlock fix (and also in IdThread) +} + +unit IdSchedulerOfThreadPool; + +interface + +{$i IdCompilerDefines.inc} + +uses + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ELSE} + Classes, + {$ENDIF} + IdContext, + IdScheduler, + IdSchedulerOfThread, + IdThread, + //IdThreadSafe, + IdYarn; + +type + {$IFDEF HAS_GENERICS_TThreadList} + TIdPoolThreadList = TThreadList; + TIdPoolList = TList; + {$ELSE} + // TODO: flesh out to match TThreadList and TList for non-Generics compilers + TIdPoolThreadList = TThreadList; + TIdPoolList = TList; + {$ENDIF} + + TIdSchedulerOfThreadPool = class(TIdSchedulerOfThread) + protected + FPoolSize: Integer; + FThreadPool: TIdPoolThreadList; + procedure InitComponent; override; + public + destructor Destroy; override; + function AcquireYarn: TIdYarn; override; + procedure Init; override; + function NewThread: TIdThreadWithTask; override; + procedure ReleaseYarn(AYarn: TIdYarn); override; + procedure TerminateAllYarns; override; + published + //TODO: Poolsize is only looked at during loading and when threads are + // needed. Probably should add an Active property to schedulers like + // servers have. + property PoolSize: Integer read FPoolSize write FPoolSize default 0; + End; + +implementation + +uses + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + IdGlobal, SysUtils; + +type + TIdYarnOfThreadAccess = class(TIdYarnOfThread) + end; + +destructor TIdSchedulerOfThreadPool.Destroy; +begin + inherited Destroy; + // Must be after, inherited calls TerminateThreads + FreeAndNil(FThreadPool); +end; + +function TIdSchedulerOfThreadPool.AcquireYarn: TIdYarn; +var + LThread: TIdThreadWithTask; + LList: TIdPoolList; +begin + LList := FThreadPool.LockList; + try + if LList.Count > 0 then begin + LThread := {$IFDEF HAS_GENERICS_TList}LList.Items[0]{$ELSE}TIdThreadWithTask(LList.Items[0]){$ENDIF}; + LList.Delete(0); + end else begin + LThread := nil; + end; + finally + FThreadPool.UnlockList; + end; + + if LThread = nil then begin + LThread := NewThread; + end; + + Result := NewYarn(LThread); + ActiveYarns.Add(Result); +end; + +procedure TIdSchedulerOfThreadPool.ReleaseYarn(AYarn: TIdYarn); +//only gets called from YarnOf(Fiber/Thread).Destroy +var + LThread: TIdThreadWithTask; + LList: TIdPoolList; +begin + //take posession of the thread + LThread := TIdYarnOfThread(AYarn).Thread; + TIdYarnOfThreadAccess(AYarn).FThread := nil; + //Currently LThread can =nil. Is that a valid condition? + //Assert(LThread<>nil); + + // inherited removes from ActiveYarns list + inherited ReleaseYarn(AYarn); + + if LThread <> nil then begin + // need to redeposit the thread in the pool or destroy it + LThread.Yarn := nil; // Yarn is being destroyed, de-couple it from the thread + LList := FThreadPool.LockList; + try + if (LList.Count < PoolSize) and (not LThread.Terminated) then begin + LList.Add(LThread); + Exit; + end; + finally + FThreadPool.UnlockList; + end; + LThread.Terminate; + // RLebeau - ReleaseYarn() can be called in the context of + // the yarn's thread (when TIdThread.Cleanup() destroys the + // yarn between connnections), so have to check which context + // we're in here so as not to deadlock the thread! + if IsCurrentThread(LThread) then begin + LThread.FreeOnTerminate := True; + end else begin + {$IFDEF DEPRECATED_TThread_SuspendResume} + LThread.Suspended := False; + {$ELSE} + LThread.Resume; + {$ENDIF} + LThread.WaitFor; + LThread.Free; + end; + end; +end; + +procedure TIdSchedulerOfThreadPool.TerminateAllYarns; +var + LThread: TIdThreadWithTask; + LList: TIdPoolList; +begin + // inherited will kill off ActiveYarns + inherited TerminateAllYarns; + // ThreadPool is nil if never Initted + if FThreadPool <> nil then begin + // Now we have to kill off the pooled threads + LList := FThreadPool.LockList; + try + while LList.Count > 0 do begin + LThread := {$IFDEF HAS_GENERICS_TList}LList.Items[0]{$ELSE}TIdThreadWithTask(LList.Items[0]){$ENDIF}; + LThread.Terminate; + {$IFDEF DEPRECATED_TThread_SuspendResume} + LThread.Suspended := False; + {$ELSE} + LThread.Resume; + {$ENDIF} + LThread.WaitFor; + LThread.Free; + LList.Delete(0); + end; + finally + FThreadPool.UnlockList; + end; + end; +end; + +procedure TIdSchedulerOfThreadPool.Init; +var + LList: TIdPoolList; +begin + inherited Init; + Assert(FThreadPool<>nil); + + if not IsDesignTime then begin + if PoolSize > 0 then begin + LList := FThreadPool.LockList; + try + while LList.Count < PoolSize do begin + LList.Add(NewThread); + end; + finally + FThreadPool.UnlockList; + end; + end; + end; +end; + +function TIdSchedulerOfThreadPool.NewThread: TIdThreadWithTask; +begin + Result := inherited NewThread; + Result.StopMode := smSuspend; +end; + +procedure TIdSchedulerOfThreadPool.InitComponent; +begin + inherited; + FThreadPool := TIdPoolThreadList.Create; +end; + +end. diff --git a/indy/Core/IdServerIOHandler.pas b/indy/Core/IdServerIOHandler.pas new file mode 100644 index 0000000..8e9340d --- /dev/null +++ b/indy/Core/IdServerIOHandler.pas @@ -0,0 +1,136 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 2003.10.11 5:49:56 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.6 2003.09.19 11:54:32 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.5 2003.09.18 4:10:28 PM czhower + Preliminary changes for Yarn support. + + Rev 1.4 3/23/2003 11:26:08 PM BGooijen + Added SetScheduler,MakeClientIOHandler + + Rev 1.3 3/13/2003 10:18:22 AM BGooijen + Server side fibers, bug fixes + + Rev 1.2 1-17-2003 22:22:02 BGooijen + new design + + Rev 1.1 1-1-2003 16:28:58 BGooijen + Changed TIdThread to TIdYarn + + Rev 1.0 11/13/2002 08:46:16 AM JPMugaas +} + +unit IdServerIOHandler; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdSocketHandle, IdComponent, IdIOHandlerStack, IdStackConsts, + IdIOHandler, IdThread, IdScheduler, IdYarn; + +type + TIdServerIOHandler = class(TIdComponent) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FScheduler: TIdScheduler; + {$IFNDEF USE_OBJECT_ARC} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + {$ENDIF} + public + // This is a thread and not a yarn. Its the listener thread. + function Accept( + ASocket: TIdSocketHandle; + AListenerThread: TIdThread; + AYarn: TIdYarn + ): TIdIOHandler; + virtual; + function MakeClientIOHandler(AYarn: TIdYarn): TIdIOHandler; virtual; + // Init is called when the server goes active + procedure Init; virtual; + procedure Shutdown; virtual; + // SetScheduler is called by the user (normally TCPServer) automatically + procedure SetScheduler(AScheduler: TIdScheduler); virtual; + end; + +implementation + +procedure TIdServerIOHandler.Init; +begin +end; + +function TIdServerIOHandler.Accept( + ASocket: TIdSocketHandle; + AListenerThread: TIdThread; + AYarn: TIdYarn + ): TIdIOHandler; +begin + Result := nil; +end; + +function TIdServerIOHandler.MakeClientIOHandler(AYarn: TIdYarn): TIdIOHandler; +begin + Result := nil; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +{$IFNDEF USE_OBJECT_ARC} +procedure TIdServerIOHandler.Notification(AComponent: TComponent; Operation: TOperation); +begin + // Remove the reference to the linked Scheduler if it is deleted + if (Operation = opRemove) and (AComponent = FScheduler) then begin + FScheduler := nil; + end; + inherited Notification(AComponent, Operation); +end; +{$ENDIF} + +procedure TIdServerIOHandler.SetScheduler(AScheduler: TIdScheduler); +begin + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FScheduler := AScheduler; + {$ELSE} + if FScheduler <> AScheduler then begin + // Remove self from the Scheduler's notification list + if Assigned(FScheduler) then begin + FScheduler.RemoveFreeNotification(Self); + end; + FScheduler := AScheduler; + // Add self to the Scheduler's notification list + if Assigned(FScheduler) then begin + FScheduler.FreeNotification(Self); + end; + end; + {$ENDIF} +end; + +procedure TIdServerIOHandler.Shutdown; +begin +end; + +end. diff --git a/indy/Core/IdServerIOHandlerSocket.pas b/indy/Core/IdServerIOHandlerSocket.pas new file mode 100644 index 0000000..e86d3d1 --- /dev/null +++ b/indy/Core/IdServerIOHandlerSocket.pas @@ -0,0 +1,151 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.16 23.3.2005 . 20:52:00 DBondzhev + LIOHandler is not released if exception is thrown while listening for + incomming connection. + + Rev 1.15 2004.02.03 4:17:04 PM czhower + For unit name changes. + + Rev 1.14 2004.01.20 10:03:32 PM czhower + InitComponent + + Rev 1.13 2003.10.11 5:50:00 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.12 10/5/2003 03:03:58 AM JPMugaas + Should compile. + + Rev 1.11 2003.09.19 10:11:20 PM czhower + Next stage of fiber support in servers. + + Rev 1.10 2003.09.19 11:54:34 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.9 2003.09.18 4:10:28 PM czhower + Preliminary changes for Yarn support. + + Rev 1.8 2003.07.14 10:15:36 PM czhower + Changed timeout to 250 from 100 + + Rev 1.7 3/29/2003 5:55:02 PM BGooijen + now calls AfterAccept + + Rev 1.6 3/13/2003 10:18:28 AM BGooijen + Server side fibers, bug fixes + + Rev 1.5 1-17-2003 22:22:04 BGooijen + new design + + Rev 1.4 1-1-2003 16:28:26 BGooijen + Changed TIdThread to TIdYarn + + Rev 1.3 12-7-2002 17:02:32 BGooijen + Now creates IOHandlerSockets of class IOHandlerSocketClass. + This is more flexible for descendants + + Rev 1.2 12-7-2002 12:34:38 BGooijen + Re-enabled IPv6 support + + Rev 1.1 05/12/2002 15:32:00 AO'Neill + + Rev 1.0 12/2/2002 05:01:30 PM JPMugaas + Rechecked in due to file corruption. +} + +unit IdServerIOHandlerSocket; + +interface +{$i IdCompilerDefines.inc} + +uses + IdSocketHandle, IdGlobal, IdThread, IdServerIOHandler, IdStackConsts, IdIOHandler, IdScheduler, + IdIOHandlerSocket, IdYarn; + +type + TIdIOHandlerSocketClass = class of TIdIOHandlerSocket; + + TIdServerIOHandlerSocket = class(TIdServerIOHandler) + protected + IOHandlerSocketClass: TIdIOHandlerSocketClass; + // + procedure InitComponent; override; + public + function Accept( + ASocket: TIdSocketHandle; + AListenerThread: TIdThread; + AYarn: TIdYarn + ): TIdIOHandler; + override; + procedure Init; override; + end; + +implementation +uses SysUtils; + +{ TIdServerIOHandlerSocket } + +procedure TIdServerIOHandlerSocket.Init; +begin + // +end; + +function TIdServerIOHandlerSocket.Accept( + ASocket: TIdSocketHandle; + AListenerThread: TIdThread; + AYarn: TIdYarn + ): TIdIOHandler; +var + LIOHandler: TIdIOHandlerSocket; +begin + //using a custom scheduler, AYarn may be nil, so don't assert + Assert(ASocket<>nil); + Assert(AListenerThread<>nil); + + Result := nil; + LIOHandler := IOHandlerSocketClass.Create(nil); + try + LIOHandler.Open; + while not AListenerThread.Stopped do begin + if ASocket.Select(250) then begin + if LIOHandler.Binding.Accept(ASocket.Handle) then begin + LIOHandler.AfterAccept; + Result := LIOHandler; + LIOHandler := nil; + Break; + end; + end; + end; + finally + FreeAndNil(LIOHandler); + end; +end; + +procedure TIdServerIOHandlerSocket.InitComponent; +begin + inherited InitComponent; + IOHandlerSocketClass := TIdIOHandlerSocket; +end; + +end. + diff --git a/indy/Core/IdServerIOHandlerStack.pas b/indy/Core/IdServerIOHandlerStack.pas new file mode 100644 index 0000000..507b9b4 --- /dev/null +++ b/indy/Core/IdServerIOHandlerStack.pas @@ -0,0 +1,89 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.11 2004.02.03 4:16:50 PM czhower + For unit name changes. + + Rev 1.10 2004.01.20 10:03:34 PM czhower + InitComponent + + Rev 1.9 2003.09.19 10:11:20 PM czhower + Next stage of fiber support in servers. + + Rev 1.8 2003.09.19 11:54:34 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.7 2003.09.18 4:10:28 PM czhower + Preliminary changes for Yarn support. + + Rev 1.6 3/23/2003 11:27:48 PM BGooijen + Added MakeClientIOHandler + + Rev 1.5 3/13/2003 10:18:24 AM BGooijen + Server side fibers, bug fixes + + Rev 1.4 1-17-2003 22:22:06 BGooijen + new design + + Rev 1.3 1-1-2003 16:27:50 BGooijen + Changed TIdThread to TIdYarn + + Rev 1.2 12-7-2002 17:04:02 BGooijen + Now descends from TIdServerIOHandlerSocket. + + Rev 1.1 12-7-2002 12:34:40 BGooijen + Re-enabled IPv6 support + + Rev 1.0 11/13/2002 08:58:34 AM JPMugaas +} + +unit IdServerIOHandlerStack; + +interface +{$i IdCompilerDefines.inc} + +uses + IdSocketHandle, IdGlobal, IdThread, IdServerIOHandler, IdStackConsts, IdIOHandler, IdScheduler, + IdIOHandlerStack, IdServerIOHandlerSocket, IdYarn; + +type + TIdServerIOHandlerStack = class(TIdServerIOHandlerSocket) + protected + procedure InitComponent; override; + public + function MakeClientIOHandler(ATheThread:TIdYarn ): TIdIOHandler; override; + end; + +implementation + +{ TIdServerIOHandlerStack } + +procedure TIdServerIOHandlerStack.InitComponent; +begin + inherited InitComponent; + IOHandlerSocketClass := TIdIOHandlerStack; +end; + +function TIdServerIOHandlerStack.MakeClientIOHandler(ATheThread:TIdYarn ): TIdIOHandler; +begin + Result := IOHandlerSocketClass.Create(nil); +end; + +end. + diff --git a/indy/Core/IdSimpleServer.pas b/indy/Core/IdSimpleServer.pas new file mode 100644 index 0000000..39abafd --- /dev/null +++ b/indy/Core/IdSimpleServer.pas @@ -0,0 +1,343 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.17 7/13/04 6:46:36 PM RLebeau + Added support for BoundPortMin/Max propeties +} +{ + Rev 1.16 6/6/2004 12:49:40 PM JPMugaas + Removed old todo's for things that have already been done. +} +{ + Rev 1.15 5/6/2004 6:04:44 PM JPMugaas + Attempt to reenable TransparentProxy.Bind. +} +{ + Rev 1.14 5/5/2004 2:08:40 PM JPMugaas + Reenabled Socks Listen for TIdSimpleServer. +} +{ + Rev 1.13 2004.02.03 4:16:52 PM czhower + For unit name changes. +} +{ + Rev 1.12 2004.01.20 10:03:34 PM czhower + InitComponent +} +{ + Rev 1.11 1/2/2004 12:02:16 AM BGooijen + added OnBeforeBind/OnAfterBind +} +{ + Rev 1.10 1/1/2004 10:57:58 PM BGooijen + Added IPv6 support +} +{ + Rev 1.9 10/26/2003 10:08:44 PM BGooijen + Compiles in DotNet +} +{ + Rev 1.8 10/20/2003 03:04:56 PM JPMugaas + Should now work without Transparant Proxy. That still needs to be enabled. +} +{ + Rev 1.7 2003.10.14 9:57:42 PM czhower + Compile todos +} +{ + Rev 1.6 2003.10.11 5:50:12 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support +} +{ + Rev 1.5 2003.09.30 1:23:02 PM czhower + Stack split for DotNet +} +{ + Rev 1.4 5/16/2003 9:25:36 AM BGooijen + TransparentProxy support +} +{ + Rev 1.3 3/29/2003 5:55:04 PM BGooijen + now calls AfterAccept +} +{ + Rev 1.2 3/23/2003 11:24:46 PM BGooijen + changed cast from TIdIOHandlerStack to TIdIOHandlerSocket +} +{ + Rev 1.1 1-6-2003 21:39:00 BGooijen + The handle to the listening socket was not closed when accepting a + connection. This is fixed by merging the responsible code from 9.00.11 + + Rev 1.0 11/13/2002 08:58:40 AM JPMugaas +} +unit IdSimpleServer; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdGlobal, + IdSocketHandle, + IdTCPConnection, + IdStackConsts, + IdIOHandler; + +const + ID_ACCEPT_WAIT = 1000; + +type + TIdSimpleServer = class(TIdTCPConnection) + protected + FAbortedRequested: Boolean; + FAcceptWait: Integer; + FBoundIP: String; + FBoundPort: TIdPort; + FBoundPortMin: TIdPort; + FBoundPortMax: TIdPort; + FIPVersion: TIdIPVersion; + FListenHandle: TIdStackSocketHandle; + FListening: Boolean; + FOnBeforeBind: TNotifyEvent; + FOnAfterBind: TNotifyEvent; + // + procedure Bind; + procedure DoBeforeBind; virtual; + procedure DoAfterBind; virtual; + function GetBinding: TIdSocketHandle; + procedure InitComponent; override; + procedure SetIOHandler(AValue: TIdIOHandler); override; + procedure SetIPVersion(const AValue: TIdIPVersion); + public + procedure Abort; virtual; + procedure BeginListen; virtual; + procedure CreateBinding; + procedure EndListen; virtual; + procedure Listen(ATimeout: Integer = IdTimeoutDefault); virtual; + // + property AcceptWait: Integer read FAcceptWait write FAcceptWait default ID_ACCEPT_WAIT; + published + property BoundIP: string read FBoundIP write FBoundIP; + property BoundPort: TIdPort read FBoundPort write FBoundPort; + property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin; + property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax; + property Binding: TIdSocketHandle read GetBinding; + property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion; + + property OnBeforeBind: TNotifyEvent read FOnBeforeBind write FOnBeforeBind; + property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind; + end; + + EIdCannotUseNonSocketIOHandler = class(EIdException); + +implementation + +uses + IdExceptionCore, + IdIOHandlerStack, + IdIOHandlerSocket, + IdResourceStringsCore, + IdStack; + +{ TIdSimpleServer } + +procedure TIdSimpleServer.Abort; +begin + FAbortedRequested := True; +end; + +procedure TIdSimpleServer.BeginListen; +begin + // Must be before IOHandler as it resets it + EndListen; + CreateBinding; + if Socket.TransparentProxy.Enabled then begin + Socket.Binding.IP := BoundIP; + Socket.TransparentProxy.Bind(FIOHandler, BoundPort); + end else begin + Bind; + Binding.Listen(1); + end; + FListening := True; +end; + +procedure TIdSimpleServer.Bind; +var + LBinding: TIdSocketHandle; +begin + LBinding := Binding; + try + DoBeforeBind; + LBinding.IPVersion := FIPVersion; // needs to be before AllocateSocket, because AllocateSocket uses this + LBinding.AllocateSocket; + FListenHandle := LBinding.Handle; + LBinding.IP := BoundIP; + LBinding.Port := BoundPort; + LBinding.ClientPortMin := BoundPortMin; + LBinding.ClientPortMax := BoundPortMax; + LBinding.Bind; + DoAfterBind; + except + FListenHandle := Id_INVALID_SOCKET; + raise; + end; +end; + +procedure TIdSimpleServer.CreateBinding; +begin + if not Assigned(IOHandler) then begin + CreateIOHandler(); + end; + IOHandler.Open; +end; + +procedure TIdSimpleServer.DoBeforeBind; +begin + if Assigned(FOnBeforeBind) then begin + FOnBeforeBind(self); + end; +end; + +procedure TIdSimpleServer.DoAfterBind; +begin + if Assigned(FOnAfterBind) then begin + FOnAfterBind(self); + end; +end; + +procedure TIdSimpleServer.EndListen; +begin + FAbortedRequested := False; + FListening := False; +end; + +function TIdSimpleServer.GetBinding: TIdSocketHandle; +begin + if Assigned(Socket) then begin + Result := Socket.Binding; + end else begin + Result := nil; + end; +end; + +procedure TIdSimpleServer.SetIOHandler(AValue: TIdIOHandler); +begin + if Assigned(AValue) then begin + if not (AValue is TIdIOHandlerSocket) then begin + raise EIdCannotUseNonSocketIOHandler.Create(RSCannotUseNonSocketIOHandler); + end; + end; + inherited SetIOHandler(AValue); +end; + +procedure TIdSimpleServer.SetIPVersion(const AValue: TIdIPVersion); +begin + FIPVersion := AValue; + if Assigned(Socket) then begin + Socket.IPVersion := AValue; + end; +end; + +procedure TIdSimpleServer.Listen(ATimeout: Integer = IdTimeoutDefault); +var + LAccepted: Boolean; + + function DoListenTimeout(ALTimeout: Integer; AUseProxy: Boolean): Boolean; + var + LSleepTime: Integer; + begin + LSleepTime := AcceptWait; + + if ALTimeout = IdTimeoutDefault then begin + ALTimeout := IdTimeoutInfinite; + end; + + if ALTimeout = IdTimeoutInfinite then begin + repeat + if AUseProxy then begin + Result := Socket.TransparentProxy.Listen(IOHandler, LSleepTime); + end else begin + Result := Binding.Select(LSleepTime); + end; + until Result or FAbortedRequested; + Exit; + end; + + while ALTimeout > LSleepTime do begin + if AUseProxy then begin + Result := Socket.TransparentProxy.Listen(IOHandler, LSleepTime); + end else begin + Result := Binding.Select(LSleepTime); + end; + + if Result or FAbortedRequested then begin + Exit; + end; + + Dec(ALTimeout, LSleepTime); + end; + + if AUseProxy then begin + Result := Socket.TransparentProxy.Listen(IOHandler, ALTimeout); + end else begin + Result := Binding.Select(ALTimeout); + end; + end; + +begin + if not FListening then begin + BeginListen; + end; + + if Socket.TransparentProxy.Enabled then begin + LAccepted := DoListenTimeout(ATimeout, True); + end else + begin + LAccepted := DoListenTimeout(ATimeout, False); + if LAccepted then begin + if Binding.Accept(Binding.Handle) then begin + IOHandler.AfterAccept; + end; + end; + +// This is now protected. Disconnect replaces it - but it also calls shutdown. +// Im not sure we want to call shutdown here? Need to investigate before fixing +// this. + GStack.Disconnect(FListenHandle); + FListenHandle := Id_INVALID_SOCKET; + end; + + if not LAccepted then begin + raise EIdAcceptTimeout.Create(RSAcceptTimeout); + end; +end; + +procedure TIdSimpleServer.InitComponent; +begin + inherited InitComponent; + FAcceptWait := ID_ACCEPT_WAIT; + FListenHandle := Id_INVALID_SOCKET; +end; + +end. diff --git a/indy/Core/IdSocketHandle.pas b/indy/Core/IdSocketHandle.pas new file mode 100644 index 0000000..e7c7cb1 --- /dev/null +++ b/indy/Core/IdSocketHandle.pas @@ -0,0 +1,719 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 4/11/2005 2:17:46 PM JPMugaas + Fix from Ben Taylor for where a pointer is used after it's freed causing an + invalid pointer operation. + + Rev 1.7 23.3.2005 . 20:50:04 DBondzhev + Fixed problem on multi CPU systems when connection is closed while it get's + connected at the end of the timeout period. + + Rev 1.6 11/15/2004 11:40:08 PM JPMugaas + Added IPAddressType parameter to SetBinding )AIPVersion). This would set the + same variable as the SetPeer AIPVersion parameter. It's just a convenience + sake since both the receiver and sender must have the same type of IP address + (unless there's a gateway thing we support). + + Rev 1.5 11/12/2004 11:30:18 AM JPMugaas + Expansions for IPv6. + + Rev 1.4 09/06/2004 09:48:42 CCostelloe + Kylix 3 patch + + Rev 1.3 4/26/04 12:40:26 PM RLebeau + Removed recursion from Readable() + + Rev 1.2 2004.03.07 11:48:48 AM czhower + Flushbuffer fix + other minor ones found + + Rev 1.1 3/6/2004 5:16:14 PM JPMugaas + Bug 67 fixes. Do not write to const values. + + Rev 1.0 2004.02.03 3:14:40 PM czhower + Move and updates + + Rev 1.23 2/2/2004 12:09:16 AM JPMugaas + GetSockOpt should now work in DotNET. + + Rev 1.22 2/1/2004 6:10:46 PM JPMugaas + GetSockOpt. + + Rev 1.21 12/31/2003 9:51:58 PM BGooijen + Added IPv6 support + + Rev 1.20 10/26/2003 12:29:40 PM BGooijen + DotNet + + Rev 1.19 10/22/2003 04:40:48 PM JPMugaas + Should compile with some restored functionality. Still not finished. + + Rev 1.18 2003.10.11 5:50:26 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.17 10/5/2003 9:55:30 PM BGooijen + TIdTCPServer works on D7 and DotNet now + + Rev 1.16 2003.10.02 12:44:42 PM czhower + Fix for Bind, Connect + + Rev 1.15 2003.10.02 10:16:28 AM czhower + .Net + + Rev 1.14 2003.10.01 9:11:20 PM czhower + .Net + + Rev 1.13 2003.10.01 5:05:14 PM czhower + .Net + + Rev 1.12 2003.10.01 2:30:40 PM czhower + .Net + + Rev 1.10 10/1/2003 12:14:12 AM BGooijen + DotNet: removing CheckForSocketError + + Rev 1.9 2003.10.01 1:12:36 AM czhower + .Net + + Rev 1.8 2003.09.30 1:23:02 PM czhower + Stack split for DotNet + + Rev 1.7 20.09.2003 16:33:28 ARybin + bug fix: + NOT Integer <> 0 is not boolean operation, because: + (NOT Integer) = inverted integer + + Rev 1.6 2003.07.14 1:57:24 PM czhower + -First set of IOCP fixes. + -Fixed a threadsafe problem with the stack class. + + Rev 1.5 7/1/2003 05:20:36 PM JPMugaas + Minor optimizations. Illiminated some unnecessary string operations. + + Rev 1.4 7/1/2003 03:39:52 PM JPMugaas + Started numeric IP function API calls for more efficiency. + + Rev 1.3 5/11/2003 11:59:06 AM BGooijen + Added OverLapped property + + Rev 1.2 5/11/2003 12:35:30 AM BGooijen + temporary creates overlapped socked handles + + Rev 1.1 3/21/2003 01:50:08 AM JPMugaas + SetBinding method added as per request received in private E-Mail. + + Rev 1.0 11/13/2002 08:58:46 AM JPMugaas +} + +unit IdSocketHandle; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdException, IdGlobal, IdStackConsts, IdStack, IdBaseComponent; + +type + TIdSocketHandle = class; + + TIdSocketHandles = class(TOwnedCollection) + protected + FDefaultPort: TIdPort; + // + function GetItem(Index: Integer): TIdSocketHandle; + procedure SetItem(Index: Integer; const Value: TIdSocketHandle); + public + constructor Create(AOwner: TComponent); reintroduce; + function Add: TIdSocketHandle; reintroduce; + function BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle; + property Items[Index: Integer]: TIdSocketHandle read GetItem write SetItem; default; + // + property DefaultPort: TIdPort read FDefaultPort write FDefaultPort; + end; + + TIdSocketHandle = class(TCollectionItem) + protected + FClientPortMin: TIdPort; + FClientPortMax: TIdPort; + FHandle: TIdStackSocketHandle; + FHandleAllocated: Boolean; + FIP: string; + FPeerIP: string; + FPort: TIdPort; + FPeerPort: TIdPort; + FReadSocketList: TIdSocketList; + FSocketType : TIdSocketType; + FOverLapped: Boolean; + FIPVersion: TIdIPVersion; + FConnectionHandle: TIdCriticalSection; + FBroadcastEnabled: Boolean; + FUseNagle : Boolean; + FReuseSocket: TIdReuseSocket; + // + function BindPortReserved: Boolean; + procedure BroadcastEnabledChanged; + procedure SetBroadcastEnabled(const AValue: Boolean); + procedure Disconnect; virtual; + procedure SetBroadcastFlag(const AEnabled: Boolean); + procedure SetOverLapped(const AValue: Boolean); + procedure SetHandle(AHandle: TIdStackSocketHandle); + procedure SetIPVersion(const Value: TIdIPVersion); + procedure SetUseNagle(const AValue: Boolean); + function TryBind(APort: TIdPort): Boolean; + public + function Accept(ASocket: TIdStackSocketHandle): Boolean; + procedure AllocateSocket(const ASocketType: TIdSocketType = Id_SOCK_STREAM; + const AProtocol: TIdSocketProtocol = Id_IPPROTO_IP); + // Returns True if error was ignored (Matches iIgnore), false if no error occurred + procedure Assign(Source: TPersistent); override; + procedure Bind; + procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = ''; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload; + procedure CloseSocket; virtual; + procedure Connect; virtual; + constructor Create(ACollection: TCollection); override; + destructor Destroy; override; + procedure Listen(const AQueueCount: Integer = 5); + function Readable(AMSec: Integer = IdTimeoutDefault): boolean; + function Receive(var VBuffer: TIdBytes): Integer; + function RecvFrom(var ABuffer : TIdBytes; var VIP: string; + var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer; + procedure Reset(const AResetLocal: boolean = True); + function Send(const AData: String; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): Integer; overload; + function Send(const ABuffer: TIdBytes; const AOffset: Integer = 0; const ASize: Integer = -1): Integer; overload; + procedure SendTo(const AIP: string; const APort: TIdPort; const AData: String; + const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload; + procedure SendTo(const AIP: string; const APort: TIdPort; const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); overload; + procedure SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION); + procedure SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION); + procedure GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer); + procedure SetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; AOptVal: Integer); + function Select(ATimeout: Integer = IdTimeoutInfinite): Boolean; + procedure UpdateBindingLocal; + procedure UpdateBindingPeer; + procedure AddMulticastMembership(const AGroupIP: String); + procedure DropMulticastMembership(const AGroupIP: String); + procedure SetKeepAliveValues(const AEnabled: Boolean; const ATimeMS, AInterval: Integer); + procedure SetLoopBack(const AValue: Boolean); + procedure SetMulticastTTL(const AValue: Byte); + procedure SetTTL(const AValue: Integer); + procedure SetNagleOpt(const AEnabled: Boolean); + // + property HandleAllocated: Boolean read FHandleAllocated; + property Handle: TIdStackSocketHandle read FHandle; + property OverLapped: Boolean read FOverLapped write SetOverLapped; + property PeerIP: string read FPeerIP; + property PeerPort: TIdPort read FPeerPort; + property SocketType : TIdSocketType read FSocketType; + published + property BroadcastEnabled: Boolean read FBroadcastEnabled write SetBroadcastEnabled default False; + property ClientPortMin : TIdPort read FClientPortMin write FClientPortMin default DEF_PORT_ANY; + property ClientPortMax : TIdPort read FClientPortMax write FClientPortMax default DEF_PORT_ANY; + property IP: string read FIP write FIP; + property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION; + property Port: TIdPort read FPort write FPort; + property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent; + property UseNagle: Boolean read FUseNagle write SetUseNagle default True; + end; + + TIdSocketHandleEvent = procedure(AHandle: TIdSocketHandle) of object; + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.SyncObjs, + {$ENDIF} + IdAntiFreezeBase, IdComponent, IdResourceStrings, SysUtils; + +{ TIdSocketHandle } + +procedure TIdSocketHandle.AllocateSocket(const ASocketType: TIdSocketType; + const AProtocol: TIdSocketProtocol); +begin + // If we are reallocating a socket - close and destroy the old socket handle + CloseSocket; + if HandleAllocated then begin + Reset; + end; + // Set property so it calls the writer + SetHandle(GStack.NewSocketHandle(ASocketType, AProtocol, FIPVersion, FOverLapped)); +end; + +procedure TIdSocketHandle.Disconnect; +begin + GStack.Disconnect(Handle); +end; + +procedure TIdSocketHandle.CloseSocket; +begin + if HandleAllocated then begin + FConnectionHandle.Enter; try + // Must be first, closing socket will trigger some errors, and they + // may then call (in other threads) Connected, which in turn looks at + // FHandleAllocated. + FHandleAllocated := False; + Disconnect; + SetHandle(Id_INVALID_SOCKET); + finally + FConnectionHandle.Leave; + end; + end; +end; + +procedure TIdSocketHandle.Connect; +begin + GStack.Connect(Handle, PeerIP, PeerPort, FIPVersion); + FConnectionHandle.Enter; try + if HandleAllocated then begin + // UpdateBindingLocal needs to be called even though Bind calls it. After + // Bind is may be 0.0.0.0 (INADDR_ANY). After connect it will be a real IP. + UpdateBindingLocal; + //TODO: Could Peer binding ever be other than what we specified above? + // Need to reread it? + UpdateBindingPeer; + end; + finally + FConnectionHandle.Leave; + end; +end; + +destructor TIdSocketHandle.Destroy; +begin + CloseSocket; + FreeAndNil(FConnectionHandle); + FreeAndNil(FReadSocketList); + inherited Destroy; +end; + +function TIdSocketHandle.Receive(var VBuffer: TIdBytes): Integer; +begin + Result := GStack.Receive(Handle, VBuffer); +end; + +function TIdSocketHandle.Send(const AData: String; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): Integer; +begin + Result := Send(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +function TIdSocketHandle.Send(const ABuffer: TIdBytes; const AOffset: Integer = 0; + const ASize: Integer = -1): Integer; +begin + Result := GStack.Send(Handle, ABuffer, AOffset, ASize); +end; + +procedure TIdSocketHandle.SetSockOpt(ALevel: TIdSocketOptionLevel; + AOptName: TIdSocketOption; AOptVal: Integer); +begin + GStack.SetSocketOption(Handle, ALevel, AOptName, AOptVal); +end; + +procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort; + const AData: String; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + SendTo(AIP, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), AIPVersion); +end; + +procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort; + const ABuffer : TIdBytes; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + SendTo(AIP, APort, ABuffer, 0, -1, AIPVersion); +end; + +procedure TIdSocketHandle.SendTo(const AIP: string; const APort: TIdPort; + const ABuffer : TIdBytes; const AOffset: Integer; const ASize: Integer; + const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + GStack.SendTo(Handle, ABuffer, AOffset, ASize, AIP, APort, AIPVersion); +end; + +function TIdSocketHandle.RecvFrom(var ABuffer : TIdBytes; var VIP: string; + var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer; +begin + Result := GStack.ReceiveFrom(Handle, ABuffer, VIP, VPort, VIPVersion); +end; + +procedure TIdSocketHandle.Bind; +begin + SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, + iif( + (FReuseSocket = rsTrue) or ((FReuseSocket = rsOSDependent) and (GOSType = otUnix)), + Id_SO_True, + Id_SO_False + ) + ); + if (Port = 0) and (FClientPortMin <> 0) and (FClientPortMax <> 0) then begin + if (FClientPortMin > FClientPortMax) then begin + raise EIdInvalidPortRange.CreateFmt(RSInvalidPortRange, [FClientPortMin, FClientPortMax]); + end else if not BindPortReserved then begin + raise EIdCanNotBindPortInRange.CreateFmt(RSCannotBindRange, [FClientPortMin, FClientPortMax]); + end; + end else if not TryBind(Port) then begin + raise EIdCouldNotBindSocket.Create(RSCouldNotBindSocket); + end; +end; + +procedure TIdSocketHandle.Broadcast(const AData: string; const APort: TIdPort; + const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + Broadcast(ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}), APort, AIP); +end; + +procedure TIdSocketHandle.Broadcast(const AData: TIdBytes; const APort: TIdPort; + const AIP: String = ''); +var + LIP: String; +begin + LIP := Trim(AIP); + if LIP = '' then begin + // TODO: on Windows, use WSAIoctl(SIO_GET_BROADCAST_ADDRESS) instead. + // On other platforms, use getifaddrs() or other suitable API to retreive + // the broadcast IP if possible, or else the local IP/Subnet and then + // calculate the broadcast IP manually... + LIP := '255.255.255.255'; {Do not Localize} + end else begin + LIP := GStack.ResolveHost(LIP, IPVersion); + end; + SetBroadcastFlag(True); + SendTo(LIP, APort, AData); + BroadcastEnabledChanged; +end; + +procedure TIdSocketHandle.BroadcastEnabledChanged; +begin + SetBroadcastFlag(FBroadcastEnabled); +end; + +procedure TIdSocketHandle.SetPeer(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + FPeerIP := AIP; + FPeerPort := APort; + FIPVersion := AIPVersion; +end; + +procedure TIdSocketHandle.SetBinding(const AIP: string; const APort: TIdPort; const AIPVersion : TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + FIP := AIP; + FPort := APort; + FIPVersion := AIPVersion; +end; + +procedure TIdSocketHandle.SetBroadcastEnabled(const AValue: Boolean); +begin + if FBroadCastEnabled <> AValue then begin + FBroadcastEnabled := AValue; + if HandleAllocated then begin + BroadcastEnabledChanged; + end; + end; +end; + +procedure TIdSocketHandle.SetBroadcastFlag(const AEnabled: Boolean); +begin + GStack.SetSocketOption(Handle, Id_SOL_SOCKET, Id_SO_BROADCAST, iif(AEnabled, 1, 0)); +end; + +procedure TIdSocketHandle.SetOverLapped(const AValue:boolean); +begin + // TODO: check for HandleAllocated + FOverLapped := AValue; +end; + +procedure TIdSocketHandle.Listen(const AQueueCount: Integer = 5); +begin + GStack.Listen(Handle, AQueueCount); +end; + +function TIdSocketHandle.Accept(ASocket: TIdStackSocketHandle): Boolean; +var + LAcceptedSocket: TIdStackSocketHandle; + LIP: String; + LPort: TIdPort; +begin + Reset; + LAcceptedSocket := GStack.Accept(ASocket, LIP, LPort); + Result := (LAcceptedSocket <> Id_INVALID_SOCKET); + if Result then begin + SetHandle(LAcceptedSocket); + // UpdateBindingLocal is necessary as it may be listening on multiple IPs/Ports + UpdateBindingLocal; + UpdateBindingPeer; + end; +end; + +constructor TIdSocketHandle.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + FUseNagle := True; + FReuseSocket := rsOSDependent; + FConnectionHandle := TIdCriticalSection.Create; + FReadSocketList := TIdSocketList.CreateSocketList; + Reset; + FClientPortMin := 0; + FClientPortMax := 0; + FIPVersion := ID_DEFAULT_IP_VERSION; + if Assigned(ACollection) then begin + Port := TIdSocketHandles(ACollection).DefaultPort; + end; +end; + +function TIdSocketHandle.Readable(AMSec: Integer = IdTimeoutDefault): Boolean; + + function CheckIsReadable(ALMSec: Integer): Boolean; + begin + if not HandleAllocated then begin + raise EIdConnClosedGracefully.Create(RSConnectionClosedGracefully); + end; + Result := Select(ALMSec); + end; + +begin + if AMSec = IdTimeoutDefault then begin + AMSec := IdTimeoutInfinite; + end; + if TIdAntiFreezeBase.ShouldUse then begin + if AMSec = IdTimeoutInfinite then begin + repeat + Result := CheckIsReadable(GAntiFreeze.IdleTimeOut); + until Result; + Exit; + end; + while AMSec > GAntiFreeze.IdleTimeOut do begin + Result := CheckIsReadable(GAntiFreeze.IdleTimeOut); + if Result then begin + Exit; + end; + Dec(AMSec, GAntiFreeze.IdleTimeOut); + end; + end; + Result := CheckIsReadable(AMSec); +end; + +procedure TIdSocketHandle.Assign(Source: TPersistent); +var + LSource: TIdSocketHandle; +begin + if Source is TIdSocketHandle then begin + LSource := TIdSocketHandle(Source); + FIP := LSource.FIP; + Port := LSource.Port; + FPeerIP := LSource.FPeerIP; + FPeerPort := LSource.FPeerPort; + FIPVersion := LSource.IPVersion; + end else begin + inherited + end; +end; + +procedure TIdSocketHandle.UpdateBindingLocal; +begin + GStack.GetSocketName(Handle, FIP, FPort, FIPVersion); +end; + +procedure TIdSocketHandle.UpdateBindingPeer; +begin + GStack.GetPeerName(Handle, FPeerIP, FPeerPort, FIPVersion); +end; + +procedure TIdSocketHandle.Reset(const AResetLocal: boolean = True); +begin + SetHandle(Id_INVALID_SOCKET); + if AResetLocal then begin + FIP := ''; + FPort := 0; + end; + FPeerIP := ''; + FPeerPort := 0; + FIPVersion := ID_DEFAULT_IP_VERSION; +end; + +function TIdSocketHandle.TryBind(APort: TIdPort): Boolean; +begin + try + GStack.Bind(Handle, FIP, APort, FIPVersion); + Result := True; + UpdateBindingLocal; + except + Result := False; + end; +end; + +function TIdSocketHandle.BindPortReserved: Boolean; +var + i : TIdPort; +begin + Result := False; + for i := FClientPortMax downto FClientPortMin do begin + if TryBind(i) then begin + Result := True; + Exit; + end; + end; +end; + +procedure TIdSocketHandle.GetSockOpt(ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption; out VOptVal: Integer); +begin + GStack.GetSocketOption(Handle, ALevel, AOptName, VOptVal); +end; + +function TIdSocketHandle.Select(ATimeOut: Integer = IdTimeoutInfinite): Boolean; +begin + Result := FReadSocketList.SelectRead(ATimeOut); + TIdAntiFreezeBase.DoProcess(Result = False); +end; + +procedure TIdSocketHandle.SetHandle(AHandle: TIdStackSocketHandle); +var + LOpt: Integer; +begin + if FHandle <> Id_INVALID_SOCKET then begin + FReadSocketList.Remove(FHandle); + end; + FHandle := AHandle; + FHandleAllocated := FHandle <> Id_INVALID_SOCKET; + if FHandleAllocated then begin + FReadSocketList.Add(FHandle); + GetSockOpt(Id_SOL_SOCKET, Id_SO_TYPE, FSocketType); + //Get the NODELAY Socket option if we have a TCP Socket. + if SocketType = Id_SOCK_STREAM then begin + GetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, LOpt); + FUseNagle := (LOpt = 0); + end; + end else begin + FSocketType := Id_SOCK_UNKNOWN; + end; +end; + +procedure TIdSocketHandle.SetIPVersion(const Value: TIdIPVersion); +begin + if Value <> FIPVersion then begin + if HandleAllocated then begin + raise EIdCannotSetIPVersionWhenConnected.Create(RSCannotSetIPVersionWhenConnected); + end; + FIPVersion := Value; + end; +end; + +procedure TIdSocketHandle.AddMulticastMembership(const AGroupIP: String); +begin + GStack.AddMulticastMembership(Handle, AGroupIP, FIP, FIPVersion); +end; + +procedure TIdSocketHandle.DropMulticastMembership(const AGroupIP: String); +begin + GStack.DropMulticastMembership(Handle, AGroupIP, FIP, FIPVersion); +end; + +procedure TIdSocketHandle.SetKeepAliveValues(const AEnabled: Boolean; + const ATimeMS, AInterval: Integer); +begin + GStack.SetKeepAliveValues(Handle, AEnabled, ATimeMS, AInterval); +end; + +procedure TIdSocketHandle.SetLoopBack(const AValue: Boolean); +begin + GStack.SetLoopBack(Handle, AValue, FIPVersion); +end; + +procedure TIdSocketHandle.SetMulticastTTL(const AValue: Byte); +begin + GStack.SetMulticastTTL(Handle, AValue, FIPVersion); +end; + +procedure TIdSocketHandle.SetNagleOpt(const AEnabled: Boolean); +begin + { You only want to set a Nagle option for TCP.} + if HandleAllocated and (SocketType = Id_SOCK_STREAM) then begin + SetSockOpt(Id_SOCKETOPTIONLEVEL_TCP, Id_TCP_NODELAY, Integer(not AEnabled)); + end; +end; + +procedure TIdSocketHandle.SetTTL(const AValue: Integer); +begin + if FIPVersion = Id_IPv4 then begin + SetSockOpt(Id_SOL_IP, Id_SO_IP_TTL, AValue); + end else begin + SetSockOpt(Id_SOL_IPv6, Id_IPV6_UNICAST_HOPS, AValue); + end; +end; + +procedure TIdSocketHandle.SetUseNagle(const AValue: Boolean); +begin + if FUseNagle <> AValue then begin + FUseNagle := AValue; + SetNagleOpt(FUseNagle); + end; +end; + +{ TIdSocketHandles } + +function TIdSocketHandles.Add: TIdSocketHandle; +begin + Result := inherited Add as TIdSocketHandle; + Result.Port := DefaultPort; +end; + +function TIdSocketHandles.BindingByHandle(const AHandle: TIdStackSocketHandle): TIdSocketHandle; +var + i: integer; +begin + Result := nil; + for i := Count-1 downto 0 do begin + if Items[i].Handle = AHandle then begin + Result := Items[i]; + Exit; + end; + end; +end; + +constructor TIdSocketHandles.Create(AOwner: TComponent); +begin + inherited Create(AOwner, TIdSocketHandle); +end; + +function TIdSocketHandles.GetItem(Index: Integer): TIdSocketHandle; +begin + Result := TIdSocketHandle(inherited Items[index]); +end; + +procedure TIdSocketHandles.SetItem(Index: Integer; const Value: TIdSocketHandle); +begin + inherited SetItem(Index, Value); +end; + +end. diff --git a/indy/Core/IdSocks.pas b/indy/Core/IdSocks.pas new file mode 100644 index 0000000..7d72a37 --- /dev/null +++ b/indy/Core/IdSocks.pas @@ -0,0 +1,1032 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.38 11/15/2004 11:59:12 PM JPMugaas + Hopefully, this should handle IPv6 addresses in SOCKS bind and listen. + + + Rev 1.37 11/12/2004 11:30:18 AM JPMugaas + Expansions for IPv6. + + + Rev 1.36 11/11/2004 10:25:24 PM JPMugaas + Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions + from the UDP client with SOCKS. You must call OpenProxy before using + RecvFrom or SendTo. When you are finished, you must use CloseProxy to close + any connection to the Proxy. Connect and disconnect also call OpenProxy and + CloseProxy. + + + Rev 1.35 11/11/2004 3:42:50 AM JPMugaas + Moved strings into RS. Socks will now raise an exception if you attempt to + use SOCKS4 and SOCKS4A with UDP. Those protocol versions do not support UDP + at all. + + + Rev 1.34 11/10/2004 10:55:58 PM JPMugaas + UDP Association bug fix - we now send 0's for IP address and port. + + + Rev 1.33 11/10/2004 10:38:42 PM JPMugaas + Bug fixes - UDP with SOCKS now works. + + + Rev 1.32 11/10/2004 9:42:54 PM JPMugaas + 1 in a reserved position should be 0 in a UDP request packet. + + + Rev 1.31 11/9/2004 8:18:00 PM JPMugaas + Attempt to add SOCKS support in UDP. + + + Rev 1.30 03/07/2004 10:08:22 CCostelloe + Removed spurious code that generates warning + + + Rev 1.29 6/9/04 7:44:44 PM RLebeau + various ReadBytes() tweaks + + updated MakeSocks4Request() to call AIOHandler.WriteBufferCancel() on error. + + + Rev 1.28 2004.05.20 1:39:58 PM czhower + Last of the IdStream updates + + + Rev 1.27 2004.05.20 9:19:24 AM czhower + Removed unused var + + + Rev 1.26 5/19/2004 10:44:42 PM DSiders + Corrected spelling for TIdIPAddress.MakeAddressObject method. + + + Rev 1.25 5/19/2004 2:44:40 PM JPMugaas + Fixed compiler warnings in TIdSocksInfo.Listen. + + + Rev 1.24 5/8/2004 3:45:34 PM BGooijen + Listen works in Socks 4 now + + + Rev 1.23 5/7/2004 4:52:44 PM JPMugaas + Bind in SOCKS4 should work a bit better. There's still some other work that + needs to be done on it. + + + Rev 1.22 5/7/2004 8:54:54 AM JPMugaas + Attempt to add SOCKS4 bind. + + + Rev 1.21 5/7/2004 7:43:24 AM JPMugaas + Checked Bas's changes. + + + Rev 1.20 5/7/2004 5:53:20 AM JPMugaas + Removed some duplicate code to reduce the probability of error. + + + Rev 1.19 5/7/2004 1:44:12 AM BGooijen + Bind + + + Rev 1.18 5/6/2004 6:47:04 PM JPMugaas + Attempt to work on bind further. + + + Rev 1.16 5/6/2004 5:32:58 PM JPMugaas + Port was being mangled because the compiler was assuming you wanted a 4 byte + byte order instead of only a two byte byte order function. + IP addresses are better handled. At least I can connect again. + + + Rev 1.15 5/5/2004 2:09:40 PM JPMugaas + Attempt to reintroduce bind and listen functionality for FTP. + + + Rev 1.14 2004.03.07 11:48:44 AM czhower + Flushbuffer fix + other minor ones found + + + Rev 1.13 2004.02.03 4:16:52 PM czhower + For unit name changes. + + + Rev 1.12 2/2/2004 2:33:04 PM JPMugaas + Should compile better. + + + Rev 1.11 2/2/2004 12:23:16 PM JPMugaas + Attempt to fix the last Todo concerning IPv6. + + + Rev 1.10 2/2/2004 11:43:08 AM BGooijen + DotNet + + + Rev 1.9 2/2/2004 12:00:08 AM BGooijen + Socks 4 / 4A working again + + + Rev 1.8 2004.01.20 10:03:34 PM czhower + InitComponent + + + Rev 1.7 1/11/2004 10:45:56 PM BGooijen + Socks 5 works on D7 now, Socks 4 almost + + + Rev 1.6 2003.10.11 5:50:34 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + + Rev 1.5 2003.10.01 1:37:34 AM czhower + .Net + + + Rev 1.4 2003.09.30 7:37:28 PM czhower + Updates for .net + + + Rev 1.3 4/2/2003 3:23:00 PM BGooijen + fixed and re-enabled + + + Rev 1.2 2003.01.10 8:21:04 PM czhower + Removed more warnings + + + Rev 1.1 2003.01.10 7:21:14 PM czhower + Removed warnings + + + Rev 1.0 11/13/2002 08:58:56 AM JPMugaas +} +unit IdSocks; + +interface + +{$I IdCompilerDefines.inc} +//we need to put this in Delphi mode to work. + +uses + Classes, + IdAssignedNumbers, IdException, IdBaseComponent, + IdComponent, IdCustomTransparentProxy, IdGlobal, IdIOHandler, + IdIOHandlerSocket, IdSocketHandle; + +type + EIdSocksUDPNotSupportedBySOCKSVersion = class(EIdException); + TSocksVersion = (svNoSocks, svSocks4, svSocks4A, svSocks5); + TSocksAuthentication = (saNoAuthentication, saUsernamePassword); + +const + ID_SOCKS_AUTH = saNoAuthentication; + ID_SOCKS_VER = svNoSocks; + +type + TIdSocksInfo = class(TIdCustomTransparentProxy) + protected + FAuthentication: TSocksAuthentication; + FVersion: TSocksVersion; + FUDPSocksAssociation : TIdIOHandlerSocket; + + // + function DisasmUDPReplyPacket(const APacket : TIdBytes; + var VHost : String; var VPort : TIdPort; var VIPVersion: TIdIPVersion): TIdBytes; + function MakeUDPRequestPacket(const AData: TIdBytes; + const AHost: String; const APort: TIdPort) : TIdBytes; + function GetEnabled: Boolean; override; + procedure InitComponent; override; + procedure AuthenticateSocks5Connection(AIOHandler: TIdIOHandler); + // This must be defined with an port value that's a word so that we use the 2 byte Network Order byte functions instead + // the 4 byte or 8 byte functions. If we use the wrong byte order functions, we can get a zero port value causing an error. + procedure MakeSocks4Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte); + procedure MakeSocks5Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte; var VBuf : TIdBytes; var VLen : Integer); + procedure MakeSocks4Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort); + procedure MakeSocks4Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort); + procedure MakeSocks5Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); + procedure MakeSocks5Bind(AIOHandler: TIdIOHandler; const AHost: string; + const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); + procedure MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override; + function MakeSocks4Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean; + function MakeSocks5Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean; + + //association for UDP + procedure MakeSocks5UDPAssociation(AHandle : TIdSocketHandle); + procedure CloseSocks5UDPAssociation; + public + procedure Assign(ASource: TPersistent); override; + destructor Destroy; override; + procedure Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override; + function Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean;override; + procedure OpenUDP(AHandle : TIdSocketHandle; const AHost: string = ''; const APort: TIdPort = 0; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override; + function RecvFromUDP(AHandle: TIdSocketHandle; var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion; + AMSec: Integer = IdTimeoutDefault): Integer; override; + procedure SendToUDP(AHandle: TIdSocketHandle; const AHost: string; + const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); override; + procedure CloseUDP(AHandle: TIdSocketHandle); override; + published + property Authentication: TSocksAuthentication read FAuthentication write FAuthentication default ID_SOCKS_AUTH; + property Host; + property Password; + property Port default IdPORT_SOCKS; + property IPVersion; + property Username; + property Version: TSocksVersion read FVersion write FVersion default ID_SOCKS_VER; + property ChainedProxy; + End;//TIdSocksInfo + +implementation + +uses + IdResourceStringsCore, IdExceptionCore, IdIPAddress, IdStack, + IdTCPClient, + IdIOHandlerStack, SysUtils; + +{ TIdSocksInfo } + +procedure TIdSocksInfo.Assign(ASource: TPersistent); +var + LSource: TIdSocksInfo; +begin + if ASource is TIdSocksInfo then begin + LSource := TIdSocksInfo(ASource); + FAuthentication := LSource.Authentication; + FVersion := LSource.Version; + end; + // always allow TIdCustomTransparentProxy to assign its properties as well + inherited Assign(ASource); +end; + +procedure TIdSocksInfo.MakeSocks4Request(AIOHandler: TIdIOHandler; const AHost: string; + const APort: TIdPort; const ARequest : Byte); +var + LIpAddr: String; + LBufferingStarted: Boolean; +begin + LBufferingStarted := not AIOHandler.WriteBufferingActive; + if LBufferingStarted then begin + AIOHandler.WriteBufferOpen; + end; + try + AIOHandler.Write(Byte(4)); // Version + AIOHandler.Write(ARequest); // Opcode + AIOHandler.Write(Word(APort)); // Port + + if Version = svSocks4A then begin + LIpAddr := '0.0.0.1'; {Do not Localize} + end else begin + LIpAddr := GStack.ResolveHost(AHost,Id_IPv4); + end; + + AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP + AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP + AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP + AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP + + AIOHandler.Write(Username); + AIOHandler.Write(Byte(0));// Username + + if Version = svSocks4A then begin + AIOHandler.Write(AHost); + AIOHandler.Write(Byte(0));// Host + end; + + if LBufferingStarted then begin + AIOHandler.WriteBufferClose; //flush everything + end; + except + if LBufferingStarted then begin + AIOHandler.WriteBufferCancel; //cancel everything + end; + raise; + end; +end; + +procedure TIdSocksInfo.MakeSocks4Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort); +var + LResponse: TIdBytes; +begin + MakeSocks4Request(AIOHandler, AHost, APort,$01); //connect + AIOHandler.ReadBytes(LResponse, 8, False); + case LResponse[1] of // OpCode + 90: ;// request granted, do nothing + 91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed); + 92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed); + 93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed); + else raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; +end; + +procedure TIdSocksInfo.MakeSocks5Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte; var VBuf : TIdBytes; var VLen : Integer); +var + LIP : TIdIPAddress; + LAddr: TIdBytes; +begin + // Connection process + VBuf[0] := $5; // socks version + VBuf[1] := ARequest; //request method + VBuf[2] := $0; // reserved + + // address type: IP V4 address: X'01' {Do not Localize} + // DOMAINNAME: X'03' {Do not Localize} + // IP V6 address: X'04' {Do not Localize} + + LIP := TIdIPAddress.MakeAddressObject(AHost); + if Assigned(LIP) then + begin + try + if LIP.AddrType = Id_IPv6 then begin + VBuf[3] := $04; //IPv6 address + end else begin + VBuf[3] := $01; //IPv4 address + end; + LAddr := LIP.HToNBytes; + CopyTIdBytes(LAddr, 0, VBuf, 4, Length(LAddr)); + VLen := 4 + Length(LAddr); + finally + FreeAndNil(LIP); + end; + end else + begin + LAddr := ToBytes(AHost); + VBuf[3] := $3; // host name + VBuf[4] := IndyMin(Length(LAddr), 255); + if VBuf[4] > 0 then begin + CopyTIdBytes(LAddr, 0, VBuf, 5, VBuf[4]); + end; + VLen := 5 + VBuf[4]; + end; + + // port + + CopyTIdUInt16(GStack.HostToNetwork(APort), VBuf, VLen); + VLen := VLen + 2; +end; + +procedure TIdSocksInfo.MakeSocks5Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +var + Lpos: Integer; + LBuf: TIdBytes; +begin + AuthenticateSocks5Connection(AIOHandler); + SetLength(LBuf, 255); + MakeSocks5Request(AIOHandler, AHost, APort, $01, LBuf, Lpos); + + LBuf := ToBytes(LBuf, Lpos); + AIOHandler.WriteDirect(LBuf); // send the connection packet + try + AIOHandler.ReadBytes(LBuf, 5, False); // Socks server replies on connect, this is the first part + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + + case LBuf[1] of + 0: ;// success, do nothing + 1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError); + 2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError); + 3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError); + 4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError); + 5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError); + 6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError); + 7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError); + 8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError); + else + raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + + // type of destination address is domain name + case LBuf[3] of + // IP V4 + 1: Lpos := 4 + 2; // 4 is for address and 2 is for port length + // FQDN + 3: Lpos := LBuf[4] + 2; // 2 is for port length + // IP V6 + 4: Lpos := 16 + 2; // 16 is for address and 2 is for port length + end; + + try + // Socks server replies on connect, this is the second part + // RLebeau: why -1? + AIOHandler.ReadBytes(LBuf, Lpos-1, False); // just write it over the first part for now + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; +end; + +procedure TIdSocksInfo.MakeSocks4Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort); +var + LResponse: TIdBytes; + LClient: TIdTcpClient; +begin + LClient := TIdTCPClient.Create(nil); + try +// SetLength(LResponse, 255); + SetLength(LResponse, 8); + TIdIOHandlerSocket(AIOHandler).TransparentProxy := nil; + LClient.IOHandler := AIOHandler; + LClient.Host := Host; + LClient.Port := Port; + LClient.Connect; + TIdIOHandlerSocket(AIOHandler).TransparentProxy := Self; + MakeSocks4Request(AIOHandler, AHost, APort, $02); //bind + AIOHandler.ReadBytes(LResponse, 2, False); + case LResponse[1] of // OpCode + 90: ;// request granted, do nothing + 91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed); + 92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed); + 93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed); + else raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + + try + // Socks server replies on connect, this is the second part + AIOHandler.ReadBytes(LResponse, 6, False); //overwrite the first part for now + TIdIOHandlerSocket(AIOHandler).Binding.SetBinding(BytesToIPv4Str(LResponse, 2), LResponse[0]*256+LResponse[1]); + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + finally + LClient.IOHandler := nil; + FreeAndNil(LClient); + end; +end; + +procedure TIdSocksInfo.MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + case Version of + svSocks4, svSocks4A: MakeSocks4Connection(AIOHandler, AHost, APort); + svSocks5: MakeSocks5Connection(AIOHandler, AHost, APort); + end; +end; + +function TIdSocksInfo.GetEnabled: Boolean; +Begin + Result := Version in [svSocks4, svSocks4A, svSocks5]; +End;// + +procedure TIdSocksInfo.InitComponent; +begin + inherited InitComponent; + Authentication := ID_SOCKS_AUTH; + Version := ID_SOCKS_VER; + Port := IdPORT_SOCKS; + FIPVersion := ID_DEFAULT_IP_VERSION; + FUDPSocksAssociation := TIdIOHandlerStack.Create; +end; + +procedure TIdSocksInfo.AuthenticateSocks5Connection( + AIOHandler: TIdIOHandler); +var + Lpos: Integer; + LBuf, + LUsername, + LPassword : TIdBytes; + LRequestedAuthMethod, + LServerAuthMethod, + LUsernameLen, + LPasswordLen : Byte; +begin + // keep the compiler happy + LUsername := nil; + LPassword := nil; + + SetLength(LBuf, 3); + + // defined in rfc 1928 + if Authentication = saNoAuthentication then begin + LBuf[2] := $0 // No authentication + end else begin + LBuf[2] := $2; // Username password authentication + end; + + LRequestedAuthMethod := LBuf[2]; + LBuf[0] := $5; // socks version + LBuf[1] := $1; // number of possible authentication methods + AIOHandler.WriteDirect(LBuf); + try + AIOHandler.ReadBytes(LBuf, 2, False); // Socks server sends the selected authentication method + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + + LServerAuthMethod := LBuf[1]; + if (LServerAuthMethod <> LRequestedAuthMethod) or (LServerAuthMethod = $FF) then begin + raise EIdSocksAuthMethodError.Create(RSSocksAuthMethodError); + end; + + // Authentication process + if Authentication = saUsernamePassword then begin + LUsername := ToBytes(Username); + LPassword := ToBytes(Password); + LUsernameLen := IndyMin(Length(LUsername), 255); + LPasswordLen := IndyMin(Length(LPassword), 255); + SetLength(LBuf, 3 + LUsernameLen + LPasswordLen); + LBuf[0] := 1; // version of subnegotiation + LBuf[1] := LUsernameLen; + Lpos := 2; + if LUsernameLen > 0 then begin + CopyTIdBytes(LUsername, 0, LBuf, Lpos, LUsernameLen); + Lpos := Lpos + LUsernameLen; + end; + LBuf[Lpos] := LPasswordLen; + Lpos := Lpos + 1; + if LPasswordLen > 0 then begin + CopyTIdBytes(LPassword, 0, LBuf, Lpos, LPasswordLen); + end; + + AIOHandler.WriteDirect(LBuf); // send the username and password + try + AIOHandler.ReadBytes(LBuf, 2, False); // Socks server sends the authentication status + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + + if LBuf[1] <> $0 then begin + raise EIdSocksAuthError.Create(RSSocksAuthError); + end; + end; +end; + +procedure TIdSocksInfo.MakeSocks5Bind(AIOHandler: TIdIOHandler; const AHost: string; + const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +var + Lpos: Integer; + LBuf: TIdBytes; + LClient: TIdTCPClient; + LType : Byte; + LAddress: TIdIPv6Address; + LIPVersion: TIdIPVersion; +begin + LClient := TIdTCPClient.Create(nil); + try + SetLength(LBuf, 255); + TIdIOHandlerSocket(AIOHandler).TransparentProxy := nil; + LClient.IOHandler := AIOHandler; + LClient.Host := Host; + LClient.IPVersion := IPVersion; + LClient.Port := Port; + LClient.Connect; + TIdIOHandlerSocket(AIOHandler).TransparentProxy := Self; + + AuthenticateSocks5Connection(AIOHandler); + // Bind process + MakeSocks5Request(AIOHandler, AHost, APort, $02, LBuf, LPos); //bind request + // + AIOHandler.Write(LBuf, LPos); // send the connection packet + try + AIOHandler.ReadBytes(LBuf, 4, False); // Socks server replies on connect, this is the first part + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + + case LBuf[1] of + 0: ;// success, do nothing + 1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError); + 2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError); + 3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError); + 4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError); + 5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError); + 6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError); + 7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError); + 8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError); + else + raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + LType := LBuf[3]; + // type of destination address is domain name + case LType of + // IP V4 + 1: Lpos := 4 + 2; // 4 is for address and 2 is for port length + // FQDN + 3: Lpos := LBuf[4] + 2; // 2 is for port length + // IP V6 + 4: LPos := 16 + 2; // 16 is for address and 2 is for port length + end; + try + // Socks server replies on connect, this is the second part + AIOHandler.ReadBytes(LBuf, Lpos, False); //overwrite the first part for now + case LType of + 1 : begin + //IPv4 + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf), LBuf[4]*256+LBuf[5], Id_IPv4); + end; + 3 : begin + LIPVersion := TIdIOHandlerSocket(AIOHandler).IPVersion; + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(GStack.ResolveHost(BytesToString(LBuf,0,LPos-2), LIPVersion), LBuf[4]*256+LBuf[5], LIPVersion); + end; + 4 : begin + BytesToIPv6(LBuf, LAddress); + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(IPv6AddressToStr(LAddress), LBuf[16]*256+LBuf[17], Id_IPv6); + end; + end; + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + finally + LClient.IOHandler := nil; + FreeAndNil(LClient); + end; +end; + +procedure TIdSocksInfo.Bind(AIOHandler: TIdIOHandler; const AHost: string; + const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + case Version of + svSocks4, svSocks4A: MakeSocks4Bind(AIOHandler, AHost, APort); + svSocks5: MakeSocks5Bind(AIOHandler, AHost, APort, AIPVersion); + end; +end; + +function TIdSocksInfo.Listen(AIOHandler: TIdIOHandler; + const ATimeOut: integer): boolean; +begin + Result := False; + case Version of + svSocks4, svSocks4A: Result := MakeSocks4Listen(AIOHandler, ATimeOut); + svSocks5: Result := MakeSocks5Listen(AIOHandler, ATimeOut); + end; +end; + +function TIdSocksInfo.MakeSocks5Listen(AIOHandler: TIdIOHandler; + const ATimeOut: integer): boolean; +var + Lpos: Integer; + LBuf: TIdBytes; + LType : Byte; + LAddress: TIdIPv6Address; + LIPVersion: TIdIPVersion; +begin + SetLength(LBuf, 255); + Result := TIdIOHandlerSocket(AIOHandler).Binding.Readable(ATimeOut); + if Result then begin + AIOHandler.ReadBytes(LBuf, 4, False); // Socks server replies on connect, this is the first part + + case LBuf[1] of + 0: ;// success, do nothing + 1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError); + 2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError); + 3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError); + 4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError); + 5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError); + 6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError); + 7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError); + 8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError); + else + raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + LType := LBuf[3]; + // type of destination address is domain name + case LType of + // IP V4 + 1: Lpos := 4 + 2; // 4 is for address and 2 is for port length + // FQDN + 3: Lpos := LBuf[4] + 2; // 2 is for port length + // IP V6 - 4: + else + Lpos := 16 + 2; // 16 is for address and 2 is for port length + end; + // Socks server replies on connect, this is the second part + AIOHandler.ReadBytes(LBuf, Lpos, False); // just write it over the first part for now + case LType of + 1 : begin + //IPv4 + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf), LBuf[4]*256+LBuf[5], Id_IPv4); + end; + 3 : begin + //FQN + LIPVersion := TIdIOHandlerSocket(AIOHandler).IPVersion; + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(GStack.ResolveHost(BytesToString(LBuf,0,LPos-2), LIPVersion), LBuf[4]*256+LBuf[5], LIPVersion); + end; + else begin + //IPv6 + BytesToIPv6(LBuf, LAddress); + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(IPv6AddressToStr(LAddress), LBuf[16]*256+LBuf[17], Id_IPv6); + end; + end; + end; +end; + +function TIdSocksInfo.MakeSocks4Listen(AIOHandler: TIdIOHandler; + const ATimeOut: integer): boolean; +var + LBuf: TIdBytes; +begin + SetLength(LBuf, 6); + Result := TIdIOHandlerSocket(AIOHandler).Binding.Readable(ATimeOut); + if Result then begin + AIOHandler.ReadBytes(LBuf, 2, False); // Socks server replies on connect, this is the first part + + case LBuf[1] of // OpCode + 90: ;// request granted, do nothing + 91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed); + 92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed); + 93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed); + else raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + + // Socks server replies on connect, this is the second part + AIOHandler.ReadBytes(LBuf, 6, False); // just write it over the first part for now + TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf, 2), LBuf[0]*256+LBuf[1]); + end; +end; + +procedure TIdSocksInfo.CloseSocks5UDPAssociation; +begin + if Assigned(FUDPSocksAssociation) then begin + FUDPSocksAssociation.Close; + end; +end; + +procedure TIdSocksInfo.MakeSocks5UDPAssociation(AHandle: TIdSocketHandle); +var + Lpos: Integer; + LBuf: TIdBytes; + LIPVersion : TIdIPVersion; +begin + LIPVersion := Self.IPVersion; + FUDPSocksAssociation.Host := Self.Host; + FUDPSocksAssociation.Port := Self.Port; + FUDPSocksAssociation.IPVersion := LIPVersion; + FUDPSocksAssociation.Open; + try + SetLength(LBuf, 255); + AuthenticateSocks5Connection(FUDPSocksAssociation); + // Associate process + //For SOCKS5 Associate, the IP address and port is the client's IP address and port which may + //not be known + if LIPVersion = Id_IPv4 then begin + MakeSocks5Request(FUDPSocksAssociation, '0.0.0.0', 0, $03, LBuf, LPos); //associate request + end else begin + MakeSocks5Request(FUDPSocksAssociation, '::0', 0, $03, LBuf, LPos); //associate request + end; + // + FUDPSocksAssociation.Write(LBuf, LPos); // send the connection packet + try + FUDPSocksAssociation.ReadBytes(LBuf, 2, False); // Socks server replies on connect, this is the first part )VER and RSP + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + + case LBuf[1] of + 0: ;// success, do nothing + 1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError); + 2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError); + 3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError); + 4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError); + 5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError); + 6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError); + 7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError); + 8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError); + else + raise EIdSocksUnknownError.Create(RSSocksUnknownError); + end; + FUDPSocksAssociation.ReadBytes(LBuf, 2, False); //Now get RSVD and ATYPE feilds + // type of destination address is domain name + case LBuf[1] of + // IP V4 + 1: begin + Lpos := 4 + 2; // 4 is for address and 2 is for port length + LIPVersion := Id_IPv4; + end; + // FQDN + 3: Lpos := LBuf[4] + 2; // 2 is for port length + // IP V6 + 4: begin + LPos := 16 + 2; // 16 is for address and 2 is for port length + LIPVersion := Id_IPv6; + end; + end; + try + // Socks server replies on connect, this is the second part + FUDPSocksAssociation.ReadBytes(LBuf, Lpos, False); //overwrite the first part for now + AHandle.SetPeer( (FUDPSocksAssociation as TIdIOHandlerStack).Binding.PeerIP ,LBuf[4]*256+LBuf[5],LIPVersion); + AHandle.Connect; + except + IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError)); + end; + except + FUDPSocksAssociation.Close; + raise; + end; +end; + +procedure TIdSocksInfo.CloseUDP(AHandle: TIdSocketHandle); +begin + case Version of + svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported); + svSocks5: CloseSocks5UDPAssociation; + end; +end; + +procedure TIdSocksInfo.OpenUDP(AHandle: TIdSocketHandle; + const AHost: string=''; const APort: TIdPort=0; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + case Version of + svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported); + svSocks5: MakeSocks5UDPAssociation(AHandle); + end; +end; + +function TIdSocksInfo.DisasmUDPReplyPacket(const APacket : TIdBytes; + var VHost : String; var VPort : TIdPort; var VIPVersion: TIdIPVersion): TIdBytes; +{ + + + +----+------+------+----------+----------+----------+ + |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | + +----+------+------+----------+----------+----------+ + | 2 | 1 | 1 | Variable | 2 | Variable | + +----+------+------+----------+----------+----------+ + 01 2 3 + The fields in the UDP request header are: + + o RSV Reserved X'0000' + o FRAG Current fragment number + o ATYP address type of following addresses: + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o DST.ADDR desired destination address + o DST.PORT desired destination port + o DATA user data +} +var + LLen : Integer; + LIP6 : TIdIPv6Address; + i : Integer; +begin + if Length(APacket) < 5 then begin + Exit; + end; + // type of destination address is domain name + case APacket[3] of + // IP V4 + 1: begin + LLen := 4 + 4; //4 IPv4 address len, 4- 2 reserved, 1 frag, 1 atype + VHost := BytesToIPv4Str(APacket, 4); + VIPVersion := Id_IPv4; + end; + // FQDN + 3: begin + LLen := APacket[4] +4; // 2 is for port length, 4 - 2 reserved, 1 frag, 1 atype + if Length(APacket)< (5+LLen) then begin + Exit; + end; + VHost := BytesToString(APacket, 5, APacket[4]); + // VIPVersion is pre-initialized by the receiving socket before DisasmUDPReplyPacket() is called + end; + // IP V6 - 4: + else begin + LLen := 16 + 4; // 16 is for address, 2 is for port length, 4 - 2 reserved, 1 frag, 1 atype + BytesToIPv6(APacket, LIP6, 5); + for i := 0 to 7 do begin + LIP6[i] := GStack.NetworkToHost(LIP6[i]); + end; + VHost := IPv6AddressToStr(LIP6); + VIPVersion := Id_IPv6; + end; + end; + VPort := APacket[LLen]*256 + APacket[LLen+1]; + LLen := LLen + 2; + SetLength(Result, Length(APacket)-LLen); + CopyTIdBytes(APacket, LLen, Result, 0, Length(APacket)-LLen); +end; + +function TIdSocksInfo.MakeUDPRequestPacket(const AData: TIdBytes; + const AHost : String; const APort : TIdPort) : TIdBytes; +{ + + + +----+------+------+----------+----------+----------+ + |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | + +----+------+------+----------+----------+----------+ + | 2 | 1 | 1 | Variable | 2 | Variable | + +----+------+------+----------+----------+----------+ + 01 2 3 + The fields in the UDP request header are: + + o RSV Reserved X'0000' + o FRAG Current fragment number + o ATYP address type of following addresses: + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o DST.ADDR desired destination address + o DST.PORT desired destination port + o DATA user data +} +var + LLen : Integer; + LIP : TIdIPAddress; + LAddr: TIdBytes; +begin + SetLength(Result, 1024); + Result[0] := 0; + Result[1] := 0; + Result[2] := 0; //no fragmentation - too lazy to implement it + + // address type: IP V4 address: X'01' {Do not Localize} + // DOMAINNAME: X'03' {Do not Localize} + // IP V6 address: X'04' {Do not Localize} + + LIP := TIdIPAddress.MakeAddressObject(AHost); + if Assigned(LIP) then + begin + try + if LIP.AddrType = Id_IPv6 then begin + Result[3] := $04; //IPv6 address + end else begin + Result[3] := $01; //IPv4 address + end; + LLen := 4; + LAddr := LIP.HToNBytes; + CopyTIdBytes(LAddr, 0, Result, 4, Length(LAddr)); + LLen := LLen + Length(LAddr); + finally + FreeAndNil(LIP); + end; + end else + begin + LAddr := ToBytes(AHost); + Result[3] := $3; // host name + Result[4] := IndyMin(Length(LAddr), 255); + if Result[4] > 0 then begin + CopyTIdBytes(LAddr, 0, Result, 5, Result[4]); + end; + LLen := 5 + Result[4]; + end; + + // port + CopyTIdUInt16(GStack.HostToNetwork(APort), Result, LLen); + LLen := LLen + 2; + + //now do the rest of the packet + SetLength(Result, LLen + Length(AData)); + CopyTIdBytes(AData, 0, Result, LLen, Length(AData)); +end; + +function TIdSocksInfo.RecvFromUDP(AHandle: TIdSocketHandle; + var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort; + var VIPVersion: TIdIPVersion; AMSec: Integer = IdTimeoutDefault): Integer; +var + LBuf : TIdBytes; +begin + case Version of + svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported); + end; + SetLength(LBuf, Length(ABuffer)+200); + + if not AHandle.Readable(AMSec) then begin + Result := 0; + VPeerIP := ''; {Do not Localize} + VPeerPort := 0; + VIPVersion := ID_DEFAULT_IP_VERSION; + Exit; + end; + Result := AHandle.RecvFrom(LBuf, VPeerIP, VPeerPort, VIPVersion); + SetLength(LBuf, Result); + LBuf := DisasmUDPReplyPacket(LBuf, VPeerIP, VPeerPort, VIPVersion); + Result := Length(LBuf); + CopyTIdBytes(LBuf, 0, ABuffer, 0, Result); +end; + +procedure TIdSocksInfo.SendToUDP(AHandle: TIdSocketHandle; const AHost: string; + const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); +var + LBuf : TIdBytes; +begin + case Version of + svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported); + end; + LBuf := MakeUDPRequestPacket(ABuffer, AHost, APort); + AHandle.Send(LBuf, 0); +end; + +destructor TIdSocksInfo.Destroy; +begin + FreeAndNil(FUDPSocksAssociation); + inherited Destroy; +end; + +end. diff --git a/indy/Core/IdSymbolDeprecatedOff.inc b/indy/Core/IdSymbolDeprecatedOff.inc new file mode 100644 index 0000000..130976b --- /dev/null +++ b/indy/Core/IdSymbolDeprecatedOff.inc @@ -0,0 +1,3 @@ +{$IFDEF HAS_DEPRECATED} + {$WARN SYMBOL_DEPRECATED OFF} +{$ENDIF} diff --git a/indy/Core/IdSymbolDeprecatedOn.inc b/indy/Core/IdSymbolDeprecatedOn.inc new file mode 100644 index 0000000..5969193 --- /dev/null +++ b/indy/Core/IdSymbolDeprecatedOn.inc @@ -0,0 +1,8 @@ +{$IFDEF HAS_DEPRECATED} + {$IFDEF HAS_DIRECTIVE_WARN_DEFAULT} + {$WARN SYMBOL_DEPRECATED DEFAULT} + {$ELSE} + {$WARN SYMBOL_DEPRECATED ON} + {$ENDIF} +{$ENDIF} + diff --git a/indy/Core/IdSymbolPlatformOff.inc b/indy/Core/IdSymbolPlatformOff.inc new file mode 100644 index 0000000..37b2277 --- /dev/null +++ b/indy/Core/IdSymbolPlatformOff.inc @@ -0,0 +1,3 @@ +{$IFDEF HAS_SYMBOL_PLATFORM} + {$WARN SYMBOL_PLATFORM OFF} +{$ENDIF} diff --git a/indy/Core/IdSymbolPlatformOn.inc b/indy/Core/IdSymbolPlatformOn.inc new file mode 100644 index 0000000..aa65208 --- /dev/null +++ b/indy/Core/IdSymbolPlatformOn.inc @@ -0,0 +1,8 @@ +{$IFDEF HAS_SYMBOL_PLATFORM} + {$IFDEF HAS_DIRECTIVE_WARN_DEFAULT} + {$WARN SYMBOL_PLATFORM DEFAULT} + {$ELSE} + {$WARN SYMBOL_PLATFORM ON} + {$ENDIF} +{$ENDIF} + diff --git a/indy/Core/IdSync.pas b/indy/Core/IdSync.pas new file mode 100644 index 0000000..7e36b83 --- /dev/null +++ b/indy/Core/IdSync.pas @@ -0,0 +1,546 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.13 03/16/05 11:15:42 AM JSouthwell + Named the IdNotify thread for simpler debugging. + + Rev 1.12 2004.04.13 10:22:52 PM czhower + Changed procedure to class method. + + Rev 1.11 4/12/2004 11:44:36 AM BGooijen + fix + + Rev 1.10 4/12/2004 11:36:56 AM BGooijen + NotifyThread can be cleaned up with procedure now + + Rev 1.9 2004.03.11 10:14:46 AM czhower + Improper cast fixed. + + Rev 1.8 2004.02.29 8:23:16 PM czhower + Fixed visibility mismatch. + + Rev 1.7 2004.02.25 10:11:42 AM czhower + Fixed visibility in notify + + Rev 1.6 2004.02.03 4:16:54 PM czhower + For unit name changes. + + Rev 1.5 1/1/2004 11:56:10 PM PIonescu + Fix for TIdNotifyMethod's constructor + + Rev 1.4 2003.12.31 7:33:20 PM czhower + Constructor bug fix. + + Rev 1.3 5/12/2003 9:17:42 AM GGrieve + compile fix + + Rev 1.2 2003.09.18 5:42:14 PM czhower + Removed TIdThreadBase + + Rev 1.1 05.6.2003 . 11:30:12 DBondzhev + Mem leak fix for notifiers created in main thread. Also WaitFor for waiting + notification to be executed. + + Rev 1.0 11/13/2002 09:00:10 AM JPMugaas +} + +unit IdSync; + +// Author: Chad Z. Hower - a.k.a. Kudzu + +interface + +{$i IdCompilerDefines.inc} + +{$UNDEF NotifyThreadNeeded} +{$UNDEF TNotify_InternalDoNotify_Needed} + +{$IFNDEF HAS_STATIC_TThread_Synchronize} + {$DEFINE NotifyThreadNeeded} +{$ENDIF} +{$IFNDEF HAS_STATIC_TThread_Queue} + {$DEFINE NotifyThreadNeeded} +{$ELSE} + {$IFNDEF USE_OBJECT_ARC} + {$DEFINE TNotify_InternalDoNotify_Needed} + {$ENDIF} +{$ENDIF} + +uses + Classes, + IdGlobal + {$IFDEF NotifyThreadNeeded} + , IdThread + {$ENDIF} + ; + +type + TIdSync = class(TObject) + protected + {$IFNDEF HAS_STATIC_TThread_Synchronize} + FThread: TIdThread; + {$ENDIF} + // + procedure DoSynchronize; virtual; abstract; + public + {$IFDEF HAS_STATIC_TThread_Synchronize} + constructor Create; virtual; + {$ELSE} + constructor Create; overload; virtual; + constructor Create(AThread: TIdThread); overload; virtual; + {$ENDIF} + procedure Synchronize; + class procedure SynchronizeMethod(AMethod: TThreadMethod); + // + {$IFNDEF HAS_STATIC_TThread_Synchronize} + property Thread: TIdThread read FThread; + {$ENDIF} + end; + + TIdNotify = class(TObject) + protected + FMainThreadUsesNotify: Boolean; + // + procedure DoNotify; virtual; abstract; + {$IFDEF TNotify_InternalDoNotify_Needed} + procedure InternalDoNotify; + {$ENDIF} + public + constructor Create; virtual; // here to make virtual + procedure Notify; + {$IFNDEF HAS_STATIC_TThread_Queue} + procedure WaitFor; {$IFDEF HAS_DEPRECATED}deprecated;{$ENDIF} + {$ENDIF} + class procedure NotifyMethod(AMethod: TThreadMethod); + // + property MainThreadUsesNotify: Boolean read FMainThreadUsesNotify write FMainThreadUsesNotify; // deprecated + end; + + TIdNotifyMethod = class(TIdNotify) + protected + FMethod: TThreadMethod; + // + procedure DoNotify; override; + public + constructor Create(AMethod: TThreadMethod); reintroduce; virtual; + end {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use TIdNotify.NotifyMethod()'{$ENDIF}{$ENDIF}; + +implementation + +uses + //facilitate inlining only. + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + {$ENDIF} + {$ENDIF} + {$IFDEF NotifyThreadNeeded} + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + {$ENDIF} + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + SysUtils + {$IFNDEF NotifyThreadNeeded} + , IdThread + {$ENDIF} + ; + +// TODO: there is a bug in FireMonkey prior to XE7 where FMX.TApplication does +// not assign a handler to the Classes.WakeMainThread callback (see QC #123579). +// Without that, TThread.Synchronize() and TThread.Queue() will not do anything +// if the main message queue is idle at the moment they are called!!! If the +// main thread *happens* to receive a message at a later time, say from UI +// activity, then they will be processed. But for a background process, we +// cannot rely on that. Need an alternative solution for those versions of +// FireMonkey... + +{$IFDEF NotifyThreadNeeded} +type + // This is done with a NotifyThread instead of PostMessage because starting + // with D6/Kylix Borland radically modified the mechanisms for .Synchronize. + // This is a bit more code in the end, but its source compatible and does not + // rely on Indy directly accessing any OS APIs and performance is still more + // than acceptable, especially considering Notifications are low priority. + + {$IFDEF HAS_GENERICS_TThreadList} + TIdNotifyThreadList = TThreadList; + TIdNotifyList = TList; + {$ELSE} + // TODO: flesh out to match TThreadList and TList for non-Generics compilers... + TIdNotifyThreadList = TThreadList; + TIdNotifyList = TList; + {$ENDIF} + + TIdNotifyThread = class(TIdThread) + protected + FEvent: TIdLocalEvent; + FNotifications: TIdNotifyThreadList; + public + procedure AddNotification(ASync: TIdNotify); + constructor Create; reintroduce; + destructor Destroy; override; + class procedure FreeThread; + procedure Run; override; + end; + +var + GNotifyThread: TIdNotifyThread = nil; + +procedure CreateNotifyThread; +begin + // TODO: this function has a race condition if it is called by multiple + // threads at the same time and GNotifyThread has not been assigned yet! + // Need to use something like InterlockedCompareExchangeObj() so any + // duplicate threads can be freed... + { + Thread := TIdNotifyThread.Create(True); + if InterlockedCompareExchangeObj(GNotifyThread, Thread, nil) <> nil then begin + Thread.Free; + end else begin + Thread.Start; + end; + } + if GNotifyThread = nil then begin + GNotifyThread := TIdNotifyThread.Create; + end; +end; +{$ENDIF} + +{ TIdSync } + +{$IFNDEF HAS_STATIC_TThread_Synchronize} +constructor TIdSync.Create(AThread: TIdThread); +begin + inherited Create; + FThread := AThread; +end; +{$ENDIF} + +constructor TIdSync.Create; +begin + {$IFDEF HAS_STATIC_TThread_Synchronize} + inherited Create; + {$ELSE} + {$IFDEF DOTNET} + inherited Create; + CreateNotifyThread; + FThread := GNotifyThread; + {$ELSE} + CreateNotifyThread; + Create(GNotifyThread); + {$ENDIF} + {$ENDIF} +end; + +procedure DoThreadSync(AThread: TIdThread; SyncProc: TThreadMethod); +begin + { + if not Assigned(Classes.WakeMainThread) then + begin + // TODO: if WakeMainThread is not assigned, need to force a message into + // the main message queue so TApplication.Idle() will be called so it can + // call CheckSynchronize(): + // + // on Windows, call PostMessage() to post a WM_NULL message to the TApplication window... + // + // on OSX (and iOS?), call NSApp.sendEvent(???), but with what kind of event? + // + // on Android, what to do??? + + // We can't put the message in the queue before calling TThread.Synchronize(), + // as it might get processed before Synchronize() can queue the procedure. + // Might have to use TThread.Queue() instead and wait on a manual TEvent... + end else + begin + } + {$IFDEF HAS_STATIC_TThread_Synchronize} + TThread.Synchronize(AThread, SyncProc); + {$ELSE} + AThread.Synchronize(SyncProc); + {$ENDIF} + // end; +end; + +{$IFDEF HAS_STATIC_TThread_Queue} +procedure DoThreadQueue(QueueProc: TThreadMethod); +begin + { + if not Assigned(Classes.WakeMainThread) then + begin + // TODO: if WakeMainThread is not assigned, need to force a message into + // the main message queue so TApplication.Idle() will be called so it can + // call CheckSynchronize(): + // + // on Windows, call PostMessage() to post a WM_NULL message to the TApplication window... + // + // on OSX (and iOS?), call NSApp.sendEvent(???), but with what kind of event? + // + // on Android, what to do??? + + // We can't put the message in the queue before calling TThread.Queue(), + // as it might get processed before Queue() can queue the procedure. + // Might have to wait on a manual TEvent... + end else + begin + } + TThread.Queue(nil, QueueProc); + // end; +end; +{$ENDIF} + +procedure TIdSync.Synchronize; +begin + DoThreadSync( + {$IFDEF HAS_STATIC_TThread_Synchronize}nil{$ELSE}FThread{$ENDIF}, + DoSynchronize + ); +end; + +class procedure TIdSync.SynchronizeMethod(AMethod: TThreadMethod); +begin + {$IFDEF HAS_STATIC_TThread_Synchronize} + DoThreadSync(nil, AMethod); + {$ELSE} + CreateNotifyThread; + DoThreadSync(GNotifyThread, AMethod); + {$ENDIF} +end; + +{ TIdNotify } + +constructor TIdNotify.Create; +begin + inherited Create; +end; + +procedure TIdNotify.Notify; +begin + // Note: MainThreadUsesNotify only has meaning now when TThread.Queue() is + // not available, as it calls the specified method immediately if invoked + // in the main thread! To go back to the old behavior, we would have to + // re-enable use of TIdNotifyThread, which is another interface change... + if InMainThread and (not MainThreadUsesNotify) then begin + {$IFNDEF USE_OBJECT_ARC} + try + {$ENDIF} + DoNotify; + {$IFNDEF USE_OBJECT_ARC} + finally + Free; + end; + {$ENDIF} + end else begin + {$IFNDEF USE_OBJECT_ARC} + try + {$ENDIF} + {$IFDEF HAS_STATIC_TThread_Queue} + DoThreadQueue( + {$IFDEF TNotify_InternalDoNotify_Needed} + InternalDoNotify + {$ELSE} + DoNotify + {$ENDIF} + ); + {$ELSE} + CreateNotifyThread; + GNotifyThread.AddNotification(Self); + {$ENDIF} + {$IFNDEF USE_OBJECT_ARC} + except + Free; + raise; + end; + {$ENDIF} + end; +end; + +{$IFDEF TNotify_InternalDoNotify_Needed} +procedure TIdNotify.InternalDoNotify; +begin + try + DoNotify; + finally + Free; + end; +end; +{$ENDIF} + +class procedure TIdNotify.NotifyMethod(AMethod: TThreadMethod); +begin + {$IFDEF HAS_STATIC_TThread_Queue} + DoThreadQueue(AMethod); + {$ELSE} + {$I IdSymbolDeprecatedOff.inc} + TIdNotifyMethod.Create(AMethod).Notify; + {$I IdSymbolDeprecatedOn.inc} + {$ENDIF} +end; + +{$IFNDEF HAS_STATIC_TThread_Queue} +// RLebeau: this method does not make sense. The Self pointer is not +// guaranteed to remain valid while this method is running since the +// notify thread frees the object. Also, this makes the calling thread +// block, so TIdSync should be used instead... + +{$I IdDeprecatedImplBugOff.inc} +procedure TIdNotify.WaitFor; +{$I IdDeprecatedImplBugOn.inc} +var + LNotifyIndex: Integer; + LList: TIdNotifyList; +begin + repeat + LList := GNotifyThread.FNotifications.LockList; + try + LNotifyIndex := LList.IndexOf(Self); + finally + GNotifyThread.FNotifications.UnlockList; + end; + if LNotifyIndex = -1 then begin + Break; + end; + IndySleep(10); + until False; +end; + +{$ENDIF} + +{$IFDEF NotifyThreadNeeded} + +{ TIdNotifyThread } + +procedure TIdNotifyThread.AddNotification(ASync: TIdNotify); +begin + FNotifications.Add(ASync); + FEvent.SetEvent; +end; + +constructor TIdNotifyThread.Create; +begin + FEvent := TIdLocalEvent.Create; + FNotifications := TIdNotifyThreadList.Create; + // Must be before - Thread starts running when we call inherited + inherited Create(False, False, 'IdNotify'); +end; + +destructor TIdNotifyThread.Destroy; +var + {$IFNDEF USE_OBJECT_ARC} + LNotify: TIdNotify; + {$ENDIF} + LList: TIdNotifyList; +begin + // Free remaining Notifications if there is somthing that is still in + // the queue after thread was terminated + LList := FNotifications.LockList; + try + {$IFDEF USE_OBJECT_ARC} + LList.Clear; // Items are auto-freed + {$ELSE} + while LList.Count > 0 do begin + LNotify := {$IFDEF HAS_GENERICS_TList}LList.Items[0]{$ELSE}TIdNotify(LList.Items[0]){$ENDIF}; + LNotify.Free; + LList.Delete(0); + end; + {$ENDIF} + finally + FNotifications.UnlockList; + end; + FreeAndNil(FNotifications); + FreeAndNil(FEvent); + inherited Destroy; +end; + +class procedure TIdNotifyThread.FreeThread; +begin + if GNotifyThread <> nil then begin + GNotifyThread.Stop; + GNotifyThread.FEvent.SetEvent; + GNotifyThread.WaitFor; + // Instead of FreeOnTerminate so we can set the reference to nil + FreeAndNil(GNotifyThread); + end; +end; + +procedure TIdNotifyThread.Run; +// NOTE: Be VERY careful with making changes to this proc. It is VERY delicate and the order +// of execution is very important. Small changes can have drastic effects +var + LNotifications: TIdNotifyList; + LNotify: TIdNotify; +begin + FEvent.WaitForEver; + // If terminated while waiting on the event or during the loop + while not Stopped do begin + try + LNotifications := FNotifications.LockList; + try + if LNotifications.Count = 0 then begin + Break; + end; + LNotify := {$IFDEF HAS_GENERICS_TList}LNotifications.Items[0]{$ELSE}TIdNotify(LNotifications.Items[0]){$ENDIF}; + LNotifications.Delete(0); + finally + FNotifications.UnlockList; + end; + try + DoThreadSync(Self, LNotify.DoNotify); + finally + FreeAndNil(LNotify); + end; + except // Catch all exceptions especially these which are raised during the application close + end; + end; +end; + +{$ENDIF} // NotifyThreadNeeded + +{ TIdNotifyMethod } + +{$I IdDeprecatedImplBugOff.inc} +constructor TIdNotifyMethod.Create(AMethod: TThreadMethod); +{$I IdDeprecatedImplBugOn.inc} +begin + inherited Create; + FMethod := AMethod; +end; + +{$I IdDeprecatedImplBugOff.inc} +procedure TIdNotifyMethod.DoNotify; +{$I IdDeprecatedImplBugOn.inc} +begin + FMethod; +end; + +{$IFDEF NotifyThreadNeeded} +initialization +finalization + TIdNotifyThread.FreeThread; +{$ENDIF} + +end. + diff --git a/indy/Core/IdTCPClient.pas b/indy/Core/IdTCPClient.pas new file mode 100644 index 0000000..03ec2cd --- /dev/null +++ b/indy/Core/IdTCPClient.pas @@ -0,0 +1,544 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.38 1/15/05 2:14:58 PM RLebeau + Removed virtual specifier from SetConnectTimeout() and SetReadTimeout(), not + being used by any descendants. + + Rev 1.37 11/29/2004 11:49:24 PM JPMugaas + Fixes for compiler errors. + + Rev 1.36 11/29/04 10:38:58 AM RLebeau + Updated Connect() to release the IOHandler on error if implicitally created. + + Rev 1.35 11/28/04 2:28:22 PM RLebeau + Added 'const' to various property setter parameters. + + Removed redundant getter methods. + + Rev 1.34 11/27/2004 8:27:44 PM JPMugaas + Fix for compiler errors. + + Rev 1.33 11/26/04 3:46:10 PM RLebeau + Added support for BoundIP and BoundPort properties + + Rev 1.32 2004.11.05 10:58:34 PM czhower + Changed connect overloads for C#. + + Rev 1.31 8/8/04 12:32:08 AM RLebeau + Redeclared ReadTimeout and ConnectTimeout properties as public instead of + protected in TIdTCPClientCustom + + Rev 1.30 8/4/2004 5:37:34 AM DSiders + Changed camel-casing on ReadTimeout to be consistent with ConnectTimeout. + + Rev 1.29 8/3/04 11:17:30 AM RLebeau + Added support for ReadTimeout property + + Rev 1.28 8/2/04 5:50:58 PM RLebeau + Added support for ConnectTimeout property + + Rev 1.27 2004.03.06 10:40:28 PM czhower + Changed IOHandler management to fix bug in server shutdowns. + + Rev 1.26 2004.02.03 4:16:54 PM czhower + For unit name changes. + + Rev 1.25 1/8/2004 8:22:54 PM JPMugaas + SetIPVersion now virtual so I can override in TIdFTP. Other stuff may need + the override as well. + + Rev 1.24 1/2/2004 12:02:18 AM BGooijen + added OnBeforeBind/OnAfterBind + + Rev 1.23 12/31/2003 9:52:04 PM BGooijen + Added IPv6 support + + Rev 1.20 2003.10.14 1:27:00 PM czhower + Uupdates + Intercept support + + Rev 1.19 2003.10.01 9:11:26 PM czhower + .Net + + Rev 1.18 2003.10.01 2:30:42 PM czhower + .Net + + Rev 1.17 2003.10.01 11:16:36 AM czhower + .Net + + Rev 1.16 2003.09.30 1:23:06 PM czhower + Stack split for DotNet + + Rev 1.15 2003.09.18 2:59:46 PM czhower + Modified port and host overrides to only override if values exist. + + Rev 1.14 6/3/2003 11:48:32 PM BGooijen + Undid change from version 1.12, is now fixed in iohandlersocket + + Rev 1.13 2003.06.03 7:27:56 PM czhower + Added overloaded Connect method + + Rev 1.12 5/23/2003 6:45:32 PM BGooijen + ClosedGracefully is now set if Connect failes. + + Rev 1.11 2003.04.10 8:05:34 PM czhower + removed unneeded self. reference + + Rev 1.10 4/7/2003 06:58:32 AM JPMugaas + Implicit IOHandler now created in virtual method + + function TIdTCPClientCustom.MakeImplicitClientHandler: TIdIOHandler; + + Rev 1.9 3/17/2003 9:40:16 PM BGooijen + Host and Port were not properly synchronised with the IOHandler, fixed that + + Rev 1.8 3/5/2003 11:05:24 PM BGooijen + Intercept + + Rev 1.7 2003.02.25 1:36:16 AM czhower + + Rev 1.6 12-14-2002 22:52:34 BGooijen + now also saves host and port settings when an explicit iohandler is used. the + host and port settings are copied to the iohandler if the iohandler doesn't + have them specified. + + Rev 1.5 12-14-2002 22:38:26 BGooijen + The host and port settings were lost when the implicit iohandler was created + in .Connect, fixed that. + + Rev 1.4 2002.12.07 12:26:12 AM czhower + + Rev 1.2 12/6/2002 02:11:42 PM JPMugaas + Protected Port and Host properties added to TCPClient because those are + needed by protocol implementations. Socket property added to TCPConnection. + + Rev 1.1 6/12/2002 4:08:34 PM SGrobety + + Rev 1.0 11/13/2002 09:00:26 AM JPMugaas +} + +unit IdTCPClient; + +{$i IdCompilerDefines.inc} + +interface + +uses + Classes, + IdGlobal, IdExceptionCore, IdIOHandler, IdTCPConnection; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Idtcpclient@TIdTCPClientCustom@SetPortA$qqrxus"="@Idtcpclient@TIdTCPClientCustom@SetPort$qqrxus"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Idtcpclient@TIdTCPClientCustom@SetPortW$qqrxus"="@Idtcpclient@TIdTCPClientCustom@SetPort$qqrxus"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +type + + TIdTCPClientCustom = class(TIdTCPConnection) + protected + FBoundIP: String; + FBoundPort: TIdPort; + FBoundPortMax: TIdPort; + FBoundPortMin: TIdPort; + FConnectTimeout: Integer; + FDestination: string; + FHost: string; + FIPVersion: TIdIPVersion; + FOnConnected: TNotifyEvent; + FPassword: string; + FPort: TIdPort; + FReadTimeout: Integer; + FUsername: string; + FReuseSocket: TIdReuseSocket; + FUseNagle: Boolean; + // + FOnBeforeBind: TNotifyEvent; + FOnAfterBind: TNotifyEvent; + FOnSocketAllocated: TNotifyEvent; + // + procedure DoOnConnected; virtual; + function MakeImplicitClientHandler: TIdIOHandler; virtual; + // + procedure SetConnectTimeout(const AValue: Integer); + procedure SetReadTimeout(const AValue: Integer); + procedure SetReuseSocket(const AValue: TIdReuseSocket); + procedure SetUseNagle(const AValue: Boolean); + procedure SetBoundIP(const AValue: String); + procedure SetBoundPort(const AValue: TIdPort); + procedure SetBoundPortMax(const AValue: TIdPort); + procedure SetBoundPortMin(const AValue: TIdPort); + procedure SetHost(const AValue: string); virtual; + procedure SetPort(const AValue: TIdPort); virtual; + procedure SetIPVersion(const AValue: TIdIPVersion); virtual; + // + procedure SetOnBeforeBind(const AValue: TNotifyEvent); + procedure SetOnAfterBind(const AValue: TNotifyEvent); + procedure SetOnSocketAllocated(const AValue: TNotifyEvent); + // + procedure SetIOHandler(AValue: TIdIOHandler); override; + procedure InitComponent; override; + // + function GetReadTimeout: Integer; + function GetReuseSocket: TIdReuseSocket; + function GetUseNagle: Boolean; + // + property Host: string read FHost write SetHost; + property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion; + property Password: string read FPassword write FPassword; + property Port: TIdPort read FPort write SetPort; + property Username: string read FUsername write FUsername; + public + procedure Connect; overload; virtual; + // This is overridden and not as default params so that descendants + // do not have to worry about the arguments. + // Also has been split further to allow usage from C# as it does not have optional + // params + procedure Connect(const AHost: string); overload; + procedure Connect(const AHost: string; const APort: TIdPort); overload; + function ConnectAndGetAll: string; virtual; + // + property BoundIP: string read FBoundIP write SetBoundIP; + property BoundPort: TIdPort read FBoundPort write SetBoundPort default DEF_PORT_ANY; + property BoundPortMax: TIdPort read FBoundPortMax write SetBoundPortMax default DEF_PORT_ANY; + property BoundPortMin: TIdPort read FBoundPortMin write SetBoundPortMin default DEF_PORT_ANY; + // + property ConnectTimeout: Integer read FConnectTimeout write SetConnectTimeout; + property ReadTimeout: Integer read GetReadTimeout write SetReadTimeout; + property ReuseSocket: TIdReuseSocket read GetReuseSocket write SetReuseSocket default rsOSDependent; + property UseNagle: Boolean read GetUseNagle write SetUseNagle default True; + // + property OnBeforeBind: TNotifyEvent read FOnBeforeBind write SetOnBeforeBind; + property OnAfterBind: TNotifyEvent read FOnAfterBind write SetOnAfterBind; + property OnSocketAllocated: TNotifyEvent read FOnSocketAllocated write SetOnSocketAllocated; + // + published + property OnConnected: TNotifyEvent read FOnConnected write FOnConnected; + end; + + TIdTCPClient = class(TIdTCPClientCustom) + published + property BoundIP; + property BoundPort; + property ConnectTimeout; + property Host; + property IPVersion; + property Port; + property ReadTimeout; + property ReuseSocket; + property UseNagle; + + property OnBeforeBind; + property OnAfterBind; + property OnSocketAllocated; + end; + //Temp IFDEF till we change aliaser + // Temp - reversed it for code freeze - will rereverse later. + +implementation + +uses + IdComponent, IdResourceStringsCore, IdIOHandlerSocket; + +{ TIdTCPClientCustom } + +procedure TIdTCPClientCustom.InitComponent; +begin + inherited InitComponent; + FReadTimeOut := IdTimeoutDefault; + FBoundPort := DEF_PORT_ANY; + FBoundPortMin := DEF_PORT_ANY; + FBoundPortMax := DEF_PORT_ANY; + FUseNagle := True; +end; + +procedure TIdTCPClientCustom.Connect; +begin + if Connected then begin + raise EIdAlreadyConnected.Create(RSAlreadyConnected); + end; + + if Host = '' then begin + raise EIdHostRequired.Create('A Host is required'); {do not localize} + end; + if Port = 0 then begin + raise EIdPortRequired.Create('A Port is required'); {do not localize} + end; + + if IOHandler = nil then begin + IOHandler := MakeImplicitClientHandler; + + // TODO: always assign the OnStatus event even if the IOHandler is not implicit? + IOHandler.OnStatus := OnStatus; + + ManagedIOHandler := True; + end; + + try + // Bypass GetDestination + if FDestination <> '' then begin + IOHandler.Destination := FDestination; + end; + +{BGO: not any more, TIdTCPClientCustom has precedence now (for port protocols, and things like that) + // We retain the settings that are in here (filled in by the user) + // we only do this when the iohandler has no settings, + // because the iohandler has precedence + if (IOHandler.Port = 0) and (IOHandler.Host = '') then begin + IOHandler.Port := FPort; + IOHandler.Host := FHost; + end; +} + + IOHandler.Port := FPort; //BGO: just to make sure + IOHandler.Host := FHost; + IOHandler.ConnectTimeout := FConnectTimeout; + IOHandler.ReadTimeout := FReadTimeout; + + if Socket <> nil then begin + Socket.BoundIP := FBoundIP; + Socket.BoundPort := FBoundPort; + Socket.BoundPortMin := FBoundPortMin; + Socket.BoundPortMax := FBoundPortMax; + Socket.IPVersion := FIPVersion; + Socket.ReuseSocket := FReuseSocket; + Socket.UseNagle := FUseNagle; + Socket.OnBeforeBind := FOnBeforeBind; + Socket.OnAfterBind := FOnAfterBind; + Socket.OnSocketAllocated := FOnSocketAllocated; + end; + + IOHandler.Open; + if IOHandler.Intercept <> nil then begin + IOHandler.Intercept.Connect(Self); + end; + + DoStatus(hsConnected, [Host]); + DoOnConnected; + except + if IOHandler <> nil then begin + IOHandler.Close; + if ManagedIOHandler then begin + IOHandler := nil; // RLebeau - SetIOHandler() will free the IOHandler + end; + end; + raise; + end; +end; + +function TIdTCPClientCustom.ConnectAndGetAll: string; +begin + Connect; try + Result := IOHandler.AllData; + finally Disconnect; end; +end; + +procedure TIdTCPClientCustom.DoOnConnected; +begin + if Assigned(OnConnected) then begin + OnConnected(Self); + end; +end; + +procedure TIdTCPClientCustom.SetConnectTimeout(const AValue: Integer); +begin + FConnectTimeout := AValue; + if IOHandler <> nil then begin + IOHandler.ConnectTimeout := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetReadTimeout(const AValue: Integer); +begin + FReadTimeout := AValue; + if IOHandler <> nil then begin + IOHandler.ReadTimeout := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetReuseSocket(const AValue: TIdReuseSocket); +begin + FReuseSocket := AValue; + if Socket <> nil then begin + Socket.ReuseSocket := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetUseNagle(const AValue: Boolean); +begin + FUseNagle := AValue; + if Socket <> nil then begin + Socket.UseNagle := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetBoundIP(const AValue: String); +begin + FBoundIP := AValue; + if Socket <> nil then begin + Socket.BoundIP := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetBoundPort(const AValue: TIdPort); +begin + FBoundPort := AValue; + if Socket <> nil then begin + Socket.BoundPort := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetBoundPortMax(const AValue: TIdPort); +begin + FBoundPortMax := AValue; + if Socket <> nil then begin + Socket.BoundPortMax := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetBoundPortMin(const AValue: TIdPort); +begin + FBoundPortMin := AValue; + if Socket <> nil then begin + Socket.BoundPortMin := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetHost(const AValue: string); +begin + FHost := AValue; + if IOHandler <> nil then begin + IOHandler.Host := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetPort(const AValue: TIdPort); +begin + FPort := AValue; + if IOHandler <> nil then begin + IOHandler.Port := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetIPVersion(const AValue: TIdIPVersion); +begin + FIPVersion := AValue; + if Socket <> nil then begin + Socket.IPVersion := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetOnBeforeBind(const AValue: TNotifyEvent); +begin + FOnBeforeBind := AValue; + if Socket <> nil then begin + Socket.OnBeforeBind := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetOnAfterBind(const AValue: TNotifyEvent); +begin + FOnAfterBind := AValue; + if Socket <> nil then begin + Socket.OnAfterBind := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetOnSocketAllocated(const AValue: TNotifyEvent); +begin + FOnSocketAllocated := AValue; + if Socket <> nil then begin + Socket.OnSocketAllocated := AValue; + end; +end; + +procedure TIdTCPClientCustom.SetIOHandler(AValue: TIdIOHandler); +begin + inherited SetIOHandler(AValue); + // TIdTCPClientCustom overrides settings in iohandler to initialize + // protocol defaults. + if IOHandler <> nil then begin + IOHandler.Port := FPort; + IOHandler.Host := FHost; + IOHandler.ConnectTimeout := FConnectTimeout; + IOHandler.ReadTimeout := FReadTimeout; + end; + if Socket <> nil then begin + Socket.BoundIP := FBoundIP; + Socket.BoundPort := FBoundPort; + Socket.BoundPortMin := FBoundPortMin; + Socket.BoundPortMax := FBoundPortMax; + Socket.IPVersion := FIPVersion; + Socket.ReuseSocket := FReuseSocket; + Socket.UseNagle := FUseNagle; + + // TODO: use local event handlers that then trigger the user event handler if assigned + Socket.OnBeforeBind := FOnBeforeBind; + Socket.OnAfterBind := FOnAfterBind; + Socket.OnSocketAllocated := FOnSocketAllocated; + end; +end; + +function TIdTCPClientCustom.MakeImplicitClientHandler: TIdIOHandler; +begin + Result := TIdIOHandler.MakeDefaultIOHandler(Self); +end; + +procedure TIdTCPClientCustom.Connect(const AHost: string); +begin + Host := AHost; + Connect; +end; + +procedure TIdTCPClientCustom.Connect(const AHost: string; const APort: TIdPort); +begin + Host := AHost; + Port := APort; + Connect; +end; + +function TIdTCPClientCustom.GetReadTimeout: Integer; +begin + if IOHandler <> nil then begin + Result := IOHandler.ReadTimeout; + end else begin + Result := FReadTimeout; + end; +end; + +function TIdTCPClientCustom.GetReuseSocket: TIdReuseSocket; +begin + if Socket <> nil then begin + Result := Socket.ReuseSocket; + end else begin + Result := FReuseSocket; + end; +end; + +function TIdTCPClientCustom.GetUseNagle: Boolean; +begin + if Socket <> nil then begin + Result := Socket.UseNagle; + end else begin + Result := FUseNagle; + end; +end; + +end. diff --git a/indy/Core/IdTCPConnection.pas b/indy/Core/IdTCPConnection.pas new file mode 100644 index 0000000..f4f21c7 --- /dev/null +++ b/indy/Core/IdTCPConnection.pas @@ -0,0 +1,960 @@ +{ + $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$ + + +//TODO: Elim read/write methods - they are duped +//TODO: See second uses comment + + + Rev 1.68 3/7/2005 5:48:18 PM JPMugaas + Made a backdoor so we can adjust command output in specific ways. + + + Rev 1.67 1/15/2005 6:02:02 PM JPMugaas + These should compile again. + + + Rev 1.66 1/15/05 2:16:04 PM RLebeau + Misc. tweaks + + + Rev 1.65 12/21/04 3:20:54 AM RLebeau + Removed compiler warning + + + Rev 1.64 12/12/04 2:24:28 PM RLebeau + Updated WriteRFCStrings() to call new method in the IOHandler. + + + Rev 1.63 10/26/2004 8:43:02 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + + Rev 1.62 6/11/2004 8:48:36 AM DSiders + Added "Do not Localize" comments. + + + Rev 1.61 2004.06.07 1:34:20 PM czhower + OnWork fix now sends running total as it should. + + + Rev 1.60 2004.06.06 5:18:04 PM czhower + OnWork bug fix + + + Rev 1.59 2004.06.05 9:46:30 AM czhower + IOHandler OnWork fix + + + Rev 1.58 11/05/2004 17:13:32 HHariri + Fix brought from IW for overflow of DoWork + + + Rev 1.57 4/19/2004 9:50:08 AM BGooijen + Fixed AV in .Disconnect + + + Rev 1.56 2004.04.18 12:52:04 AM czhower + Big bug fix with server disconnect and several other bug fixed that I found + along the way. + + + Rev 1.55 2004.03.06 10:40:30 PM czhower + Changed IOHandler management to fix bug in server shutdowns. + + + Rev 1.54 2004.03.06 1:32:58 PM czhower + -Change to disconnect + -Addition of DisconnectNotifyPeer + -WriteHeader now write bufers + + + Rev 1.53 3/1/04 7:12:00 PM RLebeau + Bug fix for SetIOHandler() not updating the FSocket member correctly. + + + Rev 1.52 2004.02.03 4:16:56 PM czhower + For unit name changes. + + + Rev 1.51 1/29/04 9:37:18 PM RLebeau + Added setter method for Greeting property + + + Rev 1.50 2004.01.28 9:42:32 PM czhower + Now checks for connection. + + + Rev 1.49 2004.01.20 10:03:36 PM czhower + InitComponent + + + Rev 1.48 2003.12.31 3:47:44 PM czhower + Changed to use TextIsSame + + + Rev 1.47 12/28/2003 4:47:40 PM BGooijen + Removed ChangeReplyClass + + + Rev 1.46 14/12/2003 18:14:54 CCostelloe + Added ChangeReplyClass procedure. + + + Rev 1.45 11/4/2003 10:28:34 PM DSiders + Removed exceptions moved to IdException.pas. + + + Rev 1.44 2003.10.18 9:33:28 PM czhower + Boatload of bug fixes to command handlers. + + + Rev 1.43 10/15/2003 7:32:48 PM DSiders + Added a resource string for the exception raised in + TIdTCPConnection.CreateIOHandler. + + + Rev 1.42 2003.10.14 1:27:02 PM czhower + Uupdates + Intercept support + + + Rev 1.41 10/10/2003 11:00:36 PM BGooijen + Added GetReplyClass + + + Rev 1.40 2003.10.02 8:29:40 PM czhower + Added IdReply back + + + Rev 1.39 2003.10.02 8:08:52 PM czhower + Removed unneeded unit in uses. + + + Rev 1.38 2003.10.01 9:11:28 PM czhower + .Net + + + Rev 1.37 2003.10.01 5:05:18 PM czhower + .Net + + + Rev 1.36 2003.10.01 2:30:42 PM czhower + .Net + + + Rev 1.35 2003.10.01 11:16:38 AM czhower + .Net + + + Rev 1.34 2003.09.30 1:23:06 PM czhower + Stack split for DotNet + + + Rev 1.33 2003.09.18 7:12:42 PM czhower + AV Fix in SetIOHandler + + + Rev 1.32 2003.09.18 5:18:00 PM czhower + Implemented OnWork + + + Rev 1.31 2003.06.30 6:17:48 PM czhower + Moved socket property to public. Dont know how/why it got protected. + + + Rev 1.30 2003.06.30 5:41:56 PM czhower + -Fixed AV that occurred sometimes when sockets were closed with chains + -Consolidated code that was marked by a todo for merging as it no longer + needed to be separate + -Removed some older code that was no longer necessary + + Passes bubble tests. + + + Rev 1.29 2003.06.05 10:08:52 AM czhower + Extended reply mechanisms to the exception handling. Only base and RFC + completed, handing off to J Peter. + + + Rev 1.28 6/4/2003 03:54:42 PM JPMugaas + Now should compile. + + + Rev 1.27 2003.06.04 8:10:00 PM czhower + Modified CheckResponse string version to allow '' + + + Rev 1.26 2003.06.04 12:02:30 PM czhower + Additions for text code and command handling. + + + Rev 1.25 2003.06.03 3:44:26 PM czhower + Removed unused variable. + + + Rev 1.24 2003.05.30 10:25:58 PM czhower + Implemented IsEndMarker + + + Rev 1.23 5/26/2003 04:29:52 PM JPMugaas + Removed GenerateReply and ParseReply. Those are now obsolete duplicate + functions in the new design. + + + Rev 1.22 5/26/2003 12:19:56 PM JPMugaas + + + Rev 1.21 2003.05.26 11:38:20 AM czhower + + + Rev 1.20 5/25/2003 03:34:54 AM JPMugaas + + + Rev 1.19 5/25/2003 03:16:22 AM JPMugaas + + + Rev 1.18 5/20/2003 02:40:10 PM JPMugaas + + + Rev 1.17 5/20/2003 12:43:50 AM BGooijen + changeable reply types + + + Rev 1.16 4/4/2003 8:10:14 PM BGooijen + procedure CreateIOHandler is now public + + + Rev 1.15 3/27/2003 3:17:32 PM BGooijen + Removed MaxLineLength, MaxLineAction, SendBufferSize, RecvBufferSize, + ReadLnSplit, ReadLnTimedOut + + + Rev 1.14 3/19/2003 1:04:16 PM BGooijen + changed procedure CreateIOHandler a little (default parameter, and other + behavour when parameter = nil (constructs default now)) + + + Rev 1.13 3/5/2003 11:07:18 PM BGooijen + removed intercept from this file + + + Rev 1.12 2003.02.25 7:28:02 PM czhower + Fixed WriteRFCReply + + + Rev 1.11 2003.02.25 1:36:20 AM czhower + + + Rev 1.10 2/13/2003 02:14:44 PM JPMugaas + Now calls ReadLn in GetInternelResponse so a space is not dropped. Dropping + a space throws off some things in FTP such as the FEAT reply. + + + Rev 1.9 2003.01.18 12:29:52 PM czhower + + + Rev 1.8 1-17-2003 22:22:08 BGooijen + new design + + + Rev 1.7 12-16-2002 20:44:38 BGooijen + Added procedure CreateIOHandler(....) + + + Rev 1.6 12-15-2002 23:32:32 BGooijen + Added RecvBufferSize + + + Rev 1.5 12-14-2002 22:16:32 BGooijen + improved method to detect timeouts in ReadLn. + + + Rev 1.4 12/6/2002 02:11:46 PM JPMugaas + Protected Port and Host properties added to TCPClient because those are + needed by protocol implementations. Socket property added to TCPConnection. + + + Rev 1.3 6/12/2002 11:00:16 AM SGrobety + + + Rev 1.0 21/11/2002 12:36:48 PM SGrobety Version: Indy 10 + + + Rev 1.2 11/15/2002 01:26:42 PM JPMugaas + Restored Trim to ReadLnWait and changed GetInternelResponse to use ReadLn + instead of ReadLn wait. + + + Rev 1.1 11/14/2002 06:44:54 PM JPMugaas + Removed Trim from ReadLnWait. It was breaking the new RFC Reply parsing code + by removing the space at the beggining of a line. + + + Rev 1.0 11/13/2002 09:00:30 AM JPMugaas +} +unit IdTCPConnection; + +interface + +{$i IdCompilerDefines.inc} + +{ +2003-12-14 - Ciaran Costelloe + - Added procedure ChangeReplyClass, because in .NET, you cannot set FReplyClass + before calling the constructor, so call this procedure after the constructor + to set FReplyClass to (say) TIdReplyIMAP4. +2002-06 -Andrew P.Rybin + -WriteStream optimization and new "friendly" interface, InputLn fix (CrLf only if AEcho) +2002-04-12 - Andrew P.Rybin + - ReadLn bugfix and optimization +2002-01-20 - Chad Z. Hower a.k.a Kudzu + -WriteBuffer change was not correct. Removed. Need info on original problem to fix properly. + -Modified ReadLnWait +2002-01-19 - Grahame Grieve + - Fix to WriteBuffer to accept -1 from the stack. + Also fixed to clean up FWriteBuffer if connection lost. +2002-01-19 - Chad Z. Hower a.k.a Kudzu + -Fix to ReadLn +2002-01-16 - Andrew P.Rybin + -ReadStream optimization, TIdManagedBuffer new +2002-01-03 - Chad Z. Hower a.k.a Kudzu + -Added MaxLineAction + -Added ReadLnSplit +2001-12-27 - Chad Z. Hower a.k.a Kudzu + -Changes and bug fixes to InputLn + -Modifed how buffering works + -Added property InputBuffer + -Moved some things to TIdBuffer + -Modified ReadLn + -Added LineCount to Capture +2001-12-25 - Andrew P.Rybin + -MaxLineLength,ReadLn,InputLn and Merry Christmas! +Original Author and Maintainer: + -Chad Z. Hower a.k.a Kudzu +} + +uses + Classes, + IdComponent, + IdException, + IdExceptionCore, + IdGlobal, + IdIntercept, + IdIOHandler, + IdIOHandlerSocket, + IdIOHandlerStack, + IdReply, + IdSocketHandle, + IdBaseComponent; + +type + TIdTCPConnection = class(TIdComponent) + protected + FGreeting: TIdReply; // TODO: Only TIdFTP uses it, so it should be moved! + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIntercept: TIdConnectionIntercept; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FIOHandler: TIdIOHandler; + FLastCmdResult: TIdReply; + FManagedIOHandler: Boolean; + FOnDisconnected: TNotifyEvent; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSocket: TIdIOHandlerSocket; + FReplyClass: TIdReplyClass; + // + procedure CheckConnected; + procedure DoOnDisconnected; virtual; + procedure InitComponent; override; + function GetIntercept: TIdConnectionIntercept; virtual; + function GetReplyClass: TIdReplyClass; virtual; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure SetIntercept(AValue: TIdConnectionIntercept); virtual; + procedure SetIOHandler(AValue: TIdIOHandler); virtual; + procedure SetGreeting(AValue: TIdReply); + procedure WorkBeginEvent(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64); + procedure WorkEndEvent(ASender: TObject; AWorkMode: TWorkMode); + procedure WorkEvent(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); + procedure PrepareCmd(var aCmd: string); virtual; + public + procedure CreateIOHandler(ABaseType: TIdIOHandlerClass = nil); + procedure CheckForGracefulDisconnect(ARaiseExceptionIfDisconnected: Boolean = True); virtual; + // + function CheckResponse(const AResponse: Int16; + const AAllowedResponses: array of Int16): Int16; overload; virtual; + function CheckResponse(const AResponse, AAllowedResponse: string): string; overload; virtual; + // + function Connected: Boolean; virtual; + destructor Destroy; override; + // Dont allow override of this one, its for overload only + procedure Disconnect; overload; // .Net overload + procedure Disconnect(ANotifyPeer: Boolean); overload; virtual; + // This is called when a protocol sends a command to tell the other side (typically client to + // server) that it is about to disconnect. The implementation should go here. + procedure DisconnectNotifyPeer; virtual; + // GetInternalResponse is not in IOHandler as some protocols may need to + // override it. It could be still moved and proxied from here, but at this + // point it is here. + procedure GetInternalResponse(AEncoding: IIdTextEncoding = nil); virtual; + // Reads response using GetInternalResponse which each reply type can define + // the behaviour. Then checks against expected Code. + // + // Seperate one for singles as one of the older Delphi compilers cannot + // match a single number into an array. IIRC newer ones do. + function GetResponse(const AAllowedResponse: Int16 = -1; + AEncoding: IIdTextEncoding = nil): Int16; overload; + function GetResponse(const AAllowedResponses: array of Int16; + AEncoding: IIdTextEncoding = nil): Int16; overload; virtual; + // No array type for strings as ones that use strings are usually bastard + // protocols like POP3/IMAP which dont include proper substatus anyways. + // + // If a case can be made for some other condition this may be expanded + // in the future + function GetResponse(const AAllowedResponse: string; + AEncoding: IIdTextEncoding = nil): string; overload; virtual; + // + property Greeting: TIdReply read FGreeting write SetGreeting; + // RaiseExceptionForCmdResult - Overload necesary as a exception as a default param doesnt work + procedure RaiseExceptionForLastCmdResult; overload; virtual; + procedure RaiseExceptionForLastCmdResult(AException: TClassIdException); + overload; virtual; + // These are extended GetResponses, so see the comments for GetResponse + function SendCmd(AOut: string; const AResponse: Int16 = -1; + AEncoding: IIdTextEncoding = nil): Int16; overload; + function SendCmd(AOut: string; const AResponse: array of Int16; + AEncoding: IIdTextEncoding = nil): Int16; overload; virtual; + function SendCmd(AOut: string; const AResponse: string; + AEncoding: IIdTextEncoding = nil): string; overload; + // + procedure WriteHeader(AHeader: TStrings); + procedure WriteRFCStrings(AStrings: TStrings); + // + property LastCmdResult: TIdReply read FLastCmdResult; + property ManagedIOHandler: Boolean read FManagedIOHandler write FManagedIOHandler; + property Socket: TIdIOHandlerSocket read FSocket; + published + property Intercept: TIdConnectionIntercept read GetIntercept write SetIntercept; + property IOHandler: TIdIOHandler read FIOHandler write SetIOHandler; + // Events + property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected; + property OnWork; + property OnWorkBegin; + property OnWorkEnd; + end; + +implementation + +uses + IdAntiFreezeBase, IdResourceStringsCore, IdStackConsts, IdReplyRFC, + SysUtils; + +function TIdTCPConnection.GetIntercept: TIdConnectionIntercept; +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + LIOHandler := IOHandler; + if LIOHandler <> nil then begin + Result := LIOHandler.Intercept; + end else begin + Result := FIntercept; + end; +end; + +function TIdTCPConnection.GetReplyClass:TIdReplyClass; +begin + Result := TIdReplyRFC; +end; + +procedure TIdTCPConnection.CreateIOHandler(ABaseType:TIdIOHandlerClass=nil); +begin + if Connected then begin + raise EIdException.Create(RSIOHandlerCannotChange); + end; + if Assigned(ABaseType) then begin + IOHandler := TIdIOHandler.MakeIOHandler(ABaseType, Self); + end else begin + IOHandler := TIdIOHandler.MakeDefaultIOHandler(Self); + end; + ManagedIOHandler := True; +end; + +function TIdTCPConnection.Connected: Boolean; +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + // Its been changed now that IOHandler is not usually nil, but can be before the initial connect + // and also this keeps it here so the user does not have to access the IOHandler for this and + // also to allow future control from the connection. + LIOHandler := IOHandler; + Result := Assigned(LIOHandler); + if Result then begin + Result := LIOHandler.Connected; + end; +end; + +destructor TIdTCPConnection.Destroy; +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + // Just close IOHandler directly. Dont call Disconnect - Disconnect may be override and + // try to read/write to the socket. + LIOHandler := IOHandler; + if Assigned(LIOHandler) then begin + LIOHandler.Close; + // This will free any managed IOHandlers + {$IFDEF USE_OBJECT_ARC}LIOHandler := nil;{$ENDIF} + SetIOHandler(nil); + end; + FreeAndNil(FLastCmdResult); + FreeAndNil(FGreeting); + inherited Destroy; +end; + +procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + try + // Separately to avoid calling .Connected unless needed + if ANotifyPeer then begin + // TODO: do not call Connected() here if DisconnectNotifyPeer() is not + // overriden. Ideally, Connected() should be called by overridden + // DisconnectNotifyPeer() implementations if they really need it. But + // to avoid any breakages in third-party overrides, we could check here + // if DisconnectNotifyPeer() has been overridden and then call Connected() + // to maintain existing behavior... + // + try + if Connected then begin + DisconnectNotifyPeer; + end; + except + // TODO: maybe shallow only EIdConnClosedGracefully and EIdSocketError? + end; + end; + finally + { + there are a few possible situations here: + 1) we are still connected, then everything works as before, + status disconnecting, then disconnect, status disconnected + 2) we are not connected, and this is just some "rogue" call to + disconnect(), then nothing happens + 3) we are not connected, because ClosedGracefully, then + LConnected will be false, but the implicit call to + CheckForDisconnect (inside Connected) will call the events + } + // We dont check connected here - we realy dont care about actual socket state + // Here we just want to close the actual IOHandler. It is very possible for a + // socket to be disconnected but the IOHandler still open. In this case we only + // care of the IOHandler is still open. + // + // This is especially important if the socket has been disconnected with error, at this + // point we just want to ignore it and checking .Connected would trigger this. We + // just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause + // CONNABORTED. So its extra important we just disconnect without checking socket state. + LIOHandler := IOHandler; + if Assigned(LIOHandler) then begin + if LIOHandler.Opened then begin + DoStatus(hsDisconnecting); + LIOHandler.Close; + DoOnDisconnected; + DoStatus(hsDisconnected); + //LIOHandler.InputBuffer.Clear; + end; + end; + end; +end; + +procedure TIdTCPConnection.DoOnDisconnected; +begin + if Assigned(OnDisconnected) then begin + OnDisconnected(Self); + end; +end; + +function TIdTCPConnection.GetResponse(const AAllowedResponses: array of Int16; + AEncoding: IIdTextEncoding = nil): Int16; +begin + GetInternalResponse(AEncoding); + Result := CheckResponse(LastCmdResult.NumericCode, AAllowedResponses); +end; + +procedure TIdTCPConnection.RaiseExceptionForLastCmdResult( + AException: TClassIdException); +begin + raise AException.Create(LastCmdResult.Text.Text); +end; + +procedure TIdTCPConnection.RaiseExceptionForLastCmdResult; +begin + LastCmdResult.RaiseReplyError; +end; + +function TIdTCPConnection.SendCmd(AOut: string; const AResponse: Array of Int16; + AEncoding: IIdTextEncoding = nil): Int16; +begin + CheckConnected; + PrepareCmd(AOut); + IOHandler.WriteLn(AOut, AEncoding); + Result := GetResponse(AResponse, AEncoding); +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdTCPConnection.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) then begin + {$IFNDEF USE_OBJECT_ARC} + if (AComponent = FIntercept) then begin + FIntercept := nil; + end else + {$ENDIF} + if (AComponent = FIOHandler) then begin + FIOHandler := nil; + FSocket := nil; + FManagedIOHandler := False; + end; + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdTCPConnection.SetIntercept(AValue: TIdConnectionIntercept); +var + // under ARC, convert weak references to strong references before working with them + LIntercept: TIdConnectionIntercept; + LIOHandler: TIdIOHandler; +begin + LIntercept := FIntercept; + + if LIntercept <> AValue then + begin + LIOHandler := IOHandler; + + // RLebeau 8/25/09 - normally, short-circuit logic should skip all subsequent + // evaluations in a multi-condition statement once one of the conditions + // evaluates to False. However, a user just ran into a situation where that + // was not the case! It caused an AV in SetIOHandler() further below when + // AValue was nil (from Destroy() further above) because Assigned(AValue.Intercept) + // was still being evaluated even though Assigned(AValue) was returning False. + // SetIntercept() is using the same kind of short-circuit logic here as well. + // Let's not rely on short-circuiting anymore, just to be on the safe side. + // + // old code: if Assigned(IOHandler) and Assigned(IOHandler.Intercept) and Assigned(AValue) and (AValue <> IOHandler.Intercept) then begin + // + if Assigned(LIOHandler) and Assigned(AValue) then begin + if Assigned(LIOHandler.Intercept) and (LIOHandler.Intercept <> AValue) then begin + raise EIdException.Create(RSInterceptIsDifferent); + end; + end; + + // TODO: should LIntercept.Connection be set to nil here if LIntercept + // is not nil and LIntercept.Connection is set to Self? + + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FIntercept := AValue; + {$ELSE} + // remove self from the Intercept's free notification list + if Assigned(LIntercept) then begin + LIntercept.RemoveFreeNotification(Self); + end; + FIntercept := AValue; + // add self to the Intercept's free notification list + if Assigned(AValue) then begin + AValue.FreeNotification(Self); + end; + {$ENDIF} + + if Assigned(LIOHandler) then begin + LIOHandler.Intercept := AValue; + end; + + // TODO: should FIntercept.Connection be set to Self here if FIntercept + // is not nil? + end; +end; + +procedure TIdTCPConnection.SetIOHandler(AValue: TIdIOHandler); +var + // under ARC, convert weak references to strong references before working with them + LIOHandler: TIdIOHandler; + LIntercept, LOtherIntercept: TIdConnectionIntercept; +begin + LIOHandler := FIOHandler; + + if LIOHandler <> AValue then begin + LIntercept := FIntercept; + + // RLebeau 8/25/09 - normally, short-circuit logic should skip all subsequent + // evaluations in a multi-condition statement once one of the conditions + // evaluates to False. However, a user just ran into a situation where that + // was not the case! It caused an AV when AValue was nil (from Destroy() + // further above) because Assigned(AValue.Intercept) was still being evaluated + // even though Assigned(AValue) was returning False. Let's not rely on + // short-circuiting anymore, just to be on the safe side. + // + // old code: if Assigned(AValue) and Assigned(AValue.Intercept) and Assigned(FIntercept) and (AValue.Intercept <> FIntercept) then begin + // + if Assigned(AValue) and Assigned(LIntercept) then begin + LOtherIntercept := AValue.Intercept; + if Assigned(LOtherIntercept) then begin + if LOtherIntercept <> LIntercept then begin + raise EIdException.Create(RSInterceptIsDifferent); + end; + {$IFDEF USE_OBJECT_ARC}LOtherIntercept := nil;{$ENDIF} + end; + end; + + if ManagedIOHandler then begin + if Assigned(LIOHandler) then begin + FIOHandler := nil; + IdDisposeAndNil(LIOHandler); + end; + ManagedIOHandler := False; + end; + + // under ARC, all weak references to a freed object get nil'ed automatically + + // Reset this if nil (to match nil, but not needed) or when a new IOHandler is specified + // If true, code must set it after the IOHandler is set + // Must do after call to FreeManagedIOHandler + FSocket := nil; + + // Clear out old values whether setting AValue to nil, or setting a new value + if Assigned(LIOHandler) then begin + LIOHandler.WorkTarget := nil; + {$IFNDEF USE_OBJECT_ARC} + LIOHandler.RemoveFreeNotification(Self); + {$ENDIF} + end; + + if Assigned(AValue) then begin + {$IFNDEF USE_OBJECT_ARC} + // add self to the IOHandler's free notification list + AValue.FreeNotification(Self); + {$ENDIF} + // Must set to handlers and not events directly as user may change + // the events of TCPConnection after we have initialized these and then + // these would point to old values + AValue.WorkTarget := Self; + if Assigned(LIntercept) then begin + AValue.Intercept := LIntercept; + end; + if AValue is TIdIOHandlerSocket then begin + FSocket := TIdIOHandlerSocket(AValue); + end; + end; + + // Last as some code uses FIOHandler to finalize items + FIOHandler := AValue; + end; +end; + +procedure TIdTCPConnection.WriteHeader(AHeader: TStrings); +var + i: Integer; + LBufferingStarted: Boolean; + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + CheckConnected; + LIOHandler := IOHandler; + LBufferingStarted := not LIOHandler.WriteBufferingActive; + if LBufferingStarted then begin + LIOHandler.WriteBufferOpen; + end; + try + for i := 0 to AHeader.Count -1 do begin + // No ReplaceAll flag - we only want to replace the first one + LIOHandler.WriteLn(ReplaceOnlyFirst(AHeader[i], '=', ': ')); + end; + LIOHandler.WriteLn; + if LBufferingStarted then begin + LIOHandler.WriteBufferClose; + end; + except + if LBufferingStarted then begin + LIOHandler.WriteBufferCancel; + end; + raise; + end; +end; + +function TIdTCPConnection.SendCmd(AOut: string; const AResponse: Int16 = -1; + AEncoding: IIdTextEncoding = nil): Int16; +begin + if AResponse < 0 then begin + Result := SendCmd(AOut, [], AEncoding); + end else begin + Result := SendCmd(AOut, [AResponse], AEncoding); + end; +end; + +procedure TIdTCPConnection.CheckForGracefulDisconnect(ARaiseExceptionIfDisconnected: Boolean); +var + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + LIOHandler := IOHandler; + if Assigned(LIOHandler) then begin + LIOHandler.CheckForDisconnect(ARaiseExceptionIfDisconnected); + end else if ARaiseExceptionIfDisconnected then begin + raise EIdException.Create(RSNotConnected); + end; +end; + +function TIdTCPConnection.CheckResponse(const AResponse: Int16; + const AAllowedResponses: array of Int16): Int16; +var + i: Integer; + LResponseFound: Boolean; +begin + if High(AAllowedResponses) > -1 then begin + LResponseFound := False; + for i := Low(AAllowedResponses) to High(AAllowedResponses) do begin + if AResponse = AAllowedResponses[i] then begin + LResponseFound := True; + Break; + end; + end; + if not LResponseFound then begin + RaiseExceptionForLastCmdResult; + end; + end; + Result := AResponse; +end; + +procedure TIdTCPConnection.GetInternalResponse(AEncoding: IIdTextEncoding = nil); +var + LLine: string; + LResponse: TStringList; + // under ARC, convert a weak reference to a strong reference before working with it + LIOHandler: TIdIOHandler; +begin + CheckConnected; + LResponse := TStringList.Create; + try + // Some servers with bugs send blank lines before reply. Dont remember which + // ones, but I do remember we changed this for a reason + // RLebeau 9/14/06: this can happen in between lines of the reply as well + LIOHandler := IOHandler; + repeat + LLine := LIOHandler.ReadLnWait(MaxInt, AEncoding); + LResponse.Add(LLine); + until FLastCmdResult.IsEndMarker(LLine); + //Note that FormattedReply uses an assign in it's property set method. + FLastCmdResult.FormattedReply := LResponse; + finally + FreeAndNil(LResponse); + end; +end; + +procedure TIdTCPConnection.WriteRFCStrings(AStrings: TStrings); +begin + CheckConnected; + IOHandler.WriteRFCStrings(AStrings, True); +end; + +function TIdTCPConnection.GetResponse(const AAllowedResponse: Int16 = -1; + AEncoding: IIdTextEncoding = nil): Int16; +begin + if AAllowedResponse < 0 then begin + Result := GetResponse([], AEncoding); + end else begin + Result := GetResponse([AAllowedResponse], AEncoding); + end; +end; + +function TIdTCPConnection.GetResponse(const AAllowedResponse: string; + AEncoding: IIdTextEncoding = nil): string; +begin + GetInternalResponse(AEncoding); + Result := CheckResponse(LastCmdResult.Code, AAllowedResponse); +end; + +function TIdTCPConnection.SendCmd(AOut: string; const AResponse: string; + AEncoding: IIdTextEncoding = nil): string; +begin + CheckConnected; + PrepareCmd(AOut); + IOHandler.WriteLn(AOut, AEncoding); + Result := GetResponse(AResponse, AEncoding); +end; + +function TIdTCPConnection.CheckResponse(const AResponse, AAllowedResponse: string): string; +begin + if (AAllowedResponse <> '') + and (not TextIsSame(AResponse, AAllowedResponse)) then begin + RaiseExceptionForLastCmdResult; + end; + Result := AResponse; +end; + +procedure TIdTCPConnection.WorkBeginEvent(ASender: TObject; AWorkMode: TWorkMode; + AWorkCountMax: Int64); +begin + BeginWork(AWorkMode, AWorkCountMax) +end; + +procedure TIdTCPConnection.WorkEndEvent(ASender: TObject; AWorkMode: TWorkMode); +begin + EndWork(AWorkMode) +end; + +procedure TIdTCPConnection.WorkEvent(ASender: TObject; AWorkMode: TWorkMode; + AWorkCount: Int64); +begin + DoWork(AWorkMode, AWorkCount) +end; + +procedure TIdTCPConnection.InitComponent; +begin + inherited InitComponent; + FReplyClass := GetReplyClass; + FGreeting := FReplyClass.CreateWithReplyTexts(nil, nil); + FLastCmdResult := FReplyClass.CreateWithReplyTexts(nil, nil); +end; + +procedure TIdTCPConnection.CheckConnected; +begin + if not Assigned(IOHandler) then begin + raise EIdNotConnected.Create(RSNotConnected); + end; +end; + +procedure TIdTCPConnection.SetGreeting(AValue: TIdReply); +begin + FGreeting.Assign(AValue); +end; + +procedure TIdTCPConnection.Disconnect; +begin + // The default should be to tell the other side we are disconnecting + Disconnect(True); +end; + +procedure TIdTCPConnection.DisconnectNotifyPeer; +begin +end; + +procedure TIdTCPConnection.PrepareCmd(var aCmd: string); +begin + //Leave this empty here. It's for cases where we may need to + // override what is sent to a server in a transparent manner. +end; + +end. diff --git a/indy/Core/IdTCPServer.pas b/indy/Core/IdTCPServer.pas new file mode 100644 index 0000000..84b2d44 --- /dev/null +++ b/indy/Core/IdTCPServer.pas @@ -0,0 +1,54 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.69 12/2/2004 9:26:42 PM JPMugaas + Bug fix. +} + +unit IdTCPServer; + +interface +{$i IdCompilerDefines.inc} + +uses IdCustomTCPServer; + +type + EIdTCPNoOnExecute = class(EIdTCPServerError); + + TIdTCPServer = class(TIdCustomTCPServer) + protected + procedure CheckOkToBeActive; override; + published + property OnExecute; + end; + +implementation + +uses IdResourceStringsCore; + +{ TIdTCPServer } + +procedure TIdTCPServer.CheckOkToBeActive; +begin + inherited CheckOkToBeActive; + if not Assigned(FOnExecute) then begin + raise EIdTCPNoOnExecute.Create(RSNoOnExecute); + end; +end; + +end. diff --git a/indy/Core/IdTCPStream.pas b/indy/Core/IdTCPStream.pas new file mode 100644 index 0000000..981bb4d --- /dev/null +++ b/indy/Core/IdTCPStream.pas @@ -0,0 +1,140 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.13 27.08.2004 21:58:22 Andreas Hausladen + Speed optimization ("const" for string parameters) + + Rev 1.12 29/05/2004 21:17:48 CCostelloe + ReadLnSplit added, needed for binary attachments + + Rev 1.11 28/05/2004 20:30:12 CCostelloe + Bug fix + + Rev 1.10 2004.05.21 8:22:16 PM czhower + Added ReadLn + + Rev 1.9 2004.05.20 1:40:00 PM czhower + Last of the IdStream updates + + Rev 1.8 2004.03.07 11:48:46 AM czhower + Flushbuffer fix + other minor ones found + + Rev 1.7 2004.02.03 4:16:58 PM czhower + For unit name changes. + + Rev 1.6 5/12/2003 9:17:58 AM GGrieve + remove dead code + + Rev 1.5 5/12/2003 12:32:14 AM GGrieve + Refactor to work under DotNet + + Rev 1.4 10/10/2003 11:04:24 PM BGooijen + DotNet + + Rev 1.3 9/10/2003 1:50:50 PM SGrobety + DotNet + + Rev 1.2 2003.10.01 11:16:38 AM czhower + .Net + + Rev 1.1 2003.10.01 1:37:36 AM czhower + .Net + + Rev 1.0 11/13/2002 09:01:04 AM JPMugaas +} + +unit IdTCPStream; + +interface + +{$I IdCompilerDefines.inc} +//TODO: This should be renamed to IdStreamTCP for consistency, and class too + +uses + Classes, + IdGlobal, IdTCPConnection; + +type + TIdTCPStream = class(TIdBaseStream) + protected + FConnection: TIdTCPConnection; + FWriteThreshold: Integer; + FWriteBuffering: Boolean; + function IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; override; + function IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; override; + function IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override; + procedure IdSetSize(ASize: Int64); override; + public + constructor Create(AConnection: TIdTCPConnection; const AWriteThreshold: Integer = 0); reintroduce; + destructor Destroy; override; + property Connection: TIdTCPConnection read FConnection; + end; + +implementation + +uses + IdException, + SysUtils; + +constructor TIdTCPStream.Create(AConnection: TIdTCPConnection; const AWriteThreshold: Integer = 0); +begin + inherited Create; + FConnection := AConnection; + FWriteThreshold := AWriteThreshold; +end; + +destructor TIdTCPStream.Destroy; +begin + if FWriteBuffering then begin + Connection.IOHandler.WriteBufferClose; + end; + inherited Destroy; +end; + +function TIdTCPStream.IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; +begin + if AOffset <> 0 then begin + ToDo('IdRead() method of TIdTCPStream class does not support seeking'); {do not localized} + end; + Connection.IOHandler.ReadBytes(VBuffer, ACount, False); + Result := ACount; +end; + +function TIdTCPStream.IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; +begin + Result := 0; +end; + +procedure TIdTCPStream.IdSetSize(ASize: Int64); +begin +// +end; + +function TIdTCPStream.IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; +begin + if (not FWriteBuffering) and (FWriteThreshold > 0) and (not Connection.IOHandler.WriteBufferingActive) then begin + Connection.IOHandler.WriteBufferOpen(FWriteThreshold); + FWriteBuffering := True; + end; + Connection.IOHandler.Write(ABuffer, ACount, AOffset); + Result := ACount; +end; + +end. + + diff --git a/indy/Core/IdTask.pas b/indy/Core/IdTask.pas new file mode 100644 index 0000000..8e5cec3 --- /dev/null +++ b/indy/Core/IdTask.pas @@ -0,0 +1,144 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2003.11.04 3:49:00 PM czhower + Update to sync TC + + Rev 1.1 2003.10.21 12:19:02 AM czhower + TIdTask support and fiber bug fixes. +} + +unit IdTask; + +interface +{$i IdCompilerDefines.inc} + +uses + {$IFDEF USE_OBJECT_ARC} + IdGlobal, + {$ENDIF} + IdYarn, + SysUtils; + +type + TIdTask = class(TObject) + protected + FBeforeRunDone: Boolean; + {$IFDEF USE_OBJECT_ARC} + // When ARC is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so + // we will provide separate properties for those purposes + // + // TODO; use TValue instead of separating them + // + FDataObject: TObject; + FDataValue: PtrInt; + {$ELSE} + FData: TObject; + {$ENDIF} + FYarn: TIdYarn; + // + procedure AfterRun; virtual; + procedure BeforeRun; virtual; + function Run: Boolean; virtual; abstract; + procedure HandleException(AException: Exception); virtual; + public + constructor Create( + AYarn: TIdYarn + ); reintroduce; virtual; + destructor Destroy; override; + // The Do's are separate so we can add events later if necessary without + // needing the inherited calls to perform them, as well as allowing + // us to keep the real runs as protected + procedure DoAfterRun; + procedure DoBeforeRun; + function DoRun: Boolean; + procedure DoException(AException: Exception); + // BeforeRunDone property to allow flexibility in alternative schedulers + property BeforeRunDone: Boolean read FBeforeRunDone; + // + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read FDataObject write FDataObject; + property DataValue: PtrInt read FDataValue write FDataValue; + {$ELSE} + property Data: TObject read FData write FData; + {$ENDIF} + property Yarn: TIdYarn read FYarn; + end; + +implementation + +{$IFNDEF USE_OBJECT_ARC} +uses + IdGlobal; +{$ENDIF} + +{ TIdTask } + +procedure TIdTask.AfterRun; +begin +end; + +procedure TIdTask.BeforeRun; +begin +end; + +procedure TIdTask.HandleException(AException: Exception); +begin +end; + +constructor TIdTask.Create(AYarn: TIdYarn); +begin + inherited Create; + FYarn := AYarn; + FBeforeRunDone := False; +end; + +destructor TIdTask.Destroy; +begin + // Dont free the yarn, that is the responsibilty of the thread / fiber. + // .Yarn here is just a reference, not an ownership + FreeAndNil({$IFDEF USE_OBJECT_ARC}FDataObject{$ELSE}FData{$ENDIF}); + {$IFDEF USE_OBJECT_ARC} + FDataValue := 0; + {$ENDIF} + inherited Destroy; +end; + +procedure TIdTask.DoAfterRun; +begin + AfterRun; +end; + +procedure TIdTask.DoBeforeRun; +begin + FBeforeRunDone := True; + BeforeRun; +end; + +function TIdTask.DoRun: Boolean; +begin + Result := Run; +end; + +procedure TIdTask.DoException(AException: Exception); +begin + HandleException(AException); +end; + +end. diff --git a/indy/Core/IdThread.pas b/indy/Core/IdThread.pas new file mode 100644 index 0000000..234cb40 --- /dev/null +++ b/indy/Core/IdThread.pas @@ -0,0 +1,746 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.34 03/16/05 10:29:40 AM JSouthwell + Added a default thread name to ease debugging of IdThreads. + + Rev 1.33 1/15/05 1:52:36 PM RLebeau + Extra cleanup handling for the FYarn member + + Rev 1.32 1/6/2005 10:02:58 PM JPMugaas + This should compile. + + Rev 1.31 1/6/05 2:33:04 PM RLebeau + one more try...finally block, for Before/AfterExecute() + + Rev 1.29 1/5/05 5:31:08 PM RLebeau + Added extra try..finally block to Execute() to free the FYarn member. + + Rev 1.28 6/9/2004 10:38:46 PM DSiders + Fixed case for TIdNotifyThreadEvent. + + Rev 1.27 3/12/2004 7:11:02 PM BGooijen + Changed order of commands for dotnet + + Rev 1.26 2004.03.01 5:12:44 PM czhower + -Bug fix for shutdown of servers when connections still existed (AV) + -Implicit HELP support in CMDserver + -Several command handler bugs + -Additional command handler functionality. + + Rev 1.25 2004.02.03 4:17:00 PM czhower + For unit name changes. + + Rev 1.24 2004.01.22 5:59:12 PM czhower + IdCriticalSection + + Rev 1.23 2003.12.28 2:33:16 PM czhower + .Net finalization fix. + + Rev 1.22 2003.12.28 1:27:46 PM czhower + .Net compatibility + + Rev 1.21 2003.10.24 12:59:20 PM czhower + Name change + + Rev 1.20 2003.10.21 12:19:04 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.19 10/15/2003 8:40:48 PM DSiders + Added locaization comments. + + Rev 1.18 10/5/2003 3:19:58 PM BGooijen + disabled some stuff for DotNet + + Rev 1.17 2003.09.19 10:11:22 PM czhower + Next stage of fiber support in servers. + + Rev 1.14 2003.09.19 11:54:36 AM czhower + -Completed more features necessary for servers + -Fixed some bugs + + Rev 1.13 2003.09.18 4:43:18 PM czhower + -Removed IdBaseThread + -Threads now have default names + + Rev 1.12 12.9.2003 . 16:42:08 DBondzhev + Fixed AV when exception is raised in BeforeRun and thread is terminated + before Start is compleated + + Rev 1.11 2003.07.08 2:41:52 PM czhower + Avoid calling SetThreadName if we do not need to + + Rev 1.10 08.07.2003 13:16:18 ARybin + tiny opt fix + + Rev 1.9 7/1/2003 7:11:30 PM BGooijen + Added comment + + Rev 1.8 2003.07.01 4:14:58 PM czhower + Consolidation. + Added Name, Loop + + Rev 1.7 04.06.2003 14:06:20 ARybin + bug fix & limited waiting + + Rev 1.6 28.05.2003 14:16:16 ARybin + WaitAllThreadsTerminated class method + + Rev 1.5 08.05.2003 12:45:10 ARybin + "be sure" fix + + Rev 1.4 4/30/2003 4:53:26 PM BGooijen + Fixed bug in Kylix where GThreadCount was not decremented + + Rev 1.3 4/22/2003 4:44:06 PM BGooijen + changed Handle to ThreadID + + Rev 1.2 3/22/2003 12:53:26 PM BGooijen + - Exceptions in the constructor are now handled better. + - GThreadCount can't become negative anymore + + Rev 1.1 06.03.2003 11:54:24 ARybin + TIdThreadOptions: is thread Data owner, smart Cleanup + + Rev 1.0 11/13/2002 09:01:14 AM JPMugaas + + 2002-03-12 -Andrew P.Rybin + -TerminatingExceptionClass, etc. + + 2002-06-20 -Andrew P.Rybin + -"Terminated Start" bug fix (FLock.Leave AV) + -Wait All threads termination in FINALIZATION (prevent AV in WinSock) + -HandleRunException + + 2003-01-27 -Andrew P.Rybin + -TIdThreadOptions +} + +unit IdThread; + +{ +2002-03-12 -Andrew P.Rybin + -TerminatingExceptionClass, etc. +2002-06-20 -Andrew P.Rybin + -"Terminated Start" bug fix (FLock.Leave AV) + -Wait All threads termination in FINALIZATION (prevent AV in WinSock) + -HandleRunException +2003-01-27 -Andrew P.Rybin + -TIdThreadOptions +} + +interface + +{$I IdCompilerDefines.inc} + +// RLebeau: On OSX/iOS, an auto-release object pool should be used to clean up +// Objective-C objects that are created within a thread. On Android, any thread +// that uses Java objects will attach to the JVM and must be detached from the +// JVM before terminating. +// +// All objects must be released before terminating/detaching the thread. +// +// This problem was fixed in TThread in RAD Studio XE6. +// + +{$UNDEF PLATFORM_CLEANUP_NEEDED} + +{$IFDEF DCC} + {$IFNDEF VCL_XE6_OR_ABOVE} + {$IFDEF MACOS} + {$DEFINE PLATFORM_CLEANUP_NEEDED} + {$ENDIF MACOS} + {$IFDEF ANDROID} + {$DEFINE PLATFORM_CLEANUP_NEEDED} + {$ENDIF} + {$ENDIF} +{$ELSE} +// TODO: Does this need to be applied to FreePascal? +{$ENDIF} + +uses + Classes, + IdGlobal, IdException, IdYarn, IdTask, IdThreadSafe, SysUtils; + +const + IdWaitAllThreadsTerminatedCount = 1 * 60 * 1000; + IdWaitAllThreadsTerminatedStep = 250; + +type + EIdThreadException = class(EIdException); + EIdThreadTerminateAndWaitFor = class(EIdThreadException); + + TIdThreadStopMode = (smTerminate, smSuspend); + TIdThread = class; + TIdExceptionThreadEvent = procedure(AThread: TIdThread; AException: Exception) of object; + TIdNotifyThreadEvent = procedure(AThread: TIdThread) of object; + TIdSynchronizeThreadEvent = procedure(AThread: TIdThread; AData: Pointer) of object; + + // Note: itoDataOwner doesn't make sense in DCC nextgen when AutoRefCounting is enabled... + TIdThreadOptions = set of (itoStopped, itoReqCleanup, itoDataOwner, itoTag); + + TIdThread = class(TThread) + protected + {$IFDEF USE_OBJECT_ARC} + // When ARC is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so + // we will provide separate properties for those purposes + // + // TODO; use TValue instead of separating them + // + FDataObject: TObject; + FDataValue: PtrInt; + {$ELSE} + FData: TObject; + {$ENDIF} + FLock: TIdCriticalSection; + FLoop: Boolean; + FName: string; + FStopMode: TIdThreadStopMode; + FOptions: TIdThreadOptions; + FTerminatingException: String; + FTerminatingExceptionClass: TClass; + FYarn: TIdYarn; + // + FOnException: TIdExceptionThreadEvent; + FOnStopped: TIdNotifyThreadEvent; + // + {$IFDEF PLATFORM_CLEANUP_NEEDED} + {$IFDEF MACOS} + FObjCPool: Pointer; + {$ENDIF} + {$ENDIF} + procedure AfterRun; virtual; //3* not abstract - otherwise it is required + procedure AfterExecute; virtual;//5 not abstract - otherwise it is required + procedure BeforeExecute; virtual;//1 not abstract - otherwise it is required + procedure BeforeRun; virtual; //2* not abstract - otherwise it is required + procedure Cleanup; virtual;//4* + procedure DoException(AException: Exception); virtual; + procedure DoStopped; virtual; + procedure Execute; override; + {$IFDEF PLATFORM_CLEANUP_NEEDED} + procedure DoTerminate; override; + {$ENDIF} + function GetStopped: Boolean; + function HandleRunException(AException: Exception): Boolean; virtual; + procedure Run; virtual; abstract; + class procedure WaitAllThreadsTerminated( + AMSec: Integer = IdWaitAllThreadsTerminatedCount); + public + constructor Create(ACreateSuspended: Boolean = True; + ALoop: Boolean = True; const AName: string = ''); virtual; + destructor Destroy; override; + procedure Start; {$IFDEF DEPRECATED_TThread_SuspendResume}reintroduce;{$ENDIF} virtual; + procedure Stop; virtual; + procedure Synchronize(Method: TThreadMethod); overload; +//BGO:TODO procedure Synchronize(Method: TMethod); overload; + // Here to make virtual + procedure Terminate; virtual; + procedure TerminateAndWaitFor; virtual; + // + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read FDataObject write FDataObject; + property DataValue: PtrInt read FDataValue write FDataValue; + {$ELSE} + property Data: TObject read FData write FData; + {$ENDIF} + property Loop: Boolean read FLoop write FLoop; + property Name: string read FName write FName; + property ReturnValue; + property StopMode: TIdThreadStopMode read FStopMode write FStopMode; + property Stopped: Boolean read GetStopped; + property Terminated; + // TODO: Change this to be like TIdFiber. D6 implementation is not as good + // as what is done in TIdFiber. + property TerminatingException: string read FTerminatingException; + property TerminatingExceptionClass: TClass read FTerminatingExceptionClass; + //Represents the thread or fiber for the scheduler of the thread. + property Yarn: TIdYarn read FYarn write FYarn; + // + property OnException: TIdExceptionThreadEvent read FOnException write FOnException; + property OnStopped: TIdNotifyThreadEvent read FOnStopped write FOnStopped; + end; + + TIdThreadWithTask = class(TIdThread) + protected + FTask: TIdTask; + // + procedure AfterRun; override; + procedure BeforeRun; override; + procedure Run; override; + procedure DoException(AException: Exception); override; + procedure SetTask(AValue: TIdTask); + public + // Defaults because + // Must always create suspended so task can be set + // And a bit crazy to create a non looped task + constructor Create( + ATask: TIdTask = nil; + const AName: string = '' + ); reintroduce; virtual; + destructor Destroy; override; + // + // Must be writeable because tasks are often created after thread or + // thread is pooled + property Task: TIdTask read FTask write SetTask; + end; + + TIdThreadClass = class of TIdThread; + TIdThreadWithTaskClass = class of TIdThreadWithTask; + +var + // GThreadCount shoudl be in implementation as it is not needed outside of + // this unit. However with D8, GThreadCount will be deallocated before the + // finalization can run and thus when the finalizaiton accesses GThreadCount + // in TerminateAll an error occurs. Moving this declaration to the interface + // "fixes" it. + GThreadCount: TIdThreadSafeInteger = nil; + +implementation + +uses + //facilitate inlining only. + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + {$IFDEF VCL_XE3_OR_ABOVE} + System.SyncObjs, + {$ENDIF} + {$IFDEF PLATFORM_CLEANUP_NEEDED} + {$IFDEF MACOS} + Macapi.ObjCRuntime, + {$ENDIF} + {$IFDEF ANDROID} + Androidapi.NativeActivity, + {$ENDIF} + {$ENDIF} + IdResourceStringsCore; + +class procedure TIdThread.WaitAllThreadsTerminated(AMSec: Integer = IdWaitAllThreadsTerminatedCount); +begin + while AMSec > 0 do begin + if GThreadCount.Value = 0 then begin + Break; + end; + IndySleep(IdWaitAllThreadsTerminatedStep); + AMSec := AMSec - IdWaitAllThreadsTerminatedStep; + end; +end; + +procedure TIdThread.TerminateAndWaitFor; +begin + if FreeOnTerminate then begin + raise EIdThreadTerminateAndWaitFor.Create(RSThreadTerminateAndWaitFor); + end; + Terminate; + Start; //resume + WaitFor; +end; + +procedure TIdThread.BeforeRun; +begin +end; + +procedure TIdThread.AfterRun; +begin +end; + +procedure TIdThread.BeforeExecute; +begin +end; + +procedure TIdThread.AfterExecute; +begin +end; + +procedure TIdThread.Execute; +begin + // Must make this call from INSIDE the thread. The call in Create + // was naming the thread that was creating this thread. :( + // + // RLebeau - no need to put this inside the try blocks below as it + // already uses its own try..except block internally + if Name = '' then begin + Name := 'IdThread (unknown)'; + end; + SetThreadName(Name); + + {$IFDEF PLATFORM_CLEANUP_NEEDED} + {$IFDEF MACOS} + // Register the auto release pool + FObjCPool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'), sel_getUid('alloc')), sel_getUid('init')); + {$ENDIF MACOS} + {$ENDIF} + + try + BeforeExecute; + try + while not Terminated do begin + if Stopped then begin + DoStopped; + // It is possible that either in the DoStopped or from another thread, + // the thread is restarted, in which case we dont want to restop it. + if Stopped then begin // DONE: if terminated? + if Terminated then begin + Break; + end; + // Thread manager will revive us + {$IFDEF DEPRECATED_TThread_SuspendResume} + Suspended := True; + {$ELSE} + Suspend; + {$ENDIF} + if Terminated then begin + Break; + end; + end; + end; + + Include(FOptions, itoReqCleanup); + try + try + try + BeforeRun; + if Loop then begin + while not Stopped do begin + try + Run; + except + on E: Exception do begin + if not HandleRunException(E) then begin + Terminate; + raise; + end; + end; + end; + end; + end else begin + try + Run; + except + on E: Exception do begin + if not HandleRunException(E) then begin + Terminate; + raise; + end; + end; + end; + end; + finally + AfterRun; + end; + except + Terminate; + raise; + end; + finally + Cleanup; + end; + end; + finally + AfterExecute; + end; + except + on E: Exception do begin + FTerminatingExceptionClass := E.ClassType; + FTerminatingException := E.Message; + DoException(E); + Terminate; + end; + end; +end; + +{$IFDEF PLATFORM_CLEANUP_NEEDED} +procedure TIdThread.DoTerminate; +{$IFDEF ANDROID} +var + PActivity: PANativeActivity; +{$ENDIF} +begin + try + inherited; + finally + {$IFDEF MACOS} + // Last thing to do in thread is to drain the pool + objc_msgSend(FObjCPool, sel_getUid('drain')); + {$ENDIF} + {$IFDEF ANDROID} + // Detach the NativeActivity virtual machine to ensure the proper release of JNI contexts attached to the current thread + PActivity := PANativeActivity(System.DelphiActivity); + PActivity^.vm^.DetachCurrentThread(PActivity^.vm); + {$ENDIF} + end; +end; +{$ENDIF} + +constructor TIdThread.Create(ACreateSuspended: Boolean; ALoop: Boolean; const AName: string); +begin + {$IFDEF DOTNET} + inherited Create(True); + {$ENDIF} + FOptions := [itoDataOwner]; + if ACreateSuspended then begin + Include(FOptions, itoStopped); + end; + FLock := TIdCriticalSection.Create; + Loop := ALoop; + Name := AName; + // + {$IFDEF DOTNET} + if not ACreateSuspended then begin + {$IFDEF DEPRECATED_TThread_SuspendResume} + Suspended := False; + {$ELSE} + Resume; + {$ENDIF} + end; + {$ELSE} + // + // Most things BEFORE inherited - inherited creates the actual thread and if + // not suspended will start before we initialize + inherited Create(ACreateSuspended); + {$IFNDEF VCL_6_OR_ABOVE} + // Delphi 6 and above raise an exception when an error occures while + // creating a thread (eg. not enough address space to allocate a stack) + // Delphi 5 and below don't do that, which results in a TIdThread + // instance with an invalid handle in it, therefore we raise the + // exceptions manually on D5 and below + if (ThreadID = 0) then begin + IndyRaiseLastError; + end; + {$ENDIF} + {$ENDIF} + // Last, so we only do this if successful + GThreadCount.Increment; +end; + +destructor TIdThread.Destroy; +begin + inherited Destroy; + try + if itoReqCleanup in FOptions then begin + Cleanup; + end; + finally + // RLebeau- clean up the Yarn one more time, in case the thread was + // terminated after the Yarn was assigned but the thread was not + // re-started, so the Yarn would not be freed in Cleanup() + try + IdDisposeAndNil(FYarn); + finally + // Protect FLock if thread was resumed by Start Method and we are still there. + // This usually happens if Exception was raised in BeforeRun for some reason + // And thread was terminated there before Start method is completed. + FLock.Enter; try + finally FLock.Leave; end; + + FreeAndNil(FLock); + GThreadCount.Decrement; + end; + end; +end; + +procedure TIdThread.Start; +begin + FLock.Enter; try + if Stopped then begin + // Resume is also called for smTerminate as .Start can be used to initially start a + // thread that is created suspended + if Terminated then begin + Include(FOptions,itoStopped); + end else begin + Exclude(FOptions,itoStopped); + end; + {$IFDEF DEPRECATED_TThread_SuspendResume} + Suspended := False; + {$ELSE} + Resume; + {$ENDIF} + {APR: [in past] thread can be destroyed here! now Destroy wait FLock} + end; + finally FLock.Leave; end; +end; + +procedure TIdThread.Stop; +begin + FLock.Enter; try + if not Stopped then begin + case FStopMode of + smTerminate: Terminate; + smSuspend: {DO not suspend here. Suspend is immediate. See Execute for implementation}; + end; + Include(FOptions, itoStopped); + end; + finally FLock.Leave; end; +end; + +function TIdThread.GetStopped: Boolean; +begin + if Assigned(FLock) then begin + FLock.Enter; try + // Suspended may be True if checking stopped from another thread + Result := Terminated or (itoStopped in FOptions) or Suspended; + finally FLock.Leave; end; + end else begin + Result := True; //user call Destroy + end; +end; + +procedure TIdThread.DoStopped; +begin + if Assigned(OnStopped) then begin + OnStopped(Self); + end; +end; + +procedure TIdThread.DoException(AException: Exception); +begin + if Assigned(FOnException) then begin + FOnException(Self, AException); + end; +end; + +procedure TIdThread.Terminate; +begin + //this assert can only raise if terminate is called on an already-destroyed thread + Assert(FLock<>nil); + + FLock.Enter; try + Include(FOptions, itoStopped); + inherited Terminate; + finally FLock.Leave; end; +end; + +procedure TIdThread.Cleanup; +begin + Exclude(FOptions, itoReqCleanup); + IdDisposeAndNil(FYarn); + if itoDataOwner in FOptions then begin + FreeAndNil({$IFDEF USE_OBJECT_ARC}FDataObject{$ELSE}FData{$ENDIF}); + end; + {$IFDEF USE_OBJECT_ARC} + FDataValue := 0; + {$ENDIF} +end; + +function TIdThread.HandleRunException(AException: Exception): Boolean; +begin + // Default behavior: Exception is death sentence + Result := False; +end; + +procedure TIdThread.Synchronize(Method: TThreadMethod); +begin + inherited Synchronize(Method); +end; +//BGO:TODO +//procedure TIdThread.Synchronize(Method: TMethod); +//begin +// inherited Synchronize(TThreadMethod(Method)); +//end; + +{ TIdThreadWithTask } + +procedure TIdThreadWithTask.AfterRun; +begin + FTask.DoAfterRun; + inherited AfterRun; +end; + +procedure TIdThreadWithTask.BeforeRun; +begin + inherited BeforeRun; + FTask.DoBeforeRun; +end; + +procedure TIdThreadWithTask.DoException(AException: Exception); +begin + inherited DoException(AException); + FTask.DoException(AException); +end; + +constructor TIdThreadWithTask.Create(ATask: TIdTask; const AName: string); +begin + inherited Create(True, True, AName); + FTask := ATask; +end; + +destructor TIdThreadWithTask.Destroy; +begin + FreeAndNil(FTask); + inherited Destroy; +end; + +procedure TIdThreadWithTask.Run; +begin + if not FTask.DoRun then begin + Stop; + end; +end; + +procedure TIdThreadWithTask.SetTask(AValue: TIdTask); +begin + if FTask <> AValue then begin + FreeAndNil(FTask); + FTask := AValue; + end; +end; + +{$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} +type + TIdThreadSafeIntegerAccess = class(TIdThreadSafeInteger); +{$ENDIF} + +initialization + // RLebeau 7/19/09: According to RAID #271221: + // + // "Indy always names the main thread. It should not name the main thread, + // it should only name threads that it creates. This basically means that + // any app that uses Indy will end up with the main thread named "Main". + // + // The IDE currently names it's main thread, but because Indy is used by + // the dcldbx140.bpl package which gets loaded by the IDE, the name used + // for the main thread always ends up being overwritten with the name + // Indy gives it." + // + // So, DO NOT uncomment the following line... + // SetThreadName('Main'); {do not localize} + + GThreadCount := TIdThreadSafeInteger.Create; + {$IFNDEF FREE_ON_FINAL} + {$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} + IndyRegisterExpectedMemoryLeak(GThreadCount); + IndyRegisterExpectedMemoryLeak(TIdThreadSafeIntegerAccess(GThreadCount).FCriticalSection); + {$ENDIF} + {$ENDIF} +finalization + // This call hangs if not all threads have been properly destroyed. + // But without this, bad threads can often have worse results. Catch 22. +// TIdThread.WaitAllThreadsTerminated; + + {$IFDEF FREE_ON_FINAL} + //only enable this if you know your code exits thread-clean + FreeAndNil(GThreadCount); + {$ENDIF} +end. diff --git a/indy/Core/IdThreadComponent.pas b/indy/Core/IdThreadComponent.pas new file mode 100644 index 0000000..beb5696 --- /dev/null +++ b/indy/Core/IdThreadComponent.pas @@ -0,0 +1,703 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.13 9/30/2004 2:26:04 PM BGooijen + wrong property was referenced + + Rev 1.12 2004.02.03 4:17:12 PM czhower + For unit name changes. + + Rev 1.11 2004.01.20 10:03:38 PM czhower + InitComponent + + Rev 1.10 09.11.2003 14:05:52 ARybin + AV + + Rev 1.9 08.11.2003 20:03:20 ARybin + run-time active bug + + Rev 1.8 10/15/2003 8:48:58 PM DSiders + Added resource strings for exceptions raised when setting thread component + properties. + + Rev 1.7 2003.10.11 9:58:04 PM czhower + Several bug fixes + + Rev 1.6 2003.10.11 5:51:54 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.5 2003.09.30 7:48:02 PM czhower + Fixed Loop and ThreadName + + Rev 1.4 9/18/2003 07:40:52 PM JPMugaas + Removed IdGlobal. + + Rev 1.3 9/16/2003 04:47:22 PM JPMugaas + Made some code follow the Indy conventions so it's easier to debug. + + Rev 1.2 2003.07.01 4:14:38 PM czhower + ThreadName and Loop added. Other bugs fixed. + + Rev 1.1 06.03.2003 12:16:52 ARybin + adapted for new IdThread + + Rev 1.0 11/13/2002 08:03:06 AM JPMugaas + + 2002-05-03 -Andrew P.Rybin + -Stphane Grobty (Fulgan) suggestion: component is Data owner, don't FreeAndNIL Data property + -special TThread.OnTerminate support (it is sync-event) + + 2002-05-23 -APR + -right support for Thread terminate +} + +unit IdThreadComponent; + +{ + UnitName: IdThreadComponent + Author: Andrew P.Rybin [magicode@mail.ru] + Creation: 12.03.2002 + Version: 0.1.0 + Purpose: + History: Based on my TmcThread +} + +interface +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdBaseComponent, IdException, IdGlobal, IdThread, SysUtils; + +const + IdThreadComponentDefaultPriority = tpNormal; + IdThreadComponentDefaultStopMode = smTerminate; + +type + TIdThreadComponent = class; + + TIdExceptionThreadComponentEvent = procedure(Sender: TIdThreadComponent; AException: Exception) of object; + TIdExceptionThreadComponentEventEx = procedure(Sender: TIdThreadComponent; AException: Exception; var VHandled: Boolean) of object; + TIdNotifyThreadComponentEvent = procedure(Sender: TIdThreadComponent) of object; + //TIdSynchronizeThreadComponentEvent = procedure(Sender: TIdThreadComponent; AData: Pointer) of object; + + TIdThreadComponent = class(TIdBaseComponent) + protected + FActive: Boolean; + FLoop: Boolean; + FPriority : TIdThreadPriority; + FStopMode : TIdThreadStopMode; + FThread: TIdThread; + FThreadName: string; + // + FOnAfterExecute: TIdNotifyThreadComponentEvent; + FOnAfterRun: TIdNotifyThreadComponentEvent; + FOnBeforeExecute: TIdNotifyThreadComponentEvent; + FOnBeforeRun: TIdNotifyThreadComponentEvent; + FOnCleanup: TIdNotifyThreadComponentEvent; + FOnException: TIdExceptionThreadComponentEvent; + FOnRun: TIdNotifyThreadComponentEvent; + FOnStopped: TIdNotifyThreadComponentEvent; + FOnTerminate: TIdNotifyThreadComponentEvent; + FOnHandleRunException: TIdExceptionThreadComponentEventEx; + // + {$IFDEF INT_THREAD_PRIORITY} + procedure DefineProperties(Filer: TFiler); override; + procedure ReadPriority(Reader: TReader); + procedure WritePriority(Writer: TWriter); + {$ENDIF} + procedure DoAfterExecute; virtual; + procedure DoAfterRun; virtual; + procedure DoBeforeExecute; virtual; + procedure DoBeforeRun; virtual; + procedure DoCleanup; virtual; + procedure DoException(AThread: TIdThread; AException: Exception); virtual; //thev + function DoHandleRunException(AException: Exception): Boolean; virtual; + procedure DoRun; virtual; + procedure DoStopped(AThread: TIdThread); virtual; //thev + procedure DoTerminate(Sender: TObject); virtual; //thev + function GetActive: Boolean; + {$IFDEF USE_OBJECT_ARC} + // When ARC is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so + // we will provide separate properties for those purposes + // + // TODO; use TValue instead of separating them + // + function GetDataObject: TObject; + function GetDataValue: PtrInt; + {$ELSE} + function GetData: TObject; + {$ENDIF} + function GetHandle: TIdThreadHandle; + function GetPriority: TIdThreadPriority; + function GetReturnValue: Integer; + function GetStopMode: TIdThreadStopMode; + function GetStopped: Boolean; + function GetSuspended: Boolean; + function GetTerminatingException: string; + function GetTerminatingExceptionClass: TClass; + function GetTerminated: Boolean; + procedure InitComponent; override; + function IsRunning: Boolean; + procedure Loaded; override; + procedure SetActive(const AValue: Boolean); virtual; + {$IFDEF USE_OBJECT_ARC} + procedure SetDataObject(const AValue: TObject); + procedure SetDataValue(const AValue: PtrInt); + {$ELSE} + procedure SetData(const AValue: TObject); + {$ENDIF} + procedure SetLoop(const AValue: Boolean); + procedure SetThreadName(const AValue: string); + procedure SetOnTerminate(const AValue: TIdNotifyThreadComponentEvent); + procedure SetPriority(const AValue: TIdThreadPriority); + procedure SetReturnValue(const AValue: Integer); + procedure SetStopMode(const AValue: TIdThreadStopMode); + public + destructor Destroy; override; + procedure Start; virtual; + procedure Stop; virtual; + procedure Synchronize(AMethod: TThreadMethod); + procedure Terminate; virtual; + procedure TerminateAndWaitFor; virtual; + function WaitFor: UInt32; + // Properties + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read GetDataObject write SetDataObject; + property DataValue: PtrInt read GetDataValue write SetDataValue; + {$ELSE} + property Data: TObject read GetData write SetData; + {$ENDIF} + property Handle: TIdThreadHandle read GetHandle; + property ReturnValue: Integer read GetReturnValue write SetReturnValue; + property Stopped: Boolean read GetStopped; + property Suspended: Boolean read GetSuspended; + property TerminatingException: string read GetTerminatingException; + property TerminatingExceptionClass: TClass read GetTerminatingExceptionClass; + property Terminated: Boolean read GetTerminated; + {$IFDEF INT_THREAD_PRIORITY} + property Priority: TIdThreadPriority read GetPriority write SetPriority; + {$ENDIF} + published + property Active: Boolean read GetActive write SetActive; + property Loop: Boolean read FLoop write SetLoop; + {$IFNDEF INT_THREAD_PRIORITY} + property Priority: TIdThreadPriority read GetPriority write SetPriority; + {$ENDIF} + property StopMode: TIdThreadStopMode read GetStopMode write SetStopMode; + property ThreadName: string read FThreadName write SetThreadName; + // Events + property OnAfterExecute: TIdNotifyThreadComponentEvent read FOnAfterExecute write FOnAfterExecute; + property OnAfterRun: TIdNotifyThreadComponentEvent read FOnAfterRun write FOnAfterRun; + property OnBeforeExecute: TIdNotifyThreadComponentEvent read FOnBeforeExecute write FOnBeforeExecute; + property OnBeforeRun: TIdNotifyThreadComponentEvent read FOnBeforeRun write FOnBeforeRun; + property OnCleanup: TIdNotifyThreadComponentEvent read FOnCleanup write FOnCleanup; + property OnException: TIdExceptionThreadComponentEvent read FOnException write FOnException; + property OnHandleRunException: TIdExceptionThreadComponentEventEx + read FOnHandleRunException write FOnHandleRunException; + property OnRun: TIdNotifyThreadComponentEvent read FOnRun write FOnRun; + property OnStopped: TIdNotifyThreadComponentEvent read FOnStopped + write FOnStopped; + property OnTerminate: TIdNotifyThreadComponentEvent read FOnTerminate + write SetOnTerminate; + end; + + //For Component-writers ONLY! + TIdThreadEx = class(TIdThread) + protected + FThreadComponent: TIdThreadComponent; + // + procedure AfterRun; override; + procedure AfterExecute; override; + procedure BeforeExecute; override; + procedure BeforeRun; override; + procedure Cleanup; override; + function HandleRunException(AException: Exception): Boolean; override; + procedure Run; override; + public + constructor Create(AThreadComponent: TIdThreadComponent); reintroduce; + end; + +implementation + +uses + IdResourceStringsCore; + +{ TIdThreadEx } + +procedure TIdThreadEx.AfterExecute; +begin + try + FThreadComponent.DoAfterExecute; + finally + FThreadComponent.FActive := FALSE; + end; +end; + +procedure TIdThreadEx.AfterRun; +begin + FThreadComponent.DoAfterRun; +end; + +procedure TIdThreadEx.BeforeExecute; +begin + FThreadComponent.DoBeforeExecute; +end; + +procedure TIdThreadEx.BeforeRun; +begin + FThreadComponent.DoBeforeRun; +end; + +procedure TIdThreadEx.Cleanup; +begin + inherited Cleanup; + FThreadComponent.DoCleanup; +end; + +constructor TIdThreadEx.Create(AThreadComponent: TIdThreadComponent); +begin + inherited Create(True, AThreadComponent.Loop, iif(AThreadComponent.ThreadName = '' + , AThreadComponent.Name, AThreadComponent.ThreadName)); + Exclude(FOptions, itoDataOwner); //TIdThreadComponent is data owner + FThreadComponent := AThreadComponent; + FOnException := FThreadComponent.DoException; + FOnStopped := FThreadComponent.DoStopped; +end; + +function TIdThreadEx.HandleRunException(AException: Exception): Boolean; +begin + Result := FThreadComponent.DoHandleRunException(AException); +end; + +procedure TIdThreadEx.Run; +begin + FThreadComponent.DoRun; +end; + +{ TIdThreadComponent } + +{$IFDEF INT_THREAD_PRIORITY} +procedure TIdThreadComponent.DefineProperties(Filer: TFiler); +begin + inherited; + Filer.DefineProperty('Priority', ReadPriority, WritePriority, FPriority <> tpNormal); +end; + +procedure TIdThreadComponent.ReadPriority(Reader: TReader); +var + Value: Integer; +begin + if Reader.NextValue = vaIdent then + begin + // an older DFM that stored TThreadPriority as enum value names is being read, so convert to integer ... + case PosInStrArray(Reader.ReadIdent, ['tpIdle', 'tpLowest', 'tpLower', 'tpNormal', 'tpHigher', 'tpHighest', 'tpTimeCritical'], False) of {do not localize} + 0: Value := tpIdle; + 1: Value := tpLowest; + 2: Value := tpLower; + 3: Value := tpNormal; + 4: Value := tpHigher; + 5: Value := tpHighest; + 6: Value := tpTimeCritical; + else + Value := tpNormal; + end; + end else + begin + Value := Reader.ReadInteger; + if Value < -20 then begin + Value := -20; + end + else if Value > 19 then begin + Value := 19; + end; + end; + FPriority := Value; +end; + +procedure TIdThreadComponent.WritePriority(Writer: TWriter); +begin + Writer.WriteInteger(FPriority); +end; +{$ENDIF} + +procedure TIdThreadComponent.DoAfterExecute; +begin + if Assigned(FOnAfterExecute) then + begin + FOnAfterExecute(SELF); + end; +end; + +procedure TIdThreadComponent.DoAfterRun; +begin + if Assigned(FOnAfterRun) then + begin + FOnAfterRun(SELF); + end; +end; + +procedure TIdThreadComponent.DoBeforeExecute; +begin + if Assigned(FOnBeforeExecute) then + begin + FOnBeforeExecute(SELF); + end; +end; + +procedure TIdThreadComponent.DoBeforeRun; +begin + if Assigned(FOnBeforeRun) then + begin + FOnBeforeRun(SELF); + end; +end; + +procedure TIdThreadComponent.DoCleanup; +begin + if Assigned(FOnCleanup) then + begin + FOnCleanup(SELF); + end; +end; + +destructor TIdThreadComponent.Destroy; +begin + {FThread.TerminateAndWaitFor;} + //make sure thread is not active before we attempt to destroy it + if Assigned(FThread) then begin + FThread.Terminate; + FThread.Start;//resume for terminate + end; + IdDisposeAndNil(FThread); + inherited Destroy; +end; + +procedure TIdThreadComponent.DoException(AThread: TIdThread; AException: Exception); +begin + if Assigned(FOnException) then begin + FOnException(SELF,AException); + end; +end; + +function TIdThreadComponent.DoHandleRunException(AException: Exception): Boolean; +begin + Result := FALSE;//not handled + if Assigned(FOnHandleRunException) then begin + FOnHandleRunException(SELF,AException,Result); + end; +end; + +procedure TIdThreadComponent.DoStopped(AThread: TIdThread); +begin + if Assigned(FOnStopped) then begin + FOnStopped(SELF); + end; +end; + +procedure TIdThreadComponent.DoTerminate; +begin + if Assigned(FOnTerminate) then begin + FOnTerminate(SELF); + end; +end; + +{$IFDEF USE_OBJECT_ARC} + +function TIdThreadComponent.GetDataObject: TObject; +begin + Result := FThread.DataObject; +end; + +function TIdThreadComponent.GetDataValue: PtrInt; +begin + Result := FThread.DataValue; +end; + +{$ELSE} + +function TIdThreadComponent.GetData: TObject; +begin + Result := FThread.Data; +end; + +{$ENDIF} + +function TIdThreadComponent.GetHandle: TIdThreadHandle; +begin + Result := GetThreadHandle(FThread); +end; + +function TIdThreadComponent.GetReturnValue: Integer; +begin + Result := FThread.ReturnValue; +end; + +function TIdThreadComponent.GetStopMode: TIdThreadStopMode; +begin + if FThread = NIL then begin + Result := FStopMode; + end else begin + Result := FThread.StopMode; + end; +end; + +function TIdThreadComponent.GetStopped: Boolean; +begin + if Assigned(FThread) then begin + Result := FThread.Stopped; + end else begin + Result := TRUE; + end; +end; + +function TIdThreadComponent.GetSuspended: Boolean; +begin + Result := FThread.Suspended; +end; + +function TIdThreadComponent.GetTerminated: Boolean; +begin + if Assigned(FThread) then begin + Result := FThread.Terminated; + end else begin + Result := TRUE; + end; +end; + +function TIdThreadComponent.GetTerminatingException: string; +begin + Result := FThread.TerminatingException; +end; + +function TIdThreadComponent.GetTerminatingExceptionClass: TClass; +begin + Result := FThread.TerminatingExceptionClass; +end; + +procedure TIdThreadComponent.Loaded; +begin + inherited Loaded; + // Active = True must not be performed before all other props are loaded + if Assigned(FThread) and Assigned(OnTerminate) then begin + FThread.OnTerminate := DoTerminate; + end; + + if FActive then begin + // Retoggle for load since we ignore during loading until all properties + // are ready + FActive := False; + Active := True; + end; +end; + +procedure TIdThreadComponent.DoRun; +begin + if Assigned(FOnRun) then begin + FOnRun(SELF); + end; +end; + +procedure TIdThreadComponent.SetActive(const AValue: Boolean); +begin + if IsDesignTime or IsLoading then begin + FActive := AValue; + end + else if Active <> AValue then begin + if AValue then begin + Start; + end else begin + Stop; + end; + FActive := AValue; + end; +end; + +{$IFDEF USE_OBJECT_ARC} + +procedure TIdThreadComponent.SetDataObject(const AValue: TObject); +begin +// this should not be accessed at design-time. + FThread.DataObject := AValue; +end; + +procedure TIdThreadComponent.SetDataValue(const AValue: PtrInt); +begin +// this should not be accessed at design-time. + FThread.DataValue := AValue; +end; + +{$ELSE} + +procedure TIdThreadComponent.SetData(const AValue: TObject); +begin +// this should not be accessed at design-time. + FThread.Data := AValue; +end; + +{$ENDIF} + +procedure TIdThreadComponent.SetReturnValue(const AValue: Integer); +begin +// this should not be accessed at design-time. + FThread.ReturnValue := AValue; +end; + +procedure TIdThreadComponent.SetStopMode(const AValue: TIdThreadStopMode); +begin + if Assigned(FThread) and NOT FThread.Terminated then begin + FThread.StopMode := AValue; + end; + FStopMode := AValue; +end; + +procedure TIdThreadComponent.Start; +begin + if not IsDesignTime then begin + if Assigned(FThread) and FThread.Terminated then begin + IdDisposeAndNil(FThread); + end; + + if not Assigned(FThread) then begin + FThread := TIdThreadEx.Create(Self); + end; + + // MUST read from F variants as thread is now created + + if Assigned(FOnTerminate) then begin + FThread.OnTerminate := DoTerminate; + end else begin + FThread.OnTerminate := nil; + end; + + FThread.Name := FThreadName; + FThread.Loop := FLoop; + FThread.Priority := FPriority; + FThread.StopMode := FStopMode; + + FThread.Start; + end; +end; + +procedure TIdThreadComponent.Stop; +begin + if Assigned(FThread) then begin + FThread.Stop; + end; +end; + +procedure TIdThreadComponent.Synchronize(AMethod: TThreadMethod); +begin + FThread.Synchronize(AMethod); +end; + +procedure TIdThreadComponent.Terminate; +begin + FThread.Terminate; +end; + +procedure TIdThreadComponent.TerminateAndWaitFor; +begin + FThread.TerminateAndWaitFor; +end; + +function TIdThreadComponent.WaitFor: UInt32; +begin + Result := FThread.WaitFor; +end; + +function TIdThreadComponent.GetPriority: TIdThreadPriority; +begin + if FThread <> nil then begin + Result := FThread.Priority; + end else begin + Result := FPriority; + end; +end; + +procedure TIdThreadComponent.SetPriority(const AValue: TIdThreadPriority); +begin + if Assigned(FThread) then begin + if not FThread.Terminated then begin + FThread.Priority := AValue; + end; + end; + FPriority := AValue; +end; + +function TIdThreadComponent.GetActive: Boolean; +begin + Result := False; + if IsDesignTime then begin + Result := FActive; + end else if FThread <> nil then begin + Result := IsRunning; + end; +end; + +procedure TIdThreadComponent.SetOnTerminate(const AValue: TIdNotifyThreadComponentEvent); +begin + FOnTerminate := AValue; + if Assigned(FThread) then begin + if Assigned(AValue) then begin + FThread.OnTerminate := DoTerminate; + end else begin + FThread.OnTerminate := nil; + end; + end; +end; + +procedure TIdThreadComponent.SetLoop(const AValue: Boolean); +begin + if IsRunning then begin + raise EIdException.Create(RSThreadComponentLoopAlreadyRunning); + end; + FLoop := AValue; +end; + +procedure TIdThreadComponent.SetThreadName(const AValue: string); +begin + if IsRunning then begin + raise EIdException.Create(RSThreadComponentThreadNameAlreadyRunning); + end; + FThreadName := AValue; +end; + +function TIdThreadComponent.IsRunning: Boolean; +begin + if FThread = nil then begin + Result := False; + end else begin + Result := not FThread.Stopped + end; +end; + +procedure TIdThreadComponent.InitComponent; +begin + inherited InitComponent; + StopMode := IdThreadComponentDefaultStopMode; + Priority := IdThreadComponentDefaultPriority; +end; + +end. + diff --git a/indy/Core/IdThreadSafe.pas b/indy/Core/IdThreadSafe.pas new file mode 100644 index 0000000..0abd947 --- /dev/null +++ b/indy/Core/IdThreadSafe.pas @@ -0,0 +1,896 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 2004.10.29 8:49:00 PM czhower + Fixed a constructor. + + Rev 1.9 2004.10.27 9:20:04 AM czhower + For TIdStrings + + Rev 1.8 10/26/2004 8:43:02 PM JPMugaas + Should be more portable with new references to TIdStrings and TIdStringList. + + Rev 1.7 4/12/2004 4:50:36 PM JPMugaas + TIdThreadSafeInt64 created for some internal work but I figured it would help + with other stuff. + + Rev 1.6 3/26/2004 1:09:28 PM JPMugaas + New ThreadSafe objects that I needed for some other work I'm doing. + + Rev 1.5 3/17/2004 8:57:32 PM JPMugaas + Increment and decrement overloads added for quick math in the + TIdThreadSafeCardinal and TIdThreadSafeInteger. I need this for personal + work. + + Rev 1.4 2004.02.25 8:23:20 AM czhower + Small changes + + Rev 1.3 2004.02.03 4:17:00 PM czhower + For unit name changes. + + Rev 1.2 2004.01.22 5:59:12 PM czhower + IdCriticalSection + + Rev 1.1 3/27/2003 12:29:46 AM BGooijen + Added TIdThreadSafeList.Assign + + Rev 1.0 11/13/2002 09:01:54 AM JPMugaas +} + +unit IdThreadSafe; + +interface + +{$I IdCompilerDefines.inc} +//we need to put this in Delphi mode to work + +uses + Classes, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdGlobal; + +type + TIdThreadSafe = class + protected + FCriticalSection: TIdCriticalSection; + public + constructor Create; virtual; + destructor Destroy; override; + procedure Lock; + procedure Unlock; + end; + + // Yes we know that integer operations are "atomic". However we do not like to rely on + // internal compiler implementation. This is a safe and proper way to keep our code independent + TIdThreadSafeInteger = class(TIdThreadSafe) + protected + FValue: Integer; + // + function GetValue: Integer; + procedure SetValue(const AValue: Integer); + public + function Decrement: Integer; overload; + function Decrement(const AValue : Integer) : Integer; overload; + function Increment: Integer; overload; + function Increment(const AValue : Integer) : Integer; overload; + // + property Value: Integer read GetValue write SetValue; + end; + TIdThreadSafeInt32 = TIdThreadSafeInteger; + + TIdThreadSafeBoolean = class(TIdThreadSafe) + protected + FValue: Boolean; + // + function GetValue: Boolean; + procedure SetValue(const AValue: Boolean); + public + function Toggle: Boolean; + // + property Value: Boolean read GetValue write SetValue; + end; + + TIdThreadSafeCardinal = class(TIdThreadSafe) + protected + FValue: Cardinal; + // + function GetValue: Cardinal; + procedure SetValue(const AValue: Cardinal); + public + function Decrement: Cardinal; overload; + function Decrement(const AValue : Cardinal) : Cardinal; overload; + function Increment: Cardinal; overload; + function Increment(const AValue : Cardinal) : Cardinal; overload; + // + property Value: Cardinal read GetValue write SetValue; + end; + TIdThreadSafeUInt32 = TIdThreadSafeCardinal; + + TIdThreadSafeInt64 = class(TIdThreadSafe) + protected + FValue: Int64; + // + function GetValue: Int64; + procedure SetValue(const AValue: Int64); + public + function Decrement: Int64; overload; + function Decrement(const AValue : Int64) : Int64; overload; + function Increment: Int64; overload; + function Increment(const AValue : Int64) : Int64; overload; + // + property Value: Int64 read GetValue write SetValue; + end; + + TIdThreadSafeString = class(TIdThreadSafe) + protected + FValue: string; + // + function GetValue: string; + procedure SetValue(const AValue: string); + public + procedure Append(const AValue: string); + procedure Prepend(const AValue: string); + // + property Value: string read GetValue write SetValue; + end; + + TIdThreadSafeStringList = class(TIdThreadSafe) + protected + FValue: TStringList; + // + function GetValue(const AName: string): string; + procedure SetValue(const AName, AValue: string); + public + constructor Create; override; + destructor Destroy; override; + procedure Add(const AItem: string); + procedure AddObject(const AItem: string; AObject: TObject); + procedure Clear; + function Empty: Boolean; + function Lock: TStringList; reintroduce; + function ObjectByItem(const AItem: string): TObject; + procedure Remove(const AItem: string); + procedure Unlock; reintroduce; + property Values[const AName: string]: string read GetValue write SetValue; + end; + + TIdThreadSafeDateTime = class(TIdThreadSafe) + protected + FValue : TDateTime; + function GetValue: TDateTime; + procedure SetValue(const AValue: TDateTime); + public + procedure Add(const AValue : TDateTime); + procedure Subtract(const AValue : TDateTime); + property Value: TDateTime read GetValue write SetValue; + end; + + //In D7, a double is the same as a TDateTime. In DotNET, it is not. + TIdThreadSafeDouble = class(TIdThreadSafe) + protected + FValue : Double; + function GetValue: Double; + procedure SetValue(const AValue: Double); + public + procedure Add(const AValue : Double); + procedure Subtract(const AValue : Double); + property Value: Double read GetValue write SetValue; + end; + + //TODO: Later make this descend from TIdThreadSafe instead + {$IFDEF HAS_GENERICS_TThreadList} + TIdThreadSafeList = class(TThreadList) + {$ELSE} + TIdThreadSafeList = class(TThreadList) + {$ENDIF} + public + procedure Assign(AThreadList: TThreadList{$IFDEF HAS_GENERICS_TThreadList}{$ENDIF});overload; + procedure Assign(AList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF});overload; + // Here to make it virtual + constructor Create; virtual; + function IsCountLessThan(const AValue: UInt32): Boolean; + function Count:Integer; + function IsEmpty: Boolean; + function Pop: {$IFDEF HAS_GENERICS_TThreadList}T{$ELSE}Pointer{$ENDIF}; + function Pull: {$IFDEF HAS_GENERICS_TThreadList}T{$ELSE}Pointer{$ENDIF}; + end; + + {$IFDEF HAS_GENERICS_TObjectList} + TIdThreadSafeObjectList = class(TIdThreadSafeList) + {$ELSE} + TIdThreadSafeObjectList = class(TIdThreadSafeList) + {$ENDIF} + {$IFNDEF USE_OBJECT_ARC} + private + FOwnsObjects: Boolean; + {$ENDIF} + public + {$IFNDEF USE_OBJECT_ARC} + constructor Create; override; + destructor Destroy; override; + {$ENDIF} + procedure ClearAndFree; + {$IFNDEF USE_OBJECT_ARC} + property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects; + {$ENDIF} + end; + +implementation + +uses + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + {$IFDEF VCL_XE3_OR_ABOVE} + System.SyncObjs, + {$ENDIF} + SysUtils; + +{ TIdThreadSafe } + +constructor TIdThreadSafe.Create; +begin + inherited Create; + FCriticalSection := TIdCriticalSection.Create; +end; + +destructor TIdThreadSafe.Destroy; +begin + FreeAndNil(FCriticalSection); + inherited Destroy; +end; + +procedure TIdThreadSafe.Lock; +begin + FCriticalSection.Enter; +end; + +procedure TIdThreadSafe.Unlock; +begin + FCriticalSection.Leave; +end; + +{ TIdThreadSafeInteger } + +function TIdThreadSafeInteger.Decrement: Integer; +begin + Lock; + try + Result := FValue; + Dec(FValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInteger.Decrement(const AValue: Integer): Integer; +begin + Lock; + try + Result := FValue; + Dec(FValue,AValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInteger.GetValue: Integer; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeInteger.Increment: Integer; +begin + Lock; + try + Result := FValue; + Inc(FValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInteger.Increment(const AValue: Integer): Integer; +begin + Lock; + try + Result := FValue; + Inc(FValue,AValue); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeInteger.SetValue(const AValue: Integer); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeString } + +procedure TIdThreadSafeString.Append(const AValue: string); +begin + Lock; + try + FValue := FValue + AValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeString.GetValue: string; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeString.Prepend(const AValue: string); +begin + Lock; + try + FValue := AValue + FValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeString.SetValue(const AValue: string); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeStringList } + +procedure TIdThreadSafeStringList.Add(const AItem: string); +begin + Lock; + try + FValue.Add(AItem); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeStringList.AddObject(const AItem: string; AObject: TObject); +begin + Lock; + try + FValue.AddObject(AItem, AObject); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeStringList.Clear; +begin + Lock; + try + FValue.Clear; + finally + Unlock; + end; +end; + +constructor TIdThreadSafeStringList.Create; +begin + inherited Create; + FValue := TStringList.Create; +end; + +destructor TIdThreadSafeStringList.Destroy; +begin + inherited Lock; + try + FreeAndNil(FValue); + finally + inherited Unlock; + end; + inherited Destroy; +end; + +function TIdThreadSafeStringList.Empty: Boolean; +begin + Lock; + try + Result := FValue.Count = 0; + finally Unlock; end; +end; + +function TIdThreadSafeStringList.GetValue(const AName: string): string; +begin + Lock; + try + Result := FValue.Values[AName]; + finally + Unlock; + end; +end; + +function TIdThreadSafeStringList.Lock: TStringList; +begin + inherited Lock; + Result := FValue; +end; + +function TIdThreadSafeStringList.ObjectByItem(const AItem: string): TObject; +var + i: Integer; +begin + Result := nil; + Lock; + try + i := FValue.IndexOf(AItem); + if i > -1 then begin + Result := FValue.Objects[i]; + end; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeStringList.Remove(const AItem: string); +var + i: Integer; +begin + Lock; + try + i := FValue.IndexOf(AItem); + if i > -1 then begin + FValue.Delete(i); + end; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeStringList.SetValue(const AName, AValue: string); +begin + Lock; + try + FValue.Values[AName] := AValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeStringList.Unlock; +begin + inherited Unlock; +end; + +{ TIdThreadSafeCardinal } + +function TIdThreadSafeCardinal.Decrement: Cardinal; +begin + Lock; + try + Result := FValue; + Dec(FValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeCardinal.Decrement(const AValue: Cardinal): Cardinal; +begin + Lock; + try + Result := FValue; + Dec(FValue,AValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeCardinal.GetValue: Cardinal; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeCardinal.Increment: Cardinal; +begin + Lock; + try + Result := FValue; + Inc(FValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeCardinal.Increment(const AValue: Cardinal): Cardinal; +begin + Lock; + try + Result := FValue; + Inc(FValue,AValue); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeCardinal.SetValue(const AValue: Cardinal); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeList } + +function TIdThreadSafeList{$IFDEF HAS_GENERICS_TThreadList}{$ENDIF}.IsCountLessThan(const AValue: UInt32): Boolean; +begin + if Assigned(Self) then begin + Result := UInt32(Count) < AValue; + end else begin + Result := True; // none always < + end; +end; + +function TIdThreadSafeList{$IFDEF HAS_GENERICS_TThreadList}{$ENDIF}.IsEmpty: Boolean; +begin + Result := IsCountLessThan(1); +end; + +{$IFDEF HAS_GENERICS_TThreadList} +function TIdThreadSafeList.Pop: T; +{$ELSE} +function TIdThreadSafeList.Pop: Pointer; +{$ENDIF} +var + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; +begin + LList := LockList; + try + if LList.Count > 0 then begin + Result := LList.Items[Count - 1]; + LList.Delete(Count - 1); + end else begin + Result := {$IFDEF HAS_GENERICS_TThreadList}Default(T){$ELSE}nil{$ENDIF}; + end; + finally + UnlockList; + end; +end; + +{$IFDEF HAS_GENERICS_TThreadList} +function TIdThreadSafeList.Pull: T; +{$ELSE} +function TIdThreadSafeList.Pull: Pointer; +{$ENDIF} +var + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; +begin + LList := LockList; + try + if LList.Count > 0 then begin + Result := LList.Items[0]; + LList.Delete(0); + end else begin + Result := {$IFDEF HAS_GENERICS_TThreadList}Default(T){$ELSE}nil{$ENDIF}; + end; + finally + UnlockList; + end; +end; + +{$IFDEF HAS_GENERICS_TThreadList} +procedure TIdThreadSafeList.Assign(AList: TList); +{$ELSE} +procedure TIdThreadSafeList.Assign(AList: TList); +{$ENDIF} +var + i: integer; + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; +begin + LList := LockList; + try + LList.Clear; + LList.Capacity := AList.Capacity; + for i := 0 to AList.Count - 1 do begin + LList.Add(AList.Items[i]); + end; + finally + UnlockList; + end; +end; + +{$IFDEF HAS_GENERICS_TThreadList} +procedure TIdThreadSafeList.Assign(AThreadList: TThreadList); +{$ELSE} +procedure TIdThreadSafeList.Assign(AThreadList: TThreadList); +{$ENDIF} +var + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; +begin + LList := AThreadList.LockList; + try + Assign(LList); + finally + AThreadList.UnlockList; + end; +end; + +constructor TIdThreadSafeList{$IFDEF HAS_GENERICS_TThreadList}{$ENDIF}.Create; +begin + inherited Create; +end; + +function TIdThreadSafeList{$IFDEF HAS_GENERICS_TThreadList}{$ENDIF}.Count: Integer; +var + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; +begin + LList := LockList; + try + Result := LList.Count; + finally + UnlockList; + end; +end; + +{ TIdThreadSafeObjectList } + +{$IFNDEF USE_OBJECT_ARC} + +constructor TIdThreadSafeObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}.Create; +begin + inherited Create; + OwnsObjects := False; +end; + +destructor TIdThreadSafeObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}.Destroy; +begin + if OwnsObjects then ClearAndFree; + inherited; +end; + +{$ENDIF} + +procedure TIdThreadSafeObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}.ClearAndFree; +var + LList: TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}; + i: Integer; +begin + LList := LockList; + try + {$IFNDEF USE_OBJECT_ARC} + for i := 0 to LList.Count-1 do begin + {$IFDEF HAS_GENERICS_TList}LList[i]{$ELSE}TObject(LList[i]){$ENDIF}.Free; + end; + {$ENDIF} + LList.Clear; + finally + UnlockList; + end; +end; + +{ TIdThreadSafeBoolean } + +function TIdThreadSafeBoolean.GetValue: Boolean; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeBoolean.SetValue(const AValue: Boolean); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeBoolean.Toggle: Boolean; +begin + Lock; + try + FValue := not FValue; + Result := FValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeDateTime } + +procedure TIdThreadSafeDateTime.Add(const AValue: TDateTime); +begin + Lock; + try + FValue := FValue + AValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeDateTime.GetValue: TDateTime; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeDateTime.SetValue(const AValue: TDateTime); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeDateTime.Subtract(const AValue: TDateTime); +begin + Lock; + try + FValue := FValue - AValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeDouble } + +procedure TIdThreadSafeDouble.Add(const AValue: Double); +begin + Lock; + try + FValue := FValue + AValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeDouble.GetValue: Double; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeDouble.SetValue(const AValue: Double); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeDouble.Subtract(const AValue: Double); +begin + Lock; + try + FValue := FValue - AValue; + finally + Unlock; + end; +end; + +{ TIdThreadSafeInt64 } + +function TIdThreadSafeInt64.Decrement(const AValue: Int64): Int64; +begin + Lock; + try + Result := FValue; + Dec(FValue,AValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInt64.Decrement: Int64; +begin + Lock; + try + Result := FValue; + Dec(FValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInt64.GetValue: Int64; +begin + Lock; + try + Result := FValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeInt64.Increment(const AValue: Int64): Int64; +begin + Lock; + try + Result := FValue; + Inc(FValue,AValue); + finally + Unlock; + end; +end; + +function TIdThreadSafeInt64.Increment: Int64; +begin + Lock; + try + Result := FValue; + Inc(FValue); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeInt64.SetValue(const AValue: Int64); +begin + Lock; + try + FValue := AValue; + finally + Unlock; + end; +end; + +end. diff --git a/indy/Core/IdTraceRoute.pas b/indy/Core/IdTraceRoute.pas new file mode 100644 index 0000000..d5e541b --- /dev/null +++ b/indy/Core/IdTraceRoute.pas @@ -0,0 +1,105 @@ +{ + $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$ +} + +unit IdTraceRoute; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdIcmpClient, IdRawBase, IdRawClient, IdThread; + +type + TIdTraceRoute = class(TIdCustomICMPClient) + protected + FResolveHostNames : Boolean; + procedure DoReply; override; + public + procedure Trace; + published + {$IFDEF DOTNET_2_OR_ABOVE} + property IPVersion; + {$ENDIF} + property PacketSize; + property ReceiveTimeout; + property ResolveHostNames : Boolean read FResolveHostNames write FResolveHostNames; + property OnReply; + end; + +implementation + +uses + IdGlobal, IdStack; + +{ TIdTraceRoute } + +procedure TIdTraceRoute.DoReply; +begin + if FResolveHostNames and + (PosInStrArray(FReplyStatus.FromIpAddress, ['0.0.0.0', '::0']) = -1) then {do not localize} + begin + //resolve IP to hostname + try + FReplyStatus.HostName := GStack.HostByAddress(FReplyStatus.FromIpAddress, FBinding.IPVersion); + except + { + We do things this way because we are likely have a reverse DNS + failure if you have a computer with IP address and no DNS name at all. + } + FReplyStatus.HostName := FReplyStatus.FromIpAddress; + end; + end; + inherited DoReply; +end; + +procedure TIdTraceRoute.Trace; +//In traceroute, there are a maximum of thirty echo request packets. You start +//requests with a TTL of one and keep sending them until you get to thirty or you +//get an echo response (whatever comes sooner). +var + i : Integer; + lSeq : UInt32; + LTTL : Integer; + LIPAddr : String; +begin + +// PacketSize := 64; +//We do things this way because we only want to resolve the destination host name +//only one time. Otherwise, there's a performance penalty for earch DNS resolve. + LIPAddr := GStack.ResolveHost(FHost, FBinding.IPVersion); + LSeq := $1; + LTTL := 1; + TTL := LTTL; + for i := 1 to 30 do + begin + ReplyStatus.PacketNumber := i; + InternalPing(LIPAddr, '', LSeq); + case ReplyStatus.ReplyStatusType of + rsErrorTTLExceeded, + rsTimeout : ; + else + Break; + end; + Inc(LTTL); + TTL := LTTL; + LSeq := LSeq * 2; + end; +end; + +end. diff --git a/indy/Core/IdUDPBase.pas b/indy/Core/IdUDPBase.pas new file mode 100644 index 0000000..ac5b27e --- /dev/null +++ b/indy/Core/IdUDPBase.pas @@ -0,0 +1,385 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.15 11/12/2004 11:30:18 AM JPMugaas + Expansions for IPv6. + + Rev 1.14 11/11/04 12:05:32 PM RLebeau + Updated ReceiveBuffer() to set AMSec to IdTimeoutInfinite when the + ReceiveTimeout property is 0 + + Rev 1.13 11/7/2004 11:33:30 PM JPMugaas + Now uses Connect, Disconnect, Send, and Receive similarly to the TCP Clients. + This should prevent unneeded DNS name to IP address conversions that SendTo + was doing. + + Rev 1.12 7/21/04 3:33:10 PM RLebeau + Updated TIdUDPBase.ReceiveString() to use new BytesToString() parameters + + Rev 1.11 09/06/2004 00:29:56 CCostelloe + Kylix 3 patch + + Rev 1.10 2004.02.03 4:17:00 PM czhower + For unit name changes. + + Rev 1.9 21.1.2004 . 12:31:00 DBondzhev + Fix for Indy source. Workaround for dccil bug + now it can be compiled using Compile instead of build + + Rev 1.7 10/26/2003 12:30:18 PM BGooijen + DotNet + + Rev 1.6 10/24/2003 5:18:36 PM BGooijen + Removed boolean shortcutting from .GetActive + + Rev 1.5 10/22/2003 04:40:58 PM JPMugaas + Should compile with some restored functionality. Still not finished. + + Rev 1.4 10/19/2003 9:34:30 PM BGooijen + SetSocketOption + + Rev 1.3 2003.10.11 9:58:48 PM czhower + Started on some todos + + Rev 1.2 2003.10.11 5:52:10 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.1 2003.09.30 1:23:08 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 09:02:06 AM JPMugaas +} + +unit IdUDPBase; + +interface + +{$I IdCompilerDefines.inc} +//here to flip FPC into Delphi mode + +uses + IdComponent, + IdGlobal, + IdException, + IdSocketHandle; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortA$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Idudpbase@TIdUDPBase@SetPortW$qqrxus"="@Idudpbase@TIdUDPBase@SetPort$qqrxus"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +const + ID_UDP_BUFFERSIZE = 8192; + +type + TIdUDPBase = class(TIdComponent) + protected + FBinding: TIdSocketHandle; + FBufferSize: Integer; + FDsgnActive: Boolean; + FHost: String; + FPort: TIdPort; + FReceiveTimeout: Integer; + FReuseSocket: TIdReuseSocket; + FIPVersion: TIdIPVersion; + // + FBroadcastEnabled: Boolean; + procedure BroadcastEnabledChanged; dynamic; + procedure CloseBinding; virtual; + function GetActive: Boolean; virtual; + procedure InitComponent; override; + procedure SetActive(const Value: Boolean); + procedure SetBroadcastEnabled(const AValue: Boolean); + function GetBinding: TIdSocketHandle; virtual; abstract; + procedure Loaded; override; + + function GetIPVersion: TIdIPVersion; virtual; + procedure SetIPVersion(const AValue: TIdIPVersion); virtual; + + function GetHost : String; virtual; + procedure SetHost(const AValue : String); virtual; + + function GetPort : TIdPort; virtual; + procedure SetPort(const AValue : TIdPort); virtual; + + property Host: string read GetHost write SetHost; + property Port: TIdPort read GetPort write SetPort; + public + destructor Destroy; override; + // + property Binding: TIdSocketHandle read GetBinding; + procedure Broadcast(const AData: string; const APort: TIdPort; const AIP: String = ''; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Broadcast(const AData: TIdBytes; const APort: TIdPort; const AIP: String = ''); overload; + function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort; + AMSec: Integer = IdTimeoutDefault): integer; overload; virtual; + function ReceiveBuffer(var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort; + var VIPVersion: TIdIPVersion; const AMSec: Integer = IdTimeoutDefault): integer; overload; virtual; + function ReceiveBuffer(var ABuffer : TIdBytes; + const AMSec: Integer = IdTimeoutDefault): Integer; overload; virtual; + function ReceiveString(const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort; + const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + procedure Send(const AHost: string; const APort: TIdPort; const AData: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); + procedure SendBuffer(const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); overload; virtual; + procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes); overload; virtual; + // + property ReceiveTimeout: Integer read FReceiveTimeout write FReceiveTimeout default IdTimeoutInfinite; + property ReuseSocket: TIdReuseSocket read FReuseSocket write FReuseSocket default rsOSDependent; + published + property Active: Boolean read GetActive write SetActive Default False; + property BufferSize: Integer read FBufferSize write FBufferSize default ID_UDP_BUFFERSIZE; + property BroadcastEnabled: Boolean read FBroadcastEnabled + write SetBroadcastEnabled default False; + property IPVersion: TIdIPVersion read GetIPVersion write SetIPVersion default ID_DEFAULT_IP_VERSION; + end; + EIdUDPException = Class(EIdException); + EIdUDPReceiveErrorZeroBytes = class(EIdUDPException); + +implementation + +uses + IdStackConsts, IdStack, SysUtils; + +{ TIdUDPBase } + +procedure TIdUDPBase.Broadcast(const AData: string; const APort: TIdPort; + const AIP: String = ''; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}); +begin + Binding.Broadcast(AData, APort, AIP, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); +end; + +procedure TIdUDPBase.Broadcast(const AData: TIdBytes; const APort: TIdPort; + const AIP: String = ''); +begin + Binding.Broadcast(AData, APort, AIP); +end; + +procedure TIdUDPBase.BroadcastEnabledChanged; +begin + if Assigned(FBinding) then begin + FBinding.BroadcastEnabled := BroadcastEnabled; + end; +end; + +procedure TIdUDPBase.CloseBinding; +begin + FreeAndNil(FBinding); +end; + +destructor TIdUDPBase.Destroy; +begin + Active := False; + //double check that binding gets freed. + //sometimes possible that binding is allocated, but active=false + CloseBinding; + inherited Destroy; +end; + +function TIdUDPBase.GetActive: Boolean; +begin + Result := FDsgnActive; + if not Result then begin + if Assigned(FBinding) then begin + Result := FBinding.HandleAllocated; + end; + end; +end; + +function TIdUDPBase.GetHost: String; +begin + Result := FHost; +end; + +function TIdUDPBase.GetIPVersion: TIdIPVersion; +begin + Result := FIPVersion; +end; + +function TIdUDPBase.GetPort: TIdPort; +begin + Result := FPort; +end; + +procedure TIdUDPBase.InitComponent; +begin + inherited InitComponent; + BufferSize := ID_UDP_BUFFERSIZE; + FReceiveTimeout := IdTimeoutInfinite; + FReuseSocket := rsOSDependent; + FIPVersion := ID_DEFAULT_IP_VERSION; +end; + +procedure TIdUDPBase.Loaded; +var + b: Boolean; +begin + inherited Loaded; + b := FDsgnActive; + FDsgnActive := False; + Active := b; +end; + +function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes; + const AMSec: Integer = IdTimeoutDefault): Integer; +var + VoidIP: string; + VoidPort: TIdPort; + VoidIPVer: TIdIPVersion; +begin + Result := ReceiveBuffer(ABuffer, VoidIP, VoidPort, VoidIPVer, AMSec); +end; + +function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; + AMSec: Integer = IdTimeoutDefault): integer; +var + VoidIPVer : TIdIPVersion; +begin + Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVer, AMSec); + // GBSDStack.CheckForSocketError(Result); +end; + +function TIdUDPBase.ReceiveBuffer(var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion; + const AMSec: Integer = IdTimeoutDefault): integer; +var + LMSec : Integer; +begin + if AMSec = IdTimeoutDefault then begin + if ReceiveTimeOut = 0 then begin + LMSec := IdTimeoutInfinite; + end else begin + LMSec := ReceiveTimeOut; + end; + end else begin + LMSec := AMSec; + end; + if not Binding.Readable(LMSec) then begin + Result := 0; + VPeerIP := ''; {Do not Localize} + VPeerPort := 0; + Exit; + end; + Result := Binding.RecvFrom(ABuffer, VPeerIP, VPeerPort, VIPVersion); +end; + +function TIdUDPBase.ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort; + const AMSec: Integer = IdTimeoutDefault; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + i: Integer; + LBuffer : TIdBytes; +begin + SetLength(LBuffer, BufferSize); + i := ReceiveBuffer(LBuffer, VPeerIP, VPeerPort, AMSec); + Result := BytesToString(LBuffer, 0, i, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); +end; + +function TIdUDPBase.ReceiveString(const AMSec: Integer = IdTimeoutDefault; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}): string; +var + VoidIP: string; + VoidPort: TIdPort; +begin + Result := ReceiveString(VoidIP, VoidPort, AMSec, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); +end; + +procedure TIdUDPBase.Send(const AHost: string; const APort: TIdPort; const AData: string; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + SendBuffer(AHost, APort, ToBytes(AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer: TIdBytes); +begin + SendBuffer(AHost, APort, IPVersion, ABuffer); +end; + +procedure TIdUDPBase.SendBuffer(const AHost: string; const APort: TIdPort; + const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes); +var + LIP : String; +begin + LIP := GStack.ResolveHost(AHost, AIPVersion); + Binding.SendTo(LIP, APort, ABuffer,AIPVersion); +end; + +procedure TIdUDPBase.SetActive(const Value: Boolean); +begin + if Active <> Value then begin + if not (IsDesignTime or IsLoading) then begin + if Value then begin + GetBinding; + end + else begin + CloseBinding; + end; + end + else begin // don't activate at designtime (or during loading of properties) {Do not Localize} + FDsgnActive := Value; + end; + end; +end; + +procedure TIdUDPBase.SetBroadcastEnabled(const AValue: Boolean); +begin + if FBroadCastEnabled <> AValue then begin + FBroadcastEnabled := AValue; + if Active then begin + BroadcastEnabledChanged; + end; + end; +end; + +procedure TIdUDPBase.SetHost(const AValue: String); +begin + FHost := Avalue; +end; + +procedure TIdUDPBase.SetIPVersion(const AValue: TIdIPVersion); +begin + FIPVersion := AValue; +end; + +procedure TIdUDPBase.SetPort(const AValue: TIdPort); +begin + FPort := AValue; +end; + +end. diff --git a/indy/Core/IdUDPClient.pas b/indy/Core/IdUDPClient.pas new file mode 100644 index 0000000..5f130f2 --- /dev/null +++ b/indy/Core/IdUDPClient.pas @@ -0,0 +1,551 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.11 11/12/2004 11:30:20 AM JPMugaas + Expansions for IPv6. + + Rev 1.10 11/11/2004 10:25:26 PM JPMugaas + Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions + from the UDP client with SOCKS. You must call OpenProxy before using + RecvFrom or SendTo. When you are finished, you must use CloseProxy to close + any connection to the Proxy. Connect and disconnect also call OpenProxy and + CloseProxy. + + Rev 1.9 11/10/2004 9:40:42 PM JPMugaas + Timeout error fix. Thanks Bas. + + Rev 1.8 11/9/2004 8:18:00 PM JPMugaas + Attempt to add SOCKS support in UDP. + + Rev 1.7 11/8/2004 5:03:00 PM JPMugaas + Eliminated Socket property because we probably do not need it after all. + Binding should work just as well. I also made some minor refinements to + Disconnect and Connect. + + Rev 1.6 11/7/2004 11:50:36 PM JPMugaas + Fixed a Send method I broke. If FSocket is not assigned, it will call the + inherited SendBuffer method. That should prevent code breakage. The connect + method should be OPTIONAL because UDP may be used for simple one-packet + query/response protocols. + + Rev 1.5 11/7/2004 11:33:30 PM JPMugaas + Now uses Connect, Disconnect, Send, and Receive similarly to the TCP Clients. + This should prevent unneeded DNS name to IP address conversions that SendTo + was doing. + + Rev 1.4 2004.02.03 4:17:02 PM czhower + For unit name changes. + + Rev 1.3 2004.01.21 2:35:40 PM czhower + Removed illegal characters from file. + + Rev 1.2 21.1.2004 . 12:31:02 DBondzhev + Fix for Indy source. Workaround for dccil bug + now it can be compiled using Compile instead of build + + Rev 1.1 10/22/2003 04:41:00 PM JPMugaas + Should compile with some restored functionality. Still not finished. + + Rev 1.0 11/13/2002 09:02:16 AM JPMugaas +} + +unit IdUDPClient; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + IdUDPBase, + IdGlobal, + IdSocketHandle, + IdCustomTransparentProxy; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Idudpclient@TIdUDPClient@SetPortA$qqrxus"="@Idudpclient@TIdUDPClient@SetPort$qqrxus"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Idudpclient@TIdUDPClient@SetPortW$qqrxus"="@Idudpclient@TIdUDPClient@SetPort$qqrxus"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +type + EIdMustUseOpenProxy = class(EIdUDPException); + + TIdUDPClient = class(TIdUDPBase) + protected + FBoundIP: string; + FBoundPort: TIdPort; + FBoundPortMin: TIdPort; + FBoundPortMax: TIdPort; + FProxyOpened : Boolean; + FOnConnected : TNotifyEvent; + FOnDisconnected: TNotifyEvent; + FConnected : Boolean; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FTransparentProxy: TIdCustomTransparentProxy; + FImplicitTransparentProxy: Boolean; + function UseProxy : Boolean; + procedure RaiseUseProxyError; + procedure DoOnConnected; virtual; + procedure DoOnDisconnected; virtual; + procedure InitComponent; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + //property methods + procedure SetIPVersion(const AValue: TIdIPVersion); override; + procedure SetHost(const AValue : String); override; + procedure SetPort(const AValue : TIdPort); override; + procedure SetTransparentProxy(AProxy : TIdCustomTransparentProxy); + function GetBinding: TIdSocketHandle; override; + function GetTransparentProxy: TIdCustomTransparentProxy; + public + destructor Destroy; override; + procedure OpenProxy; + procedure CloseProxy; + procedure Connect; virtual; + procedure Disconnect; virtual; + function Connected: Boolean; + function ReceiveBuffer(var ABuffer : TIdBytes; + const AMSec: Integer = IdTimeoutDefault): Integer; overload; override; + function ReceiveBuffer(var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; + AMSec: Integer = IdTimeoutDefault): integer; overload; override; + function ReceiveBuffer(var ABuffer : TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion; + const AMSec: Integer = IdTimeoutDefault): integer; overload; override; + procedure Send(const AData: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); overload; override; + procedure SendBuffer(const ABuffer: TIdBytes); reintroduce; overload; + procedure SendBuffer(const AHost: string; const APort: TIdPort; + const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);overload; override; + published + property BoundIP: string read FBoundIP write FBoundIP; + property BoundPort: TIdPort read FBoundPort write FBoundPort default DEF_PORT_ANY; + property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin default DEF_PORT_ANY; + property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax default DEF_PORT_ANY; + property IPVersion; + property Host; + property Port; + property ReceiveTimeout; + property ReuseSocket; + property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy; + property OnConnected: TNotifyEvent read FOnConnected write FOnConnected; + property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected; + end; + +implementation + +uses + IdComponent, IdResourceStringsCore, IdSocks, IdStack, IdStackConsts, + SysUtils; + +{ TIdUDPClient } + +procedure TIdUDPClient.CloseProxy; +begin + if UseProxy and FProxyOpened then begin + FTransparentProxy.CloseUDP(Binding); + FProxyOpened := False; + end; +end; + +procedure TIdUDPClient.Connect; +var + LIP : String; + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + if Connected then begin + Disconnect; + end; + LTransparentProxy := FTransparentProxy; + if Assigned(LTransparentProxy) then begin + if LTransparentProxy.Enabled then begin + //we don't use proxy open because we want to pass a peer's hostname and port + //in case a proxy type in the future requires this. + LTransparentProxy.OpenUDP(Binding, Host, Port); + FProxyOpened := True; + FConnected := True; + Exit; //we're done, the transparentProxy takes care of the work. + end; + end; + + if not GStack.IsIP(Host) then begin + if Assigned(OnStatus) then begin + DoStatus(hsResolving, [Host]); + end; + LIP := GStack.ResolveHost(Host, FIPVersion); + end else begin + LIP := Host; + end; + Binding.SetPeer(LIP, Port); + Binding.Connect; + + DoStatus(hsConnected, [Host]); + DoOnConnected; + FConnected := True; +end; + +function TIdUDPClient.Connected: Boolean; +begin + Result := FConnected; + if Result then begin + if Assigned(FBinding) then begin + Result := FBinding.HandleAllocated; + end else begin + Result := False; + end; + end; +end; + +procedure TIdUDPClient.Disconnect; +begin + if Connected then begin + DoStatus(hsDisconnecting); + if UseProxy and FProxyOpened then begin + CloseProxy; + end; + FBinding.CloseSocket; + DoOnDisconnected; + DoStatus(hsDisconnected); + FConnected := False; + end; +end; + +procedure TIdUDPClient.DoOnConnected; +begin + if Assigned(OnConnected) then begin + OnConnected(Self); + end; +end; + +procedure TIdUDPClient.DoOnDisconnected; +begin + if Assigned(OnDisconnected) then begin + OnDisconnected(Self); + end; +end; + +function TIdUDPClient.GetBinding: TIdSocketHandle; +begin + if FBinding = nil then begin + FBinding := TIdSocketHandle.Create(nil); + end; + if not FBinding.HandleAllocated then begin + FBinding.IPVersion := FIPVersion; + FBinding.AllocateSocket(Id_SOCK_DGRAM); + FBinding.IP := FBoundIP; + FBinding.Port := FBoundPort; + FBinding.ClientPortMin := FBoundPortMin; + FBinding.ClientPortMax := FBoundPortMax; + FBinding.ReuseSocket := FReuseSocket; + FBinding.Bind; + BroadcastEnabledChanged; + end; + Result := FBinding; +end; + +function TIdUDPClient.GetTransparentProxy: TIdCustomTransparentProxy; +var + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + LTransparentProxy := FTransparentProxy; + // Necessary at design time for Borland SOAP support + if LTransparentProxy = nil then begin + LTransparentProxy := TIdSocksInfo.Create(Self); //default + FTransparentProxy := LTransparentProxy; + FImplicitTransparentProxy := True; + end; + Result := LTransparentProxy; +end; + +procedure TIdUDPClient.InitComponent; +begin + inherited InitComponent; + FProxyOpened := False; + FConnected := False; + FBoundPort := DEF_PORT_ANY; + FBoundPortMin := DEF_PORT_ANY; + FBoundPortMax := DEF_PORT_ANY; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdUDPClient.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdUDPClient.OpenProxy; +begin + if UseProxy and (not FProxyOpened) then begin + FTransparentProxy.OpenUDP(Binding); + FProxyOpened := True; + end; +end; + +function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes; + const AMSec: Integer): Integer; +var + LMSec : Integer; + LHost : String; + LPort : TIdPort; + LIPVersion: TIdIPVersion; +begin + Result := 0; + if AMSec = IdTimeoutDefault then begin + if ReceiveTimeout = 0 then begin + LMSec := IdTimeoutInfinite; + end else begin + LMSec := ReceiveTimeout; + end; + end else begin + LMSec := AMSec; + end; + if UseProxy then begin + if not FProxyOpened then begin + RaiseUseProxyError; + end; + Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, LHost, LPort, LIPVersion, LMSec); + end else + begin + if Connected then begin + if FBinding.Readable(LMSec) then begin //Select(LMSec) then + Result := FBinding.Receive(ABuffer); + end; + end else begin + Result := inherited ReceiveBuffer(ABuffer, LMSec); + end; + end; +end; + +procedure TIdUDPClient.RaiseUseProxyError; +begin + raise EIdMustUseOpenProxy.Create(RSUDPMustUseProxyOpen); +end; + +function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; AMSec: Integer): integer; +var + VoidIPVersion: TidIPVersion; +begin + Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVersion, AMSec); +end; + +procedure TIdUDPClient.Send(const AData: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +begin + Send(Host, Port, AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); +end; + +procedure TIdUDPClient.SendBuffer(const ABuffer : TIdBytes); +begin + if UseProxy then begin + if not FProxyOpened then begin + RaiseUseProxyError; + end; + FTransparentProxy.SendToUDP(Binding, Host, Port, IPVersion, ABuffer); + end else + begin + if Connected then begin + FBinding.Send(ABuffer, 0, -1); + end else begin + inherited SendBuffer(Host, Port, IPVersion, ABuffer); + end; + end; +end; + +procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort; + const ABuffer: TIdBytes); +begin + if UseProxy then begin + if not FProxyOpened then begin + RaiseUseProxyError; + end; + FTransparentProxy.SendToUDP(Binding, AHost, APort, IPVersion, ABuffer); + end else begin + inherited SendBuffer(AHost, APort, ABuffer); + end; +end; + +procedure TIdUDPClient.SetHost(const AValue: String); +begin + if FHost <> AValue then begin + Disconnect; + end; + inherited SetHost(AValue); +end; + +procedure TIdUDPClient.SetIPVersion(const AValue: TIdIPVersion); +begin + if FIPVersion <> AValue then begin + Disconnect; + end; + inherited SetIPVersion(AValue); +end; + +procedure TIdUDPClient.SetPort(const AValue: TIdPort); +begin + if FPort <> AValue then begin + Disconnect; + end; + inherited SetPort(AValue); +end; + +procedure TIdUDPClient.SetTransparentProxy(AProxy: TIdCustomTransparentProxy); +var + LClass: TIdCustomTransparentProxyClass; + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + LTransparentProxy := FTransparentProxy; + + if LTransparentProxy <> AProxy then + begin + // All this is to preserve the compatibility with old version + // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object + // In the case when the ASocks points to an object with owner it is treated as component on form. + + // under ARC, all weak references to a freed object get nil'ed automatically + + if Assigned(AProxy) then begin + if not Assigned(AProxy.Owner) then begin + if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + LTransparentProxy := nil; + end; + LClass := TIdCustomTransparentProxyClass(AProxy.ClassType); + if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end; + if not Assigned(LTransparentProxy) then begin + LTransparentProxy := LClass.Create(Self); + FTransparentProxy := LTransparentProxy; + FImplicitTransparentProxy := True; + end; + LTransparentProxy.Assign(AProxy); + end else begin + if Assigned(LTransparentProxy) then begin + if FImplicitTransparentProxy then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end else begin + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + end; + end; + + FTransparentProxy := AProxy; + + {$IFNDEF USE_OBJECT_ARC} + AProxy.FreeNotification(Self); + {$ENDIF} + end; + end + else if Assigned(LTransparentProxy) then begin + if FImplicitTransparentProxy then begin + FTransparentProxy := nil; + FImplicitTransparentProxy := False; + IdDisposeAndNil(LTransparentProxy); + end else begin + FTransparentProxy := nil; //remove link + {$IFNDEF USE_OBJECT_ARC} + LTransparentProxy.RemoveFreeNotification(Self); + {$ENDIF} + end; + end; + end; +end; + +function TIdUDPClient.UseProxy: Boolean; +var + // under ARC, convert a weak reference to a strong reference before working with it + LTransparentProxy: TIdCustomTransparentProxy; +begin + LTransparentProxy := FTransparentProxy; + Result := Assigned(LTransparentProxy); + if Result then begin + Result := LTransparentProxy.Enabled; + end; +end; + +destructor TIdUDPClient.Destroy; +begin + if UseProxy and FProxyOpened then begin + CloseProxy; + end; + if Connected then begin + Disconnect; + end; + inherited Destroy; +end; + +function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes; + var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion; + const AMSec: Integer): integer; +var + LMSec : Integer; +begin + if AMSec = IdTimeoutDefault then begin + if ReceiveTimeout = 0 then begin + LMSec := IdTimeoutInfinite; + end else begin + LMSec := ReceiveTimeout; + end; + end else begin + LMSec := AMSec; + end; + if UseProxy then begin + if not FProxyOpened then begin + RaiseUseProxyError; + end; + Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec); + end else begin + Result := inherited ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec); + end; +end; + +procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort; + const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes); +begin + if UseProxy then begin + if not FProxyOpened then begin + RaiseUseProxyError; + end; + FTransparentProxy.SendToUDP(Binding, AHost, APort, AIPVersion, ABuffer); + end else begin + inherited SendBuffer(AHost, APort, AIPVersion, ABuffer); + end; +end; + +end. diff --git a/indy/Core/IdUDPServer.pas b/indy/Core/IdUDPServer.pas new file mode 100644 index 0000000..0242048 --- /dev/null +++ b/indy/Core/IdUDPServer.pas @@ -0,0 +1,474 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.14 11/12/2004 3:44:00 PM JPMugaas + Compiler error fix. OOPPS!!! + + Rev 1.13 11/12/2004 11:30:20 AM JPMugaas + Expansions for IPv6. + + Rev 1.12 6/11/2004 11:48:34 PM JPMugaas + Fix for mistake I made. UDPReceive should have been UDPException + + Rev 1.11 6/11/2004 4:05:34 PM JPMugaas + RecvFrom should now work in the UDP server with IPv6. + An OnException event was added for logging purposes. + + Rev 1.10 09/06/2004 00:25:32 CCostelloe + Kylix 3 patch + + Rev 1.9 2004.02.03 4:17:02 PM czhower + For unit name changes. + + Rev 1.8 2004.01.20 10:03:40 PM czhower + InitComponent + + Rev 1.7 2003.12.31 8:03:36 PM czhower + Matched visibility + + Rev 1.6 10/26/2003 6:01:44 PM BGooijen + Fixed binding problem + + Rev 1.5 10/24/2003 5:18:38 PM BGooijen + Removed boolean shortcutting from .GetActive + + Rev 1.4 10/22/2003 04:41:02 PM JPMugaas + Should compile with some restored functionality. Still not finished. + + Rev 1.3 2003.10.11 9:58:50 PM czhower + Started on some todos + + Rev 1.2 2003.10.11 5:52:18 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.1 2003.09.30 1:23:10 PM czhower + Stack split for DotNet + + Rev 1.0 11/13/2002 09:02:30 AM JPMugaas +} + +unit IdUDPServer; + +interface + +{$I IdCompilerDefines.inc} +//Put FPC into Delphi mode + +uses + Classes, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdComponent, + IdException, + IdGlobal, + IdSocketHandle, + IdStackConsts, + IdThread, + IdUDPBase, + IdStack; + +type + TIdUDPServer = class; + + TIdUDPListenerThread = class(TIdThread) + protected + FBinding: TIdSocketHandle; + FAcceptWait: Integer; + FBuffer: TIdBytes; + FCurrentException: String; + FCurrentExceptionClass: TClass; + {$IFDEF USE_OBJECT_ARC} + // When AutoRefCounting is enabled, object references MUST be valid objects. + // It is common for users to store non-object values, though, so we will + // provide separate properties for those purpose + // + // TODO; use TValue instead of separating them + // + FDataObject: TObject; + FDataValue: PtrInt; + {$ELSE} + FData: TObject; + {$ENDIF} + FServer: TIdUDPServer; + // + procedure AfterRun; override; + procedure Run; override; + public + // + //[Error] IdUDPServer.pas(266): E2391 Potentially polymorphic constructor calls must be virtual + constructor Create(AOwner: TIdUDPServer; ABinding: TIdSocketHandle); reintroduce; virtual; + destructor Destroy; override; + // + procedure UDPRead; + procedure UDPException; + // + property AcceptWait: integer read FAcceptWait write FAcceptWait; + property Binding: TIdSocketHandle read FBinding; + property Server: TIdUDPServer read FServer; + {$IFDEF USE_OBJECT_ARC} + property DataObject: TObject read FDataObject write FDataObject; + property DataValue: PtrInt read FDataValue write FDataValue; + {$ELSE} + property Data: TObject read FData write FData; + {$ENDIF} + end; + + // TODO: use TIdThreadSafeObjectList instead? + {$IFDEF HAS_GENERICS_TThreadList} + TIdUDPListenerThreadList = TThreadList; + TIdUDPListenerList = TList; + {$ELSE} + // TODO: flesh out TThreadList and TList for non-Generics compilers... + TIdUDPListenerThreadList = TThreadList; + TIdUDPListenerList = TList; + {$ENDIF} + + TIdUDPListenerThreadClass = class of TIdUDPListenerThread; + + //Exception is used instead of EIdException because the exception could be from somewhere else + TIdUDPExceptionEvent = procedure(AThread: TIdUDPListenerThread; ABinding: TIdSocketHandle; const AMessage : String; const AExceptionClass : TClass) of object; + + TUDPReadEvent = procedure(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle) of object; + + TIdUDPServer = class(TIdUDPBase) + protected + FBindings: TIdSocketHandles; + FCurrentBinding: TIdSocketHandle; + FListenerThreads: TIdUDPListenerThreadList; + FThreadClass: TIdUDPListenerThreadClass; + FThreadedEvent: boolean; + // + FOnBeforeBind: TIdSocketHandleEvent; + FOnAfterBind: TNotifyEvent; + FOnUDPRead: TUDPReadEvent; + FOnUDPException : TIdUDPExceptionEvent; + // + procedure BroadcastEnabledChanged; override; + procedure CloseBinding; override; + procedure DoBeforeBind(AHandle: TIdSocketHandle); virtual; + procedure DoAfterBind; virtual; + procedure DoOnUDPException(AThread: TIdUDPListenerThread; ABinding: TIdSocketHandle; const AMessage : String; const AExceptionClass : TClass); virtual; + procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); virtual; + function GetActive: Boolean; override; + function GetBinding: TIdSocketHandle; override; + function GetDefaultPort: TIdPort; + procedure InitComponent; override; + procedure SetBindings(const Value: TIdSocketHandles); + procedure SetDefaultPort(const AValue: TIdPort); + public + destructor Destroy; override; + property ThreadClass: TIdUDPListenerThreadClass read FThreadClass write FThreadClass; + published + property Bindings: TIdSocketHandles read FBindings write SetBindings; + property DefaultPort: TIdPort read GetDefaultPort write SetDefaultPort; + property ReuseSocket; + property ThreadedEvent: boolean read FThreadedEvent write FThreadedEvent default False; + // + property OnBeforeBind: TIdSocketHandleEvent read FOnBeforeBind write FOnBeforeBind; + property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind; + property OnUDPRead: TUDPReadEvent read FOnUDPRead write FOnUDPRead; + property OnUDPException : TIdUDPExceptionEvent read FOnUDPException write FOnUDPException; + end; + + EIdUDPServerException = class(EIdUDPException); + +implementation + +uses + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + IdGlobalCore, SysUtils; + +procedure TIdUDPServer.BroadcastEnabledChanged; +var + i: Integer; +begin + if Assigned(FCurrentBinding) then begin + for i := 0 to Bindings.Count - 1 do begin + Bindings[i].BroadcastEnabled := BroadcastEnabled; + end; + end; +end; + +procedure TIdUDPServer.CloseBinding; +var + LListenerThreads: TIdUDPListenerList; + LListener: TIdUDPListenerThread; +begin + // RLebeau 2/17/2006: TIdUDPBase.Destroy() calls CloseBinding() + if Assigned(FListenerThreads) then + begin + LListenerThreads := FListenerThreads.LockList; + try + while LListenerThreads.Count > 0 do + begin + LListener := {$IFDEF HAS_GENERICS_TThreadList}LListenerThreads[0]{$ELSE}TIdUDPListenerThread(LListenerThreads[0]){$ENDIF}; + // Stop listening + LListener.Stop; + LListener.Binding.CloseSocket; + // Tear down Listener thread + LListener.WaitFor; + LListener.Free; + LListenerThreads.Delete(0); // RLebeau 2/17/2006 + end; + finally + FListenerThreads.UnlockList; + end; + end; + FCurrentBinding := nil; +end; + +destructor TIdUDPServer.Destroy; +begin + Active := False; + FreeAndNil(FBindings); + FreeAndNil(FListenerThreads); + inherited Destroy; +end; + +procedure TIdUDPServer.DoBeforeBind(AHandle: TIdSocketHandle); +begin + if Assigned(FOnBeforeBind) then begin + FOnBeforeBind(AHandle); + end; +end; + +procedure TIdUDPServer.DoAfterBind; +begin + if Assigned(FOnAfterBind) then begin + FOnAfterBind(Self); + end; +end; + +procedure TIdUDPServer.DoOnUDPException(AThread: TIdUDPListenerThread; ABinding: TIdSocketHandle; const AMessage : String; const AExceptionClass : TClass); +begin + if Assigned(FOnUDPException) then begin + OnUDPException(AThread, ABinding, AMessage, AExceptionClass); + end; +end; + +procedure TIdUDPServer.DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); +begin + if Assigned(OnUDPRead) then begin + OnUDPRead(AThread, AData, ABinding); + end; +end; + +function TIdUDPServer.GetActive: Boolean; +begin + // inherited GetActive keeps track of design-time Active property + Result := inherited GetActive; + if not Result then begin + if Assigned(FCurrentBinding) then begin + Result := FCurrentBinding.HandleAllocated; + end; + end; +end; + +// Linux/Unix does not allow an IPv4 socket and an IPv6 socket +// to listen on the same port at the same time! Windows does not +// have that problem... +{$IFNDEF IdIPv6} + {$DEFINE CanCreateTwoBindings} + {$IFDEF LINUX} // should this be UNIX instead? + {$UNDEF CanCreateTwoBindings} + {$ENDIF} + {$IFDEF ANDROID} + {$UNDEF CanCreateTwoBindings} + {$ENDIF} +{$ENDIF} + +function TIdUDPServer.GetBinding: TIdSocketHandle; +var + LListenerThread: TIdUDPListenerThread; + i: Integer; + LBinding: TIdSocketHandle; +begin + if FCurrentBinding = nil then begin + if Bindings.Count = 0 then begin + Bindings.Add; // IPv4 or IPv6 by default + {$IFNDEF IdIPv6} + {$IFDEF CanCreateTwoBindings} + if GStack.SupportsIPv6 then begin + // maybe add a property too, so the developer can switch it on/off + Bindings.Add.IPVersion := Id_IPv6; + end; + {$ENDIF} + {$ENDIF} + end; + + // Set up listener threads + i := 0; + try + while i < Bindings.Count do begin + LBinding := Bindings[i]; +{$IFDEF LINUX} + LBinding.AllocateSocket(Integer(Id_SOCK_DGRAM)); +{$ELSE} + LBinding.AllocateSocket(Id_SOCK_DGRAM); +{$ENDIF} + // do not overwrite if the default. This allows ReuseSocket to be set per binding + if FReuseSocket <> rsOSDependent then begin + LBinding.ReuseSocket := FReuseSocket; + end; + DoBeforeBind(LBinding); + LBinding.Bind; + Inc(i); + end; + except + Dec(i); // the one that failed doesn't need to be closed + while i >= 0 do begin + Bindings[i].CloseSocket; + Dec(i); + end; + raise; + end; + + DoAfterBind; + + for i := 0 to Bindings.Count - 1 do begin + LListenerThread := FThreadClass.Create(Self, Bindings[i]); + LListenerThread.Name := Name + ' Listener #' + IntToStr(i + 1); {do not localize} + {$IFDEF DELPHI_CROSS} + {$IFNDEF MACOSX} + //Todo: Implement proper priority handling for Linux + //http://www.midnightbeach.com/jon/pubs/2002/BorCon.London/Sidebar.3.html + LListenerThread.Priority := tpListener; + {$ENDIF} + {$ENDIF} + FListenerThreads.Add(LListenerThread); + LListenerThread.Start; + end; + FCurrentBinding := Bindings[0]; + BroadcastEnabledChanged; + end; + Result := FCurrentBinding; +end; + +function TIdUDPServer.GetDefaultPort: TIdPort; +begin + Result := FBindings.DefaultPort; +end; + +procedure TIdUDPServer.InitComponent; +begin + inherited InitComponent; + FBindings := TIdSocketHandles.Create(Self); + FListenerThreads := TIdUDPListenerThreadList.Create; + FThreadClass := TIdUDPListenerThread; +end; + +procedure TIdUDPServer.SetBindings(const Value: TIdSocketHandles); +begin + FBindings.Assign(Value); +end; + +procedure TIdUDPServer.SetDefaultPort(const AValue: TIdPort); +begin + FBindings.DefaultPort := AValue; +end; + +{ TIdUDPListenerThread } + +procedure TIdUDPListenerThread.AfterRun; +begin + inherited AfterRun; + // Close just own binding. The rest will be closed from their + // coresponding threads + FBinding.CloseSocket; +end; + +constructor TIdUDPListenerThread.Create(AOwner: TIdUDPServer; ABinding: TIdSocketHandle); +begin + inherited Create(True); + FAcceptWait := 1000; + FBinding := ABinding; + FServer := AOwner; + SetLength(FBuffer, 0); +end; + +destructor TIdUDPListenerThread.Destroy; +begin + SetLength(FBuffer, 0); + inherited Destroy; +end; + +procedure TIdUDPListenerThread.Run; +var + PeerIP: string; + PeerPort : TIdPort; + PeerIPVersion: TIdIPVersion; + ByteCount: Integer; +begin + if FBinding.Select(AcceptWait) then try + // Doublecheck to see if we've been stopped + // Depending on timing - may not reach here if it is in ancestor run when thread is stopped + if not Stopped then begin + SetLength(FBuffer, FServer.BufferSize); + ByteCount := FBinding.RecvFrom(FBuffer, PeerIP, PeerPort, PeerIPVersion); + // RLebeau: some protocols make use of 0-length messages, so don't discard + // them here. This is not connection-oriented, so recvfrom() only returns + // 0 if a 0-length packet was actually received... + if ByteCount >= 0 then + begin + SetLength(FBuffer, ByteCount); + FBinding.SetPeer(PeerIP, PeerPort, PeerIPVersion); + // TODO: figure out a way to let UDPRead() run in this thread context + // and only synchronize the OnUDPRead event handler so that descendants + // do not need to be synchronized unnecessarily. Probably just have + // TIdUDPServer.DoUDPRead() use TIdSync when ThreadedEvent is false... + if FServer.ThreadedEvent then begin + UDPRead; + end else begin + Synchronize(UDPRead); + end; + end; + end; + except + // exceptions should be ignored so that other clients can be served in case of a DOS attack + on E : Exception do + begin + FCurrentException := E.Message; + FCurrentExceptionClass := E.ClassType; + if FServer.ThreadedEvent then begin + UDPException; + end else begin + Synchronize(UDPException); + end; + end; + end; +end; + +procedure TIdUDPListenerThread.UDPRead; +begin + FServer.DoUDPRead(Self, FBuffer, FBinding); +end; + +procedure TIdUDPListenerThread.UDPException; +begin + FServer.DoOnUDPException(Self, FBinding, FCurrentException, FCurrentExceptionClass); +end; + +end. diff --git a/indy/Core/IdUnitPlatformOff.inc b/indy/Core/IdUnitPlatformOff.inc new file mode 100644 index 0000000..1735aa0 --- /dev/null +++ b/indy/Core/IdUnitPlatformOff.inc @@ -0,0 +1,3 @@ +{$IFDEF HAS_UNIT_PLATFORM} + {$WARN UNIT_PLATFORM OFF} +{$ENDIF} diff --git a/indy/Core/IdUnitPlatformOn.inc b/indy/Core/IdUnitPlatformOn.inc new file mode 100644 index 0000000..ae42534 --- /dev/null +++ b/indy/Core/IdUnitPlatformOn.inc @@ -0,0 +1,8 @@ +{$IFDEF HAS_UNIT_PLATFORM} + {$IFDEF HAS_DIRECTIVE_WARN_DEFAULT} + {$WARN UNIT_PLATFORM DEFAULT} + {$ELSE} + {$WARN UNIT_PLATFORM ON} + {$ENDIF} +{$ENDIF} + diff --git a/indy/Core/IdYarn.pas b/indy/Core/IdYarn.pas new file mode 100644 index 0000000..4047089 --- /dev/null +++ b/indy/Core/IdYarn.pas @@ -0,0 +1,46 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2003.10.21 12:19:04 AM czhower + TIdTask support and fiber bug fixes. + + Rev 1.1 2003.10.11 5:52:24 PM czhower + -VCL fixes for servers + -Chain suport for servers (Super core) + -Scheduler upgrades + -Full yarn support + + Rev 1.0 2003.09.18 4:08:54 PM czhower + Initial checkin +} + +unit IdYarn; + +interface + +{$i IdCompilerDefines.inc} + +type + TIdYarn = class(TObject) + end; + +implementation + +{ TIdYarn } + +end. diff --git a/indy/Core/IddclCore90ASM90.inc b/indy/Core/IddclCore90ASM90.inc new file mode 100644 index 0000000..807ca42 --- /dev/null +++ b/indy/Core/IddclCore90ASM90.inc @@ -0,0 +1,12 @@ +[assembly: AssemblyDescription('Internet Direct (Indy) 10.6.2 Core Design-Time Package for Borland Developer Studio')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew')] +[assembly: AssemblyProduct('Indy for Microsoft .NET Framework')] +[assembly: AssemblyCopyright('Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyTitle('Indy .NET Core Design-Time Package')] +[assembly: AssemblyVersion('10.6.2.*')] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] diff --git a/indy/Core/IndyCar.bmp b/indy/Core/IndyCar.bmp new file mode 100644 index 0000000..db564c0 Binary files /dev/null and b/indy/Core/IndyCar.bmp differ diff --git a/indy/Core/IndyCore.RES b/indy/Core/IndyCore.RES new file mode 100644 index 0000000..f661541 Binary files /dev/null and b/indy/Core/IndyCore.RES differ diff --git a/indy/Core/IndyCore.rc b/indy/Core/IndyCore.rc new file mode 100644 index 0000000..c65ed7f --- /dev/null +++ b/indy/Core/IndyCore.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore.rc.tmpl b/indy/Core/IndyCore.rc.tmpl new file mode 100644 index 0000000..34db8e1 --- /dev/null +++ b/indy/Core/IndyCore.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore100.bdsproj b/indy/Core/IndyCore100.bdsproj new file mode 100644 index 0000000..2738726 --- /dev/null +++ b/indy/Core/IndyCore100.bdsproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + IndyCore100.dpk + + + 7.0 + + + 8 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + False + False + True + True + True + True + True + True + + + 0 + 0 + 1 + False + False + False + 16384 + 1048576 + 4194304 + Indy 10 Core + + + + + + + + vcl;rtl;vclx;dbrtl;vcldb;adortl;dbxcds;dbexpress;xmlrtl;vclie;inet;inetdbbde;inetdbxpress;dclOfficeXP;bdertl;soaprtl;dsnap;websnap;webdsnap;teeui;teedb;tee;vcldbx;dsnapcon;vclactnband + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + 1.0.0.0 + 1.0.0.0 + + + diff --git a/indy/Core/IndyCore100.cfg1 b/indy/Core/IndyCore100.cfg1 new file mode 100644 index 0000000..d325a52 --- /dev/null +++ b/indy/Core/IndyCore100.cfg1 @@ -0,0 +1,45 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore100.cfg2 b/indy/Core/IndyCore100.cfg2 new file mode 100644 index 0000000..f8b03e2 --- /dev/null +++ b/indy/Core/IndyCore100.cfg2 @@ -0,0 +1,46 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-JL +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00600000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore100.dpk b/indy/Core/IndyCore100.dpk new file mode 100644 index 0000000..6c75476 --- /dev/null +++ b/indy/Core/IndyCore100.dpk @@ -0,0 +1,87 @@ +package IndyCore100; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem100; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore100.rc b/indy/Core/IndyCore100.rc new file mode 100644 index 0000000..65c4b07 --- /dev/null +++ b/indy/Core/IndyCore100.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore100\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore100.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore100.rc.tmpl b/indy/Core/IndyCore100.rc.tmpl new file mode 100644 index 0000000..bed8291 --- /dev/null +++ b/indy/Core/IndyCore100.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore100\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore100.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore100.res b/indy/Core/IndyCore100.res new file mode 100644 index 0000000..ae3ff35 Binary files /dev/null and b/indy/Core/IndyCore100.res differ diff --git a/indy/Core/IndyCore100Net.bdsproj b/indy/Core/IndyCore100Net.bdsproj new file mode 100644 index 0000000..2f80dd5 --- /dev/null +++ b/indy/Core/IndyCore100Net.bdsproj @@ -0,0 +1,249 @@ + + + + + + + + + + + + IndyCore100Net.dpk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + Borland.Vcl + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + Indy 10 Core + + + + + + + c:\program files\common files\borland shared\bds\shared assemblies\4.0;w:\source\indy10\lib\core;c:\windows\microsoft.net\framework\v1.1.4322 + c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.dll;IndySystem100Net;Borland.VclRtl;Borland.Delphi + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + diff --git a/indy/Core/IndyCore100Net.dpk b/indy/Core/IndyCore100Net.dpk new file mode 100644 index 0000000..e4449ef --- /dev/null +++ b/indy/Core/IndyCore100Net.dpk @@ -0,0 +1,89 @@ +package IndyCore100Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires +Borland.Delphi, +Borland.VclRtl, + IndySystem100Net; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; +{$I IdCore90ASM90.inc} + +end. diff --git a/indy/Core/IndyCore110.RES b/indy/Core/IndyCore110.RES new file mode 100644 index 0000000..94b3c22 Binary files /dev/null and b/indy/Core/IndyCore110.RES differ diff --git a/indy/Core/IndyCore110.cfg1 b/indy/Core/IndyCore110.cfg1 new file mode 100644 index 0000000..d325a52 --- /dev/null +++ b/indy/Core/IndyCore110.cfg1 @@ -0,0 +1,45 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore110.cfg2 b/indy/Core/IndyCore110.cfg2 new file mode 100644 index 0000000..f8b03e2 --- /dev/null +++ b/indy/Core/IndyCore110.cfg2 @@ -0,0 +1,46 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-JL +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00600000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore110.dpk b/indy/Core/IndyCore110.dpk new file mode 100644 index 0000000..9b0f664 --- /dev/null +++ b/indy/Core/IndyCore110.dpk @@ -0,0 +1,87 @@ +package IndyCore110; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem110; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore110.rc b/indy/Core/IndyCore110.rc new file mode 100644 index 0000000..7b948cc --- /dev/null +++ b/indy/Core/IndyCore110.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore110\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore110.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore110.rc.tmpl b/indy/Core/IndyCore110.rc.tmpl new file mode 100644 index 0000000..303ed97 --- /dev/null +++ b/indy/Core/IndyCore110.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore110\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore110.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore110Net.dpk b/indy/Core/IndyCore110Net.dpk new file mode 100644 index 0000000..722d83b --- /dev/null +++ b/indy/Core/IndyCore110Net.dpk @@ -0,0 +1,89 @@ +package IndyCore110Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires +Borland.Delphi, +Borland.VclRtl, + IndySystem110Net; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; +{$I IdCore90ASM90.inc} + +end. diff --git a/indy/Core/IndyCore120.cfg1 b/indy/Core/IndyCore120.cfg1 new file mode 100644 index 0000000..5dea765 --- /dev/null +++ b/indy/Core/IndyCore120.cfg1 @@ -0,0 +1,47 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore120.cfg2 b/indy/Core/IndyCore120.cfg2 new file mode 100644 index 0000000..0a367e1 --- /dev/null +++ b/indy/Core/IndyCore120.cfg2 @@ -0,0 +1,48 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-JL +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00600000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore120.dpk b/indy/Core/IndyCore120.dpk new file mode 100644 index 0000000..dee2f36 --- /dev/null +++ b/indy/Core/IndyCore120.dpk @@ -0,0 +1,87 @@ +package IndyCore120; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem120; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore120.proj b/indy/Core/IndyCore120.proj new file mode 100644 index 0000000..b061fce --- /dev/null +++ b/indy/Core/IndyCore120.proj @@ -0,0 +1,26 @@ + + + indy + + + + + + + package + delphi + win32 + + + + $(DCCSWTS) $(BCBSWTS) + + + + .res + + + + \ No newline at end of file diff --git a/indy/Core/IndyCore120.rc b/indy/Core/IndyCore120.rc new file mode 100644 index 0000000..827d146 --- /dev/null +++ b/indy/Core/IndyCore120.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore120\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore120.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore120.rc.tmpl b/indy/Core/IndyCore120.rc.tmpl new file mode 100644 index 0000000..f639943 --- /dev/null +++ b/indy/Core/IndyCore120.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore120\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore120.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore120.res b/indy/Core/IndyCore120.res new file mode 100644 index 0000000..67164c0 Binary files /dev/null and b/indy/Core/IndyCore120.res differ diff --git a/indy/Core/IndyCore120Net.dpk b/indy/Core/IndyCore120Net.dpk new file mode 100644 index 0000000..1cdb65d --- /dev/null +++ b/indy/Core/IndyCore120Net.dpk @@ -0,0 +1,89 @@ +package IndyCore120Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires +Borland.Delphi, +Borland.VclRtl, + IndySystem120Net; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; +{$I IdCore90ASM90.inc} + +end. diff --git a/indy/Core/IndyCore130.dpk b/indy/Core/IndyCore130.dpk new file mode 100644 index 0000000..df9f200 --- /dev/null +++ b/indy/Core/IndyCore130.dpk @@ -0,0 +1,87 @@ +package IndyCore130; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem130; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore130.rc b/indy/Core/IndyCore130.rc new file mode 100644 index 0000000..44a4b3f --- /dev/null +++ b/indy/Core/IndyCore130.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore130\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore130.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore130.rc.tmpl b/indy/Core/IndyCore130.rc.tmpl new file mode 100644 index 0000000..108848d --- /dev/null +++ b/indy/Core/IndyCore130.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore130\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore130.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore130.res b/indy/Core/IndyCore130.res new file mode 100644 index 0000000..bbea9ad Binary files /dev/null and b/indy/Core/IndyCore130.res differ diff --git a/indy/Core/IndyCore130Net.dpk b/indy/Core/IndyCore130Net.dpk new file mode 100644 index 0000000..cbfbb71 --- /dev/null +++ b/indy/Core/IndyCore130Net.dpk @@ -0,0 +1,89 @@ +package IndyCore130Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires +Borland.Delphi, +Borland.VclRtl, + IndySystem130Net; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; +{$I IdCore90ASM90.inc} + +end. diff --git a/indy/Core/IndyCore140.RES b/indy/Core/IndyCore140.RES new file mode 100644 index 0000000..6bc6edb Binary files /dev/null and b/indy/Core/IndyCore140.RES differ diff --git a/indy/Core/IndyCore140.cfg1 b/indy/Core/IndyCore140.cfg1 new file mode 100644 index 0000000..5dea765 --- /dev/null +++ b/indy/Core/IndyCore140.cfg1 @@ -0,0 +1,47 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore140.cfg2 b/indy/Core/IndyCore140.cfg2 new file mode 100644 index 0000000..0a367e1 --- /dev/null +++ b/indy/Core/IndyCore140.cfg2 @@ -0,0 +1,48 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-JL +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00600000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore140.dpk b/indy/Core/IndyCore140.dpk new file mode 100644 index 0000000..bb68b18 --- /dev/null +++ b/indy/Core/IndyCore140.dpk @@ -0,0 +1,93 @@ +package IndyCore140; + +{$R *.res} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS ON} +{$RANGECHECKS ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem140; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore140.rc b/indy/Core/IndyCore140.rc new file mode 100644 index 0000000..49a91ae --- /dev/null +++ b/indy/Core/IndyCore140.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore140\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore140.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore140.rc.tmpl b/indy/Core/IndyCore140.rc.tmpl new file mode 100644 index 0000000..b7f9a92 --- /dev/null +++ b/indy/Core/IndyCore140.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore140\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore140.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore150.RES b/indy/Core/IndyCore150.RES new file mode 100644 index 0000000..c8c6511 Binary files /dev/null and b/indy/Core/IndyCore150.RES differ diff --git a/indy/Core/IndyCore150.cfg1 b/indy/Core/IndyCore150.cfg1 new file mode 100644 index 0000000..f47aa2a --- /dev/null +++ b/indy/Core/IndyCore150.cfg1 @@ -0,0 +1,46 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore150.cfg2 b/indy/Core/IndyCore150.cfg2 new file mode 100644 index 0000000..f1b5789 --- /dev/null +++ b/indy/Core/IndyCore150.cfg2 @@ -0,0 +1,47 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T+ +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-JL +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00600000 +--inline:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/IndyCore150.dpk b/indy/Core/IndyCore150.dpk new file mode 100644 index 0000000..9c5b812 --- /dev/null +++ b/indy/Core/IndyCore150.dpk @@ -0,0 +1,88 @@ +package IndyCore150; + +{$R *.res} +{$ALIGN 8} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem150; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore150.rc b/indy/Core/IndyCore150.rc new file mode 100644 index 0000000..d680449 --- /dev/null +++ b/indy/Core/IndyCore150.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore150\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore150.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore150.rc.tmpl b/indy/Core/IndyCore150.rc.tmpl new file mode 100644 index 0000000..87d9cca --- /dev/null +++ b/indy/Core/IndyCore150.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore150\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore150.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore160.dpk b/indy/Core/IndyCore160.dpk new file mode 100644 index 0000000..361858c --- /dev/null +++ b/indy/Core/IndyCore160.dpk @@ -0,0 +1,96 @@ +package IndyCore160; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem160; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore160.dproj b/indy/Core/IndyCore160.dproj new file mode 100644 index 0000000..9491123 --- /dev/null +++ b/indy/Core/IndyCore160.dproj @@ -0,0 +1,239 @@ + + + {CFF99767-2887-4869-A4A2-6490EFAF2888} + IndyCore160.dpk + True + Debug + 7 + Package + None + 13.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + All + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + Indy 10 Core + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + 1033 + false + false + true + false + false + false + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + IndyCore_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName) + + + All + IndyCore_Icon.ico + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + false + false + 0 + RELEASE;$(DCC_Define) + + + DEBUG;$(DCC_Define) + false + true + + + All + true + + + true + + + All + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore160.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + Embarcadero MyBase DataAccess Components + Embarcadero DataSnap Connection Components + + + + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore160.rc b/indy/Core/IndyCore160.rc new file mode 100644 index 0000000..fb6590a --- /dev/null +++ b/indy/Core/IndyCore160.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore160\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore160.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore160.rc.tmpl b/indy/Core/IndyCore160.rc.tmpl new file mode 100644 index 0000000..c9d14db --- /dev/null +++ b/indy/Core/IndyCore160.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore160\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore160.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore160.res b/indy/Core/IndyCore160.res new file mode 100644 index 0000000..7958b4a Binary files /dev/null and b/indy/Core/IndyCore160.res differ diff --git a/indy/Core/IndyCore170.dpk b/indy/Core/IndyCore170.dpk new file mode 100644 index 0000000..4e6223e --- /dev/null +++ b/indy/Core/IndyCore170.dpk @@ -0,0 +1,98 @@ +package IndyCore170; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + {$IFNDEF NEXTGEN} + rtl, + {$ENDIF} + IndySystem170; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore170.dproj b/indy/Core/IndyCore170.dproj new file mode 100644 index 0000000..7abe51c --- /dev/null +++ b/indy/Core/IndyCore170.dproj @@ -0,0 +1,204 @@ + + + {3E9D315E-A66A-4480-95C1-04BD6AADEAAA} + IndyCore170.dpk + True + Debug + 1 + Package + None + 14.3 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + All + ..\DCP;$(DCC_UnitSearchPath) + ..\BPI + ..\DCP + false + true + 1033 + true + false + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + false + 00400000 + System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) + true + Indy 10 Core + false + + + 1033 + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + false + false + 0 + RELEASE;$(DCC_Define) + + + DEBUG;$(DCC_Define) + true + false + + + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore170.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + True + False + + + 12 + + + + diff --git a/indy/Core/IndyCore170.rc b/indy/Core/IndyCore170.rc new file mode 100644 index 0000000..ee7961d --- /dev/null +++ b/indy/Core/IndyCore170.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore170\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore170.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore170.rc.tmpl b/indy/Core/IndyCore170.rc.tmpl new file mode 100644 index 0000000..5efb81a --- /dev/null +++ b/indy/Core/IndyCore170.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore170\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore170.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore170.res b/indy/Core/IndyCore170.res new file mode 100644 index 0000000..7490ff6 Binary files /dev/null and b/indy/Core/IndyCore170.res differ diff --git a/indy/Core/IndyCore180.dpk b/indy/Core/IndyCore180.dpk new file mode 100644 index 0000000..031d77f --- /dev/null +++ b/indy/Core/IndyCore180.dpk @@ -0,0 +1,107 @@ +package IndyCore180; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER250} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem180; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore180.dproj b/indy/Core/IndyCore180.dproj new file mode 100644 index 0000000..653e6b5 --- /dev/null +++ b/indy/Core/IndyCore180.dproj @@ -0,0 +1,256 @@ + + + {D5DC954A-FCFE-40AE-A4E6-DC71D8569E1E} + IndyCore180.dpk + True + Debug + 79 + Package + None + 14.6 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + true + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\DCU\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + ..\Output\DCP\$(Platform)\$(Config);$(DCC_UnitSearchPath) + ..\Output\BPI\$(Platform)\$(Config) + ..\Output\DCP\$(Platform)\$(Config) + All + VER250;$(DCC_Define) + true + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;CFBundleExecutable= + true + Indy 10 Core + true + 1033 + false + false + false + 00400000 + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + false + + + /usr/X11/bin/xterm -e "%debuggee%" + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + RELEASE;$(DCC_Define) + false + false + 0 + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + DEBUG;$(DCC_Define) + true + false + + + Debug + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore180.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore180.rc b/indy/Core/IndyCore180.rc new file mode 100644 index 0000000..d8beddd --- /dev/null +++ b/indy/Core/IndyCore180.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore180\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore180.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore180.rc.tmpl b/indy/Core/IndyCore180.rc.tmpl new file mode 100644 index 0000000..26d9f3e --- /dev/null +++ b/indy/Core/IndyCore180.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore180\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore180.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore180.res b/indy/Core/IndyCore180.res new file mode 100644 index 0000000..0cc72b9 Binary files /dev/null and b/indy/Core/IndyCore180.res differ diff --git a/indy/Core/IndyCore190.dpk b/indy/Core/IndyCore190.dpk new file mode 100644 index 0000000..0789db2 --- /dev/null +++ b/indy/Core/IndyCore190.dpk @@ -0,0 +1,109 @@ +package IndyCore190; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER260} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +// RLebeau: cannot use IdCompilerDefines.inc here! + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem190; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore190.dproj b/indy/Core/IndyCore190.dproj new file mode 100644 index 0000000..c398c3e --- /dev/null +++ b/indy/Core/IndyCore190.dproj @@ -0,0 +1,287 @@ + + + {0A24DBEB-478F-496F-A1BA-2055D58172A7} + IndyCore190.dpk + True + Release + 95 + Package + None + 15.1 + Android + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + ..\Output\BPI\$(Platform)\$(Config) + All + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + Vcl;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + true + true + false + false + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;UIDeviceFamily=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;UISupportedInterfaceOrientations=;CFBundleExecutable=;CFBundleResourceSpecification=;LSRequiresIPhoneOS=;CFBundleInfoDictionaryVersion=;CFBundleDevelopmentRegion=;package=;label=;versionCode=0;versionName=;persistent=;restoreAnyVersion=;installLocation= + false + true + Indy 10 Core + false + false + 1033 + + + iPhoneAndiPad + true + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=6.0;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + true + iPhoneAndiPad + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=6.0;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1033 + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + false + 0 + 0 + RELEASE;$(DCC_Define) + + + true + DEBUG;$(DCC_Define) + false + + + true + + + true + + + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore190.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore190.rc b/indy/Core/IndyCore190.rc new file mode 100644 index 0000000..26caaf2 --- /dev/null +++ b/indy/Core/IndyCore190.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore190\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore190.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore190.rc.tmpl b/indy/Core/IndyCore190.rc.tmpl new file mode 100644 index 0000000..c0ea935 --- /dev/null +++ b/indy/Core/IndyCore190.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore190\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore190.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore190.res b/indy/Core/IndyCore190.res new file mode 100644 index 0000000..aaf87ff Binary files /dev/null and b/indy/Core/IndyCore190.res differ diff --git a/indy/Core/IndyCore200.dpk b/indy/Core/IndyCore200.dpk new file mode 100644 index 0000000..fd66c77 --- /dev/null +++ b/indy/Core/IndyCore200.dpk @@ -0,0 +1,109 @@ +package IndyCore200; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER270} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +// RLebeau: cannot use IdCompilerDefines.inc here! + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem200; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore200.dproj b/indy/Core/IndyCore200.dproj new file mode 100644 index 0000000..2ca0e8c --- /dev/null +++ b/indy/Core/IndyCore200.dproj @@ -0,0 +1,274 @@ + + + {319A784C-7CBE-4347-B226-65EDC0D70097} + IndyCore200.dpk + True + Debug + 95 + Package + None + 15.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + false + true + false + 00400000 + All + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + HAS_PKG_RTL;$(DCC_Define) + Indy 10 Core + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;UIDeviceFamily=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;UISupportedInterfaceOrientations=;CFBundleExecutable=;CFBundleResourceSpecification=;LSRequiresIPhoneOS=;CFBundleInfoDictionaryVersion=;CFBundleDevelopmentRegion=;package=;label=;versionCode=;versionName=;persistent=;restoreAnyVersion=;installLocation=;largeHeap=;theme=;hardwareAccelerated= + false + 1033 + true + false + false + + + iPhoneAndiPad + $(MSBuildProjectName) + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=6.0;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + Debug + + + iPhoneAndiPad + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=6.0;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities + + + ..\Output\HPP\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + ..\Output\OBJ\$(Platform)\$(Config) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + 0 + 0 + false + RELEASE;$(DCC_Define) + + + true + DEBUG;$(DCC_Define) + false + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore200.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + LiveBinding Expression Components FireDac + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore200.rc b/indy/Core/IndyCore200.rc new file mode 100644 index 0000000..a47ac79 --- /dev/null +++ b/indy/Core/IndyCore200.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore200\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore200.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore200.rc.tmpl b/indy/Core/IndyCore200.rc.tmpl new file mode 100644 index 0000000..6233f02 --- /dev/null +++ b/indy/Core/IndyCore200.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore200\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore200.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore200.res b/indy/Core/IndyCore200.res new file mode 100644 index 0000000..7888116 Binary files /dev/null and b/indy/Core/IndyCore200.res differ diff --git a/indy/Core/IndyCore210.dpk b/indy/Core/IndyCore210.dpk new file mode 100644 index 0000000..fa6a3b8 --- /dev/null +++ b/indy/Core/IndyCore210.dpk @@ -0,0 +1,109 @@ +package IndyCore210; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER280} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +// RLebeau: cannot use IdCompilerDefines.inc here! + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem210; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore210.dproj b/indy/Core/IndyCore210.dproj new file mode 100644 index 0000000..14e4519 --- /dev/null +++ b/indy/Core/IndyCore210.dproj @@ -0,0 +1,244 @@ + + + {7993AB8B-7249-4A9E-9296-7C5270849D94} + IndyCore210.dpk + True + Debug + 95 + Package + None + 16.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + false + IndyCore210 + false + true + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + HAS_PKG_RTL;$(DCC_Define) + 1033 + true + Indy 10 Core + true + false + false + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true + Debug + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + iPhoneAndiPad + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities + + + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + All + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + RELEASE;$(DCC_Define) + false + 0 + 0 + + + false + DEBUG;$(DCC_Define) + true + + + true + + + true + + + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore210.dpk + + + + True + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore210.rc b/indy/Core/IndyCore210.rc new file mode 100644 index 0000000..77664b2 --- /dev/null +++ b/indy/Core/IndyCore210.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore210\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore210.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore210.rc.tmpl b/indy/Core/IndyCore210.rc.tmpl new file mode 100644 index 0000000..0004bd2 --- /dev/null +++ b/indy/Core/IndyCore210.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore210\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore210.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore210.res b/indy/Core/IndyCore210.res new file mode 100644 index 0000000..ba85d8d Binary files /dev/null and b/indy/Core/IndyCore210.res differ diff --git a/indy/Core/IndyCore220.dpk b/indy/Core/IndyCore220.dpk new file mode 100644 index 0000000..689f441 --- /dev/null +++ b/indy/Core/IndyCore220.dpk @@ -0,0 +1,109 @@ +package IndyCore220; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER290} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +// RLebeau: cannot use IdCompilerDefines.inc here! + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem220; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore220.dproj b/indy/Core/IndyCore220.dproj new file mode 100644 index 0000000..83bf288 --- /dev/null +++ b/indy/Core/IndyCore220.dproj @@ -0,0 +1,296 @@ + + + {C4663880-DF0F-4466-ACA6-441446C172DF} + IndyCore220.dpk + True + Debug + 1119 + Package + None + 17.2 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + Indy 10 Core + true + false + true + false + true + false + IndyCore220 + 00400000 + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + true + false + + + 1 + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + android-support-v4.dex.jar;apk-expansion.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + iPhoneAndiPad + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user + + + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + All + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + false + 0 + RELEASE;$(DCC_Define) + 0 + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + true + DEBUG;$(DCC_Define) + false + + + $(MSBuildProjectName) + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + iPhoneAndiPad + true + + + true + + + true + + + true + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore220.dpk + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore220.rc b/indy/Core/IndyCore220.rc new file mode 100644 index 0000000..46bf7bb --- /dev/null +++ b/indy/Core/IndyCore220.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore220\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore220.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore220.rc.tmpl b/indy/Core/IndyCore220.rc.tmpl new file mode 100644 index 0000000..d848b63 --- /dev/null +++ b/indy/Core/IndyCore220.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore220\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore220.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore220.res b/indy/Core/IndyCore220.res new file mode 100644 index 0000000..b20c06e Binary files /dev/null and b/indy/Core/IndyCore220.res differ diff --git a/indy/Core/IndyCore230.dpk b/indy/Core/IndyCore230.dpk new file mode 100644 index 0000000..40925c8 --- /dev/null +++ b/indy/Core/IndyCore230.dpk @@ -0,0 +1,109 @@ +package IndyCore230; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER300} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +// RLebeau: cannot use IdCompilerDefines.inc here! + +{$DEFINE HAS_PKG_RTL} +{$IFDEF NEXTGEN} + {$IFDEF IOS} + // there is no RTL package available for iOS + {$UNDEF HAS_PKG_RTL} + {$ENDIF} +{$ENDIF} + +requires + {$IFDEF HAS_PKG_RTL} + rtl, + {$ENDIF} + IndySystem230; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore230.dproj b/indy/Core/IndyCore230.dproj new file mode 100644 index 0000000..827d2e4 --- /dev/null +++ b/indy/Core/IndyCore230.dproj @@ -0,0 +1,296 @@ + + + {FE41297F-B928-4AA9-BF7C-04AEFC904DF1} + IndyCore230.dpk + True + Debug + 1119 + Package + None + 17.2 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + Indy 10 Core + true + false + true + false + true + false + IndyCore230 + 00400000 + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + true + false + + + 1 + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + android-support-v4.dex.jar;apk-expansion.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + iPhoneAndiPad + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user + + + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + All + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + false + 0 + RELEASE;$(DCC_Define) + 0 + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + true + DEBUG;$(DCC_Define) + false + + + $(MSBuildProjectName) + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false + iPhoneAndiPad + true + + + true + + + true + + + true + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + IndyCore230.dpk + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + True + True + True + True + + + 12 + + + + diff --git a/indy/Core/IndyCore230.rc b/indy/Core/IndyCore230.rc new file mode 100644 index 0000000..331b281 --- /dev/null +++ b/indy/Core/IndyCore230.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore230\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore230.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore230.rc.tmpl b/indy/Core/IndyCore230.rc.tmpl new file mode 100644 index 0000000..d980b30 --- /dev/null +++ b/indy/Core/IndyCore230.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore230\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore230.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore230.res b/indy/Core/IndyCore230.res new file mode 100644 index 0000000..c587751 Binary files /dev/null and b/indy/Core/IndyCore230.res differ diff --git a/indy/Core/IndyCore40.RES b/indy/Core/IndyCore40.RES new file mode 100644 index 0000000..faa8428 Binary files /dev/null and b/indy/Core/IndyCore40.RES differ diff --git a/indy/Core/IndyCore40.rc b/indy/Core/IndyCore40.rc new file mode 100644 index 0000000..35370e6 --- /dev/null +++ b/indy/Core/IndyCore40.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore40\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore40.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore40.rc.tmpl b/indy/Core/IndyCore40.rc.tmpl new file mode 100644 index 0000000..17ab419 --- /dev/null +++ b/indy/Core/IndyCore40.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore40\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore40.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore50.RES b/indy/Core/IndyCore50.RES new file mode 100644 index 0000000..2bedf46 Binary files /dev/null and b/indy/Core/IndyCore50.RES differ diff --git a/indy/Core/IndyCore50.cfg1 b/indy/Core/IndyCore50.cfg1 new file mode 100644 index 0000000..2ca3464 --- /dev/null +++ b/indy/Core/IndyCore50.cfg1 @@ -0,0 +1,42 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-JPHNE +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" diff --git a/indy/Core/IndyCore50.cfg2 b/indy/Core/IndyCore50.cfg2 new file mode 100644 index 0000000..9b6c368 --- /dev/null +++ b/indy/Core/IndyCore50.cfg2 @@ -0,0 +1,41 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" \ No newline at end of file diff --git a/indy/Core/IndyCore50.dpk b/indy/Core/IndyCore50.dpk new file mode 100644 index 0000000..ae61fe8 --- /dev/null +++ b/indy/Core/IndyCore50.dpk @@ -0,0 +1,87 @@ +package IndyCore50; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + Vcl50, + IndySystem50; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore50.rc b/indy/Core/IndyCore50.rc new file mode 100644 index 0000000..780a91c --- /dev/null +++ b/indy/Core/IndyCore50.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore50\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore50.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore50.rc.tmpl b/indy/Core/IndyCore50.rc.tmpl new file mode 100644 index 0000000..d001d65 --- /dev/null +++ b/indy/Core/IndyCore50.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore50\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore50.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore60.RES b/indy/Core/IndyCore60.RES new file mode 100644 index 0000000..f5fb873 Binary files /dev/null and b/indy/Core/IndyCore60.RES differ diff --git a/indy/Core/IndyCore60.cfg1 b/indy/Core/IndyCore60.cfg1 new file mode 100644 index 0000000..2ca3464 --- /dev/null +++ b/indy/Core/IndyCore60.cfg1 @@ -0,0 +1,42 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-JPHNE +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" diff --git a/indy/Core/IndyCore60.cfg2 b/indy/Core/IndyCore60.cfg2 new file mode 100644 index 0000000..9b6c368 --- /dev/null +++ b/indy/Core/IndyCore60.cfg2 @@ -0,0 +1,41 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" \ No newline at end of file diff --git a/indy/Core/IndyCore60.dpk b/indy/Core/IndyCore60.dpk new file mode 100644 index 0000000..e1166fa --- /dev/null +++ b/indy/Core/IndyCore60.dpk @@ -0,0 +1,87 @@ +package IndyCore60; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem60; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore60.rc b/indy/Core/IndyCore60.rc new file mode 100644 index 0000000..4701b57 --- /dev/null +++ b/indy/Core/IndyCore60.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore60\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore60.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore60.rc.tmpl b/indy/Core/IndyCore60.rc.tmpl new file mode 100644 index 0000000..2779d6c --- /dev/null +++ b/indy/Core/IndyCore60.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore60\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore60.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore70.RES b/indy/Core/IndyCore70.RES new file mode 100644 index 0000000..f56d3f7 Binary files /dev/null and b/indy/Core/IndyCore70.RES differ diff --git a/indy/Core/IndyCore70.dpk b/indy/Core/IndyCore70.dpk new file mode 100644 index 0000000..9298fde --- /dev/null +++ b/indy/Core/IndyCore70.dpk @@ -0,0 +1,87 @@ +package IndyCore70; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem70; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore70.rc b/indy/Core/IndyCore70.rc new file mode 100644 index 0000000..86bbcd9 --- /dev/null +++ b/indy/Core/IndyCore70.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore70\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore70.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore70.rc.tmpl b/indy/Core/IndyCore70.rc.tmpl new file mode 100644 index 0000000..eb9fae4 --- /dev/null +++ b/indy/Core/IndyCore70.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore70\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore70.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore90.RES b/indy/Core/IndyCore90.RES new file mode 100644 index 0000000..8341427 Binary files /dev/null and b/indy/Core/IndyCore90.RES differ diff --git a/indy/Core/IndyCore90.bdsproj b/indy/Core/IndyCore90.bdsproj new file mode 100644 index 0000000..58e9bfb --- /dev/null +++ b/indy/Core/IndyCore90.bdsproj @@ -0,0 +1,488 @@ + + + + + + + + + + + + IndyCore90.dpk + + + 7.0 + + + 8 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + False + False + True + True + True + + + 3 + 0 + 1 + False + False + False + 16384 + 1048576 + 4194304 + Indy 10 Core + + + + + + + + rtl;vcl;dbrtl;vcldb;vclx;adortl;dbxcds;dbexpress;vclib;ibxpress;IntrawebDB_72_90;Intraweb_72_90;xmlrtl;vclie;inet;inetdbbde;inetdbxpress;dclOffice2k;VclSmp;dsnap;soaprtl;inetdb;bdertl;vcldbx;webdsnap;websnap;vclactnband;vclshlctrls;dsnapcon;teeui;teedb;tee;Rave60VCL;BaseBBoxD9;MIMEBBoxD9;PGPBBoxD9;PKIBBoxD9;PGPMIMEBBoxD9;SFTPBBoxD9;SSHBBoxCliD9;SMIMEBBoxD9;SSHBBoxSrvD9;SSLBBoxSrvD9;elpackD9;PMemo6Pack9;PMemoU6Pack9;MiscUnits90;ImageTypes90;LowLevel90;IndySystem90;IndyProtocols90;tb2k_d9;vclSvComV6D9;DJcl;JvXPCtrlsD9R;JvStdCtrlsD9R;JvAppFrmD9R;JvCoreD9R;JvBandsD9R;JvCryptD9R;JvCtrlsD9R;JvCustomD9R;JvDBD9R;JvDlgsD9R;JvEDID9R;JvGlobusD9R;JvHMID9R;JvInspectorD9R;JvJansD9R;JvManagedThreadsD9R;JvCmpD9R;JvMMD9R;JvNetD9R;JvPageCompsD9R;JvPluginD9R;JvPrintPreviewD9R;JvSystemD9R;JvTimeFrameworkD9R;JvUIBD9R;JvValidatorsD9R;JvWizardD9R;IndyCore90;SFTPBBoxSrvD9;SFTPBBoxCliD9 + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + 1.0.0.0 + 1.0.0.0 + + + LMD ElPack Visual Components (Design-time) + JVCL Multimedia and Image Components + EldoS SSLBlackbox IOHandler for Indy 10 + SvCom Components + + + + diff --git a/indy/Core/IndyCore90.dpk b/indy/Core/IndyCore90.dpk new file mode 100644 index 0000000..a54e50e --- /dev/null +++ b/indy/Core/IndyCore90.dpk @@ -0,0 +1,88 @@ +package IndyCore90; + +{$R *.res} +{$ALIGN 8} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystem90; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore90.rc b/indy/Core/IndyCore90.rc new file mode 100644 index 0000000..725ca14 --- /dev/null +++ b/indy/Core/IndyCore90.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "IndyCore90\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore90.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore90.rc.tmpl b/indy/Core/IndyCore90.rc.tmpl new file mode 100644 index 0000000..b8a9ea9 --- /dev/null +++ b/indy/Core/IndyCore90.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Run-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "IndyCore90\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "IndyCore90.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/IndyCore90Net.bdsproj b/indy/Core/IndyCore90Net.bdsproj new file mode 100644 index 0000000..9fcda26 --- /dev/null +++ b/indy/Core/IndyCore90Net.bdsproj @@ -0,0 +1,243 @@ + + + + + + + + + + + + IndyCore90Net.dpk + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + Indy 10 Core + + + + + + + c:\program files\common files\borland shared\bds\shared assemblies\4.0;w:\source\indy10\lib\core + IndySystem90Net;Borland.VclRtl;Borland.Delphi + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + diff --git a/indy/Core/IndyCore90Net.dpk b/indy/Core/IndyCore90Net.dpk new file mode 100644 index 0000000..25f056f --- /dev/null +++ b/indy/Core/IndyCore90Net.dpk @@ -0,0 +1,89 @@ +package IndyCore90Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires +Borland.Delphi, +Borland.VclRtl, + IndySystem90Net; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdExceptionCore in 'IdExceptionCore.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPAddress in 'IdIPAddress.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdResourceStringsCore in 'IdResourceStringsCore.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; +{$I IdCore90ASM90.inc} + +end. diff --git a/indy/Core/IndyCoreK3.dpk b/indy/Core/IndyCoreK3.dpk new file mode 100644 index 0000000..93b63f4 --- /dev/null +++ b/indy/Core/IndyCoreK3.dpk @@ -0,0 +1,84 @@ +package IndyCoreK3; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + IndySystemK3; + +contains + IdAssignedNumbers in 'IdAssignedNumbers.pas', + IdBuffer in 'IdBuffer.pas', + IdCmdTCPClient in 'IdCmdTCPClient.pas', + IdCmdTCPServer in 'IdCmdTCPServer.pas', + IdCommandHandlers in 'IdCommandHandlers.pas', + IdContext in 'IdContext.pas', + IdCustomTCPServer in 'IdCustomTCPServer.pas', + IdCustomTransparentProxy in 'IdCustomTransparentProxy.pas', + IdGlobalCore in 'IdGlobalCore.pas', + IdIOHandler in 'IdIOHandler.pas', + IdIOHandlerSocket in 'IdIOHandlerSocket.pas', + IdIOHandlerStack in 'IdIOHandlerStack.pas', + IdIOHandlerStream in 'IdIOHandlerStream.pas', + IdIPMCastBase in 'IdIPMCastBase.pas', + IdIPMCastClient in 'IdIPMCastClient.pas', + IdIPMCastServer in 'IdIPMCastServer.pas', + IdIcmpClient in 'IdIcmpClient.pas', + IdIntercept in 'IdIntercept.pas', + IdInterceptSimLog in 'IdInterceptSimLog.pas', + IdInterceptThrottler in 'IdInterceptThrottler.pas', + IdLogBase in 'IdLogBase.pas', + IdLogDebug in 'IdLogDebug.pas', + IdLogEvent in 'IdLogEvent.pas', + IdLogFile in 'IdLogFile.pas', + IdLogStream in 'IdLogStream.pas', + IdRawBase in 'IdRawBase.pas', + IdRawClient in 'IdRawClient.pas', + IdRawFunctions in 'IdRawFunctions.pas', + IdRawHeaders in 'IdRawHeaders.pas', + IdReply in 'IdReply.pas', + IdReplyRFC in 'IdReplyRFC.pas', + IdScheduler in 'IdScheduler.pas', + IdSchedulerOfThread in 'IdSchedulerOfThread.pas', + IdSchedulerOfThreadDefault in 'IdSchedulerOfThreadDefault.pas', + IdSchedulerOfThreadPool in 'IdSchedulerOfThreadPool.pas', + IdServerIOHandler in 'IdServerIOHandler.pas', + IdServerIOHandlerSocket in 'IdServerIOHandlerSocket.pas', + IdServerIOHandlerStack in 'IdServerIOHandlerStack.pas', + IdSimpleServer in 'IdSimpleServer.pas', + IdSocketHandle in 'IdSocketHandle.pas', + IdSocks in 'IdSocks.pas', + IdSync in 'IdSync.pas', + IdTCPClient in 'IdTCPClient.pas', + IdTCPConnection in 'IdTCPConnection.pas', + IdTCPServer in 'IdTCPServer.pas', + IdTCPStream in 'IdTCPStream.pas', + IdTask in 'IdTask.pas', + IdThread in 'IdThread.pas', + IdThreadComponent in 'IdThreadComponent.pas', + IdThreadSafe in 'IdThreadSafe.pas', + IdTraceRoute in 'IdTraceRoute.pas', + IdUDPBase in 'IdUDPBase.pas', + IdUDPClient in 'IdUDPClient.pas', + IdUDPServer in 'IdUDPServer.pas', + IdYarn in 'IdYarn.pas'; + +end. diff --git a/indy/Core/IndyCore_Icon.ico b/indy/Core/IndyCore_Icon.ico new file mode 100644 index 0000000..b1c1298 Binary files /dev/null and b/indy/Core/IndyCore_Icon.ico differ diff --git a/indy/Core/Makefile.fpc b/indy/Core/Makefile.fpc new file mode 100644 index 0000000..5804d80 --- /dev/null +++ b/indy/Core/Makefile.fpc @@ -0,0 +1,89 @@ +# Makefile.fpc for indycorefpc 10.6.2.0 + +[package] +name=indycorefpc +version=10.6.2.0 +main=indy + + +[compiler] +includedir=../Inc +unittargetdir=lib/$(CPU_TARGET)-$(OS_TARGET) +unitdir=../System/lib/$(CPU_TARGET)-$(OS_TARGET) +options=-gl + +[target] +units=indycorefpc +rsts=IdResourceStringsCore.rst + +implicitunits=IdAssignedNumbers \ + IdBuffer \ + IdCmdTCPClient \ + IdCmdTCPServer \ + IdCommandHandlers \ + IdContext \ + IdCustomTCPServer \ + IdCustomTransparentProxy \ + IdExceptionCore \ + IdGlobalCore \ + IdIOHandler \ + IdIOHandlerSocket \ + IdIOHandlerStack \ + IdIOHandlerStream \ + IdIPAddress \ + IdIPMCastBase \ + IdIPMCastClient \ + IdIPMCastServer \ + IdIcmpClient \ + IdIntercept \ + IdInterceptSimLog \ + IdInterceptThrottler \ + IdLogBase \ + IdLogDebug \ + IdLogEvent \ + IdLogFile \ + IdLogStream \ + IdRawBase \ + IdRawClient \ + IdRawFunctions \ + IdRawHeaders \ + IdReply \ + IdReplyRFC \ + IdResourceStringsCore \ + IdScheduler \ + IdSchedulerOfThread \ + IdSchedulerOfThreadDefault \ + IdSchedulerOfThreadPool \ + IdServerIOHandler \ + IdServerIOHandlerSocket \ + IdServerIOHandlerStack \ + IdSimpleServer \ + IdSocketHandle \ + IdSocks \ + IdSync \ + IdTCPClient \ + IdTCPConnection \ + IdTCPServer \ + IdTCPStream \ + IdTask \ + IdThread \ + IdThreadComponent \ + IdThreadSafe \ + IdTraceRoute \ + IdUDPBase \ + IdUDPClient \ + IdUDPServer \ + IdYarn + + +[requires] +packages=indysystemfpc +packagedir=../System/lib/$(CPU_TARGET)-$(OS_TARGET) + +[install] +fpcpackage=y +fpcsubdir=packages/extra +buildunit=indycorefpc + +[shared] +build=n diff --git a/indy/Core/Makefile.fpc.tmpl b/indy/Core/Makefile.fpc.tmpl new file mode 100644 index 0000000..803e2d8 --- /dev/null +++ b/indy/Core/Makefile.fpc.tmpl @@ -0,0 +1,89 @@ +# Makefile.fpc for indycorefpc 10.6.2.$WCREV$ + +[package] +name=indycorefpc +version=10.6.2.$WCREV$ +main=indy + + +[compiler] +includedir=../Inc +unittargetdir=lib/$(CPU_TARGET)-$(OS_TARGET) +unitdir=../System/lib/$(CPU_TARGET)-$(OS_TARGET) +options=-gl + +[target] +units=indycorefpc +rsts=IdResourceStringsCore.rst + +implicitunits=IdAssignedNumbers \ + IdBuffer \ + IdCmdTCPClient \ + IdCmdTCPServer \ + IdCommandHandlers \ + IdContext \ + IdCustomTCPServer \ + IdCustomTransparentProxy \ + IdExceptionCore \ + IdGlobalCore \ + IdIOHandler \ + IdIOHandlerSocket \ + IdIOHandlerStack \ + IdIOHandlerStream \ + IdIPAddress \ + IdIPMCastBase \ + IdIPMCastClient \ + IdIPMCastServer \ + IdIcmpClient \ + IdIntercept \ + IdInterceptSimLog \ + IdInterceptThrottler \ + IdLogBase \ + IdLogDebug \ + IdLogEvent \ + IdLogFile \ + IdLogStream \ + IdRawBase \ + IdRawClient \ + IdRawFunctions \ + IdRawHeaders \ + IdReply \ + IdReplyRFC \ + IdResourceStringsCore \ + IdScheduler \ + IdSchedulerOfThread \ + IdSchedulerOfThreadDefault \ + IdSchedulerOfThreadPool \ + IdServerIOHandler \ + IdServerIOHandlerSocket \ + IdServerIOHandlerStack \ + IdSimpleServer \ + IdSocketHandle \ + IdSocks \ + IdSync \ + IdTCPClient \ + IdTCPConnection \ + IdTCPServer \ + IdTCPStream \ + IdTask \ + IdThread \ + IdThreadComponent \ + IdThreadSafe \ + IdTraceRoute \ + IdUDPBase \ + IdUDPClient \ + IdUDPServer \ + IdYarn + + +[requires] +packages=indysystemfpc +packagedir=../System/lib/$(CPU_TARGET)-$(OS_TARGET) + +[install] +fpcpackage=y +fpcsubdir=packages/extra +buildunit=indycorefpc + +[shared] +build=n diff --git a/indy/Core/Res/IdCreditsBitmap.RES b/indy/Core/Res/IdCreditsBitmap.RES new file mode 100644 index 0000000..40a5a3a Binary files /dev/null and b/indy/Core/Res/IdCreditsBitmap.RES differ diff --git a/indy/Core/Res/IdCreditsBitmap.rc b/indy/Core/Res/IdCreditsBitmap.rc new file mode 100644 index 0000000..e1c3400 --- /dev/null +++ b/indy/Core/Res/IdCreditsBitmap.rc @@ -0,0 +1,3 @@ +TIDABOUTPICTURE BITMAP "TIDABOUTPICTURE.BMP" +TIDKITCHENSINK BITMAP "SINK.BMP" +TIDDRAIN WAVE "sinkdr.wav" \ No newline at end of file diff --git a/indy/Core/Res/Maint/1sink.jpg b/indy/Core/Res/Maint/1sink.jpg new file mode 100644 index 0000000..cfe2b5f Binary files /dev/null and b/indy/Core/Res/Maint/1sink.jpg differ diff --git a/indy/Core/Res/Maint/1sinkCropped.jpg b/indy/Core/Res/Maint/1sinkCropped.jpg new file mode 100644 index 0000000..4a28337 Binary files /dev/null and b/indy/Core/Res/Maint/1sinkCropped.jpg differ diff --git a/indy/Core/Res/Maint/1sinkPlainCropped.jpg b/indy/Core/Res/Maint/1sinkPlainCropped.jpg new file mode 100644 index 0000000..0b38213 Binary files /dev/null and b/indy/Core/Res/Maint/1sinkPlainCropped.jpg differ diff --git a/indy/Core/Res/Maint/1sinkPlainCroppedRed.bmp b/indy/Core/Res/Maint/1sinkPlainCroppedRed.bmp new file mode 100644 index 0000000..0a7cee7 Binary files /dev/null and b/indy/Core/Res/Maint/1sinkPlainCroppedRed.bmp differ diff --git a/indy/Core/Res/Maint/1sinkPlainCroppedRed.jpg b/indy/Core/Res/Maint/1sinkPlainCroppedRed.jpg new file mode 100644 index 0000000..d2044e6 Binary files /dev/null and b/indy/Core/Res/Maint/1sinkPlainCroppedRed.jpg differ diff --git a/indy/Core/Res/Maint/sinkdr.wav b/indy/Core/Res/Maint/sinkdr.wav new file mode 100644 index 0000000..c652252 Binary files /dev/null and b/indy/Core/Res/Maint/sinkdr.wav differ diff --git a/indy/Core/Res/Sink.bmp b/indy/Core/Res/Sink.bmp new file mode 100644 index 0000000..63dbb23 Binary files /dev/null and b/indy/Core/Res/Sink.bmp differ diff --git a/indy/Core/Res/TIDABOUTPICTURE.BMP b/indy/Core/Res/TIDABOUTPICTURE.BMP new file mode 100644 index 0000000..99a22f5 Binary files /dev/null and b/indy/Core/Res/TIDABOUTPICTURE.BMP differ diff --git a/indy/Core/Res/makeres.bat b/indy/Core/Res/makeres.bat new file mode 100644 index 0000000..6a9a33f --- /dev/null +++ b/indy/Core/Res/makeres.bat @@ -0,0 +1,2 @@ +brcc32 IdCreditsBitmap.rc +copy *.res .. \ No newline at end of file diff --git a/indy/Core/Res/sinkdr.wav b/indy/Core/Res/sinkdr.wav new file mode 100644 index 0000000..c652252 Binary files /dev/null and b/indy/Core/Res/sinkdr.wav differ diff --git a/indy/Core/dclIndyCore.RES b/indy/Core/dclIndyCore.RES new file mode 100644 index 0000000..d5d2f7d Binary files /dev/null and b/indy/Core/dclIndyCore.RES differ diff --git a/indy/Core/dclIndyCore.rc b/indy/Core/dclIndyCore.rc new file mode 100644 index 0000000..2ae00ce --- /dev/null +++ b/indy/Core/dclIndyCore.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore.rc.tmpl b/indy/Core/dclIndyCore.rc.tmpl new file mode 100644 index 0000000..42e3bf5 --- /dev/null +++ b/indy/Core/dclIndyCore.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore100.bdsproj b/indy/Core/dclIndyCore100.bdsproj new file mode 100644 index 0000000..2b78178 --- /dev/null +++ b/indy/Core/dclIndyCore100.bdsproj @@ -0,0 +1,167 @@ + + + + + + + + + + + + dclIndyCore100.dpk + + + 7.0 + + + 8 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + False + False + True + True + True + True + True + True + + + 0 + 0 + 1 + False + False + False + 16384 + 1048576 + 4194304 + Indy 10 Core Design Time + + + + + + + + vcl;rtl;vclx;dbrtl;vcldb;adortl;dbxcds;dbexpress;xmlrtl;vclie;inet;inetdbbde;inetdbxpress;dclOfficeXP;bdertl;soaprtl;dsnap;websnap;webdsnap;teeui;teedb;tee;vcldbx;dsnapcon;vclactnband + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + 1.0.0.0 + 1.0.0.0 + + + diff --git a/indy/Core/dclIndyCore100.cfg1 b/indy/Core/dclIndyCore100.cfg1 new file mode 100644 index 0000000..51cc9ef --- /dev/null +++ b/indy/Core/dclIndyCore100.cfg1 @@ -0,0 +1,45 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/dclIndyCore100.dpk b/indy/Core/dclIndyCore100.dpk new file mode 100644 index 0000000..2a41312 --- /dev/null +++ b/indy/Core/dclIndyCore100.dpk @@ -0,0 +1,36 @@ +package dclIndyCore100; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem100, + IndyCore100; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore100.rc b/indy/Core/dclIndyCore100.rc new file mode 100644 index 0000000..29b0b21 --- /dev/null +++ b/indy/Core/dclIndyCore100.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore100\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore100.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore100.rc.tmpl b/indy/Core/dclIndyCore100.rc.tmpl new file mode 100644 index 0000000..cdcb996 --- /dev/null +++ b/indy/Core/dclIndyCore100.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore100\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore100.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore100.res b/indy/Core/dclIndyCore100.res new file mode 100644 index 0000000..5b47105 Binary files /dev/null and b/indy/Core/dclIndyCore100.res differ diff --git a/indy/Core/dclIndyCore100Net.bdsproj b/indy/Core/dclIndyCore100Net.bdsproj new file mode 100644 index 0000000..c036c18 --- /dev/null +++ b/indy/Core/dclIndyCore100Net.bdsproj @@ -0,0 +1,279 @@ + + + + + + + + + + + + dclIndyCore100Net.dpk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + Borland.Vcl + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + Indy 10 Core Design Time + + + + + + + c:\windows\microsoft.net\framework\v1.1.4322;W:\Source\Indy10\Lib\Core;w:\source\indy10\lib\system + c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.dll;W:\Source\Indy10\Lib\Core\IndyCore100Net.dll;IndySystem100Net;W:\Source\Indy10\Lib\Core\Borland.Studio.Vcl.Design.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + diff --git a/indy/Core/dclIndyCore100Net.dpk b/indy/Core/dclIndyCore100Net.dpk new file mode 100644 index 0000000..651d72d --- /dev/null +++ b/indy/Core/dclIndyCore100Net.dpk @@ -0,0 +1,38 @@ +package dclIndyCore100Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + System.Windows.Forms, + Borland.Studio.Vcl.Design, + IndySystem100Net, + IndyCore100Net; + +contains + IdAboutDotNET in 'IdAboutDotNET.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas', + IdRegisterCore in 'IdRegisterCore.pas'; +{$I IddclCore90ASM90.inc} + +end. diff --git a/indy/Core/dclIndyCore110.RES b/indy/Core/dclIndyCore110.RES new file mode 100644 index 0000000..07eeb61 Binary files /dev/null and b/indy/Core/dclIndyCore110.RES differ diff --git a/indy/Core/dclIndyCore110.cfg1 b/indy/Core/dclIndyCore110.cfg1 new file mode 100644 index 0000000..51cc9ef --- /dev/null +++ b/indy/Core/dclIndyCore110.cfg1 @@ -0,0 +1,45 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/dclIndyCore110.dpk b/indy/Core/dclIndyCore110.dpk new file mode 100644 index 0000000..4b572b5 --- /dev/null +++ b/indy/Core/dclIndyCore110.dpk @@ -0,0 +1,36 @@ +package dclIndyCore110; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem110, + IndyCore110; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore110.rc b/indy/Core/dclIndyCore110.rc new file mode 100644 index 0000000..89c39d8 --- /dev/null +++ b/indy/Core/dclIndyCore110.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore110\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore110.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore110.rc.tmpl b/indy/Core/dclIndyCore110.rc.tmpl new file mode 100644 index 0000000..0bc5949 --- /dev/null +++ b/indy/Core/dclIndyCore110.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore110\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore110.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore110Net.dpk b/indy/Core/dclIndyCore110Net.dpk new file mode 100644 index 0000000..a7ade85 --- /dev/null +++ b/indy/Core/dclIndyCore110Net.dpk @@ -0,0 +1,38 @@ +package dclIndyCore110Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + System.Windows.Forms, + Borland.Studio.Vcl.Design, + IndySystem110Net, + IndyCore110Net; + +contains + IdAboutDotNET in 'IdAboutDotNET.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas', + IdRegisterCore in 'IdRegisterCore.pas'; +{$I IddclCore90ASM90.inc} + +end. diff --git a/indy/Core/dclIndyCore120.RES b/indy/Core/dclIndyCore120.RES new file mode 100644 index 0000000..57390eb Binary files /dev/null and b/indy/Core/dclIndyCore120.RES differ diff --git a/indy/Core/dclIndyCore120.cfg1 b/indy/Core/dclIndyCore120.cfg1 new file mode 100644 index 0000000..6fd62a6 --- /dev/null +++ b/indy/Core/dclIndyCore120.cfg1 @@ -0,0 +1,47 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/dclIndyCore120.dpk b/indy/Core/dclIndyCore120.dpk new file mode 100644 index 0000000..61dcef0 --- /dev/null +++ b/indy/Core/dclIndyCore120.dpk @@ -0,0 +1,36 @@ +package dclIndyCore120; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem120, + IndyCore120; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore120.proj b/indy/Core/dclIndyCore120.proj new file mode 100644 index 0000000..ce37390 --- /dev/null +++ b/indy/Core/dclIndyCore120.proj @@ -0,0 +1,26 @@ + + + indy + + + + + + + package + delphi + win32 + + + + $(DCCSWTS) $(BCBSWTS) + + + + .res + + + + \ No newline at end of file diff --git a/indy/Core/dclIndyCore120.rc b/indy/Core/dclIndyCore120.rc new file mode 100644 index 0000000..ed921dd --- /dev/null +++ b/indy/Core/dclIndyCore120.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore120\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore120.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore120.rc.tmpl b/indy/Core/dclIndyCore120.rc.tmpl new file mode 100644 index 0000000..6e1ae0b --- /dev/null +++ b/indy/Core/dclIndyCore120.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore120\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore120.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore120Net.dpk b/indy/Core/dclIndyCore120Net.dpk new file mode 100644 index 0000000..ae1bfe7 --- /dev/null +++ b/indy/Core/dclIndyCore120Net.dpk @@ -0,0 +1,38 @@ +package dclIndyCore120Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + System.Windows.Forms, + Borland.Studio.Vcl.Design, + IndySystem120Net, + IndyCore120Net; + +contains + IdAboutDotNET in 'IdAboutDotNET.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas', + IdRegisterCore in 'IdRegisterCore.pas'; +{$I IddclCore90ASM90.inc} + +end. diff --git a/indy/Core/dclIndyCore130.dpk b/indy/Core/dclIndyCore130.dpk new file mode 100644 index 0000000..b49c35a --- /dev/null +++ b/indy/Core/dclIndyCore130.dpk @@ -0,0 +1,36 @@ +package dclIndyCore130; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem130, + IndyCore130; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore130.rc b/indy/Core/dclIndyCore130.rc new file mode 100644 index 0000000..a7021e9 --- /dev/null +++ b/indy/Core/dclIndyCore130.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore130\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore130.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore130.rc.tmpl b/indy/Core/dclIndyCore130.rc.tmpl new file mode 100644 index 0000000..fdd7df4 --- /dev/null +++ b/indy/Core/dclIndyCore130.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore130\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore130.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore130.res b/indy/Core/dclIndyCore130.res new file mode 100644 index 0000000..0fa0359 Binary files /dev/null and b/indy/Core/dclIndyCore130.res differ diff --git a/indy/Core/dclIndyCore130Net.dpk b/indy/Core/dclIndyCore130Net.dpk new file mode 100644 index 0000000..9ff57eb --- /dev/null +++ b/indy/Core/dclIndyCore130Net.dpk @@ -0,0 +1,38 @@ +package dclIndyCore130Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + System.Windows.Forms, + Borland.Studio.Vcl.Design, + IndySystem130Net, + IndyCore130Net; + +contains + IdAboutDotNET in 'IdAboutDotNET.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas', + IdRegisterCore in 'IdRegisterCore.pas'; +{$I IddclCore90ASM90.inc} + +end. diff --git a/indy/Core/dclIndyCore140.RES b/indy/Core/dclIndyCore140.RES new file mode 100644 index 0000000..c557757 Binary files /dev/null and b/indy/Core/dclIndyCore140.RES differ diff --git a/indy/Core/dclIndyCore140.cfg1 b/indy/Core/dclIndyCore140.cfg1 new file mode 100644 index 0000000..6fd62a6 --- /dev/null +++ b/indy/Core/dclIndyCore140.cfg1 @@ -0,0 +1,47 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +--string-checks:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/dclIndyCore140.dpk b/indy/Core/dclIndyCore140.dpk new file mode 100644 index 0000000..6fd68bb --- /dev/null +++ b/indy/Core/dclIndyCore140.dpk @@ -0,0 +1,42 @@ +package dclIndyCore140; + +{$R *.res} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS ON} +{$RANGECHECKS ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem140, + IndyCore140; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore140.rc b/indy/Core/dclIndyCore140.rc new file mode 100644 index 0000000..ab0e2f8 --- /dev/null +++ b/indy/Core/dclIndyCore140.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore140\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore140.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore140.rc.tmpl b/indy/Core/dclIndyCore140.rc.tmpl new file mode 100644 index 0000000..9763025 --- /dev/null +++ b/indy/Core/dclIndyCore140.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore140\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore140.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore150.RES b/indy/Core/dclIndyCore150.RES new file mode 100644 index 0000000..5001cc5 Binary files /dev/null and b/indy/Core/dclIndyCore150.RES differ diff --git a/indy/Core/dclIndyCore150.cfg1 b/indy/Core/dclIndyCore150.cfg1 new file mode 100644 index 0000000..e1560b5 --- /dev/null +++ b/indy/Core/dclIndyCore150.cfg1 @@ -0,0 +1,46 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J+ +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +--inline:on +-N0".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" +-DBCB +-Z +-w-UNSAFE_TYPE +-w-UNSAFE_CODE +-w-UNSAFE_CAST diff --git a/indy/Core/dclIndyCore150.dpk b/indy/Core/dclIndyCore150.dpk new file mode 100644 index 0000000..167c52c --- /dev/null +++ b/indy/Core/dclIndyCore150.dpk @@ -0,0 +1,37 @@ +package dclIndyCore150; + +{$R *.res} +{$ALIGN 8} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem150, + IndyCore150; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore150.rc b/indy/Core/dclIndyCore150.rc new file mode 100644 index 0000000..433f9c3 --- /dev/null +++ b/indy/Core/dclIndyCore150.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore150\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore150.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore150.rc.tmpl b/indy/Core/dclIndyCore150.rc.tmpl new file mode 100644 index 0000000..4825af9 --- /dev/null +++ b/indy/Core/dclIndyCore150.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore150\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore150.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore160.dpk b/indy/Core/dclIndyCore160.dpk new file mode 100644 index 0000000..1ab7390 --- /dev/null +++ b/indy/Core/dclIndyCore160.dpk @@ -0,0 +1,45 @@ +package dclIndyCore160; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem160, + IndyCore160; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore160.dproj b/indy/Core/dclIndyCore160.dproj new file mode 100644 index 0000000..92ce779 --- /dev/null +++ b/indy/Core/dclIndyCore160.dproj @@ -0,0 +1,164 @@ + + + {0EBD02B6-2201-4571-8FCB-6650CE38093D} + dclIndyCore160.dpk + True + Debug + 1 + Package + None + 13.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + Indy 10 Core Design Time + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + 00400000 + 1033 + false + false + true + false + false + false + + + dclIndyCore_Icon.ico + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + dclIndyCore_Icon.ico + true + + + false + false + 0 + RELEASE;$(DCC_Define) + + + true + + + DEBUG;$(DCC_Define) + false + true + + + true + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore160.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + Embarcadero Database Components + Embarcadero MyBase DataAccess Components + Embarcadero DataSnap Connection Components + + + + False + False + True + + + 12 + + + + diff --git a/indy/Core/dclIndyCore160.rc b/indy/Core/dclIndyCore160.rc new file mode 100644 index 0000000..f04928a --- /dev/null +++ b/indy/Core/dclIndyCore160.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore160\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore160.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore160.rc.tmpl b/indy/Core/dclIndyCore160.rc.tmpl new file mode 100644 index 0000000..045f931 --- /dev/null +++ b/indy/Core/dclIndyCore160.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore160\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore160.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore160.res b/indy/Core/dclIndyCore160.res new file mode 100644 index 0000000..8e52553 Binary files /dev/null and b/indy/Core/dclIndyCore160.res differ diff --git a/indy/Core/dclIndyCore170.dpk b/indy/Core/dclIndyCore170.dpk new file mode 100644 index 0000000..c1b44bd --- /dev/null +++ b/indy/Core/dclIndyCore170.dpk @@ -0,0 +1,45 @@ +package dclIndyCore170; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem170, + IndyCore170; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore170.dproj b/indy/Core/dclIndyCore170.dproj new file mode 100644 index 0000000..5b6acc3 --- /dev/null +++ b/indy/Core/dclIndyCore170.dproj @@ -0,0 +1,158 @@ + + + {C34540B4-9092-46A8-B0FB-F65D2EAA2B84} + dclIndyCore170.dpk + True + Debug + 1 + Package + None + 14.3 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + true + true + true + true + true + true + ..\BPI + ..\DCP + ..\DCP;$(DCC_UnitSearchPath) + false + true + 1033 + false + false + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + false + 00400000 + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + true + Indy 10 Core Design Time + false + + + 1033 + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + false + false + 0 + RELEASE;$(DCC_Define) + + + DEBUG;$(DCC_Define) + true + false + + + true + true + true + true + true + true + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore170.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore170.rc b/indy/Core/dclIndyCore170.rc new file mode 100644 index 0000000..4a3f271 --- /dev/null +++ b/indy/Core/dclIndyCore170.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore170\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore170.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore170.rc.tmpl b/indy/Core/dclIndyCore170.rc.tmpl new file mode 100644 index 0000000..fdb2f64 --- /dev/null +++ b/indy/Core/dclIndyCore170.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore170\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore170.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore170.res b/indy/Core/dclIndyCore170.res new file mode 100644 index 0000000..9ff58d5 Binary files /dev/null and b/indy/Core/dclIndyCore170.res differ diff --git a/indy/Core/dclIndyCore180.dpk b/indy/Core/dclIndyCore180.dpk new file mode 100644 index 0000000..73a458d --- /dev/null +++ b/indy/Core/dclIndyCore180.dpk @@ -0,0 +1,46 @@ +package dclIndyCore180; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER250} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem180, + IndyCore180; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore180.dproj b/indy/Core/dclIndyCore180.dproj new file mode 100644 index 0000000..38c4154 --- /dev/null +++ b/indy/Core/dclIndyCore180.dproj @@ -0,0 +1,168 @@ + + + {1D1AC6F4-9803-48F6-8404-5D690607BD16} + dclIndyCore180.dpk + True + Debug + 1 + Package + None + 14.6 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + ..\Output\DCP\$(Platform)\$(Config);$(DCC_UnitSearchPath) + ..\Output\DCU\$(Platform)\$(Config) + ..\Output\BPI\$(Platform)\$(Config) + ..\Output\DCP\$(Platform)\$(Config) + 1033 + false + true + VER250;$(DCC_Define) + true + false + true + false + false + false + Indy 10 Core Design Time + 00400000 + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;CFBundleExecutable= + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + 0 + false + false + RELEASE;$(DCC_Define) + + + true + DEBUG;$(DCC_Define) + false + + + ..\Output\BPI\$(Platform)\$(Config) + ..\Output\DCP\$(Platform)\$(Config) + ..\Output\DCU\$(Platform)\$(Config) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore180.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore180.rc b/indy/Core/dclIndyCore180.rc new file mode 100644 index 0000000..464ec9f --- /dev/null +++ b/indy/Core/dclIndyCore180.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore180\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore180.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore180.rc.tmpl b/indy/Core/dclIndyCore180.rc.tmpl new file mode 100644 index 0000000..7a08cf5 --- /dev/null +++ b/indy/Core/dclIndyCore180.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore180\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore180.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore180.res b/indy/Core/dclIndyCore180.res new file mode 100644 index 0000000..cc6c37b Binary files /dev/null and b/indy/Core/dclIndyCore180.res differ diff --git a/indy/Core/dclIndyCore190.dpk b/indy/Core/dclIndyCore190.dpk new file mode 100644 index 0000000..b770b8b --- /dev/null +++ b/indy/Core/dclIndyCore190.dpk @@ -0,0 +1,46 @@ +package dclIndyCore190; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER260} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem190, + IndyCore190; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore190.dproj b/indy/Core/dclIndyCore190.dproj new file mode 100644 index 0000000..c3600f0 --- /dev/null +++ b/indy/Core/dclIndyCore190.dproj @@ -0,0 +1,173 @@ + + + {C55F1EA3-11DF-4C04-A0DA-DFC8B6279CFE} + dclIndyCore190.dpk + True + Release + 17 + Package + None + 15.2 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + ..\Output\BPI\$(Platform)\$(Config) + All + ..\Output\OBJ\$(Platform)\$(Config) + ..\Output\HPP\$(Platform)\$(Config) + Vcl;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + true + true + false + false + 00400000 + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;UIDeviceFamily=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;UISupportedInterfaceOrientations=;CFBundleExecutable=;CFBundleResourceSpecification=;LSRequiresIPhoneOS=;CFBundleInfoDictionaryVersion=;CFBundleDevelopmentRegion=;package=;label=;versionCode=0;versionName=;persistent=;restoreAnyVersion=;installLocation= + false + true + false + 1033 + Indy 10 Core Design Time + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1033 + + + false + 0 + 0 + RELEASE;$(DCC_Define) + + + Vcl;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + true + DEBUG;$(DCC_Define) + false + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore190.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + True + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore190.rc b/indy/Core/dclIndyCore190.rc new file mode 100644 index 0000000..0d3b741 --- /dev/null +++ b/indy/Core/dclIndyCore190.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore190\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore190.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore190.rc.tmpl b/indy/Core/dclIndyCore190.rc.tmpl new file mode 100644 index 0000000..d0ea5d7 --- /dev/null +++ b/indy/Core/dclIndyCore190.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore190\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore190.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore190.res b/indy/Core/dclIndyCore190.res new file mode 100644 index 0000000..6909cbe Binary files /dev/null and b/indy/Core/dclIndyCore190.res differ diff --git a/indy/Core/dclIndyCore200.dpk b/indy/Core/dclIndyCore200.dpk new file mode 100644 index 0000000..18f2934 --- /dev/null +++ b/indy/Core/dclIndyCore200.dpk @@ -0,0 +1,46 @@ +package dclIndyCore200; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER270} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem200, + IndyCore200; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore200.dproj b/indy/Core/dclIndyCore200.dproj new file mode 100644 index 0000000..8398ad1 --- /dev/null +++ b/indy/Core/dclIndyCore200.dproj @@ -0,0 +1,178 @@ + + + {4EF991B8-8E3C-40B6-8D59-6BE5CB25377C} + dclIndyCore200.dpk + True + Debug + 1 + Package + None + 15.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + false + true + true + false + 00400000 + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + Indy 10 Core Design Time + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;UIDeviceFamily=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;UISupportedInterfaceOrientations=;CFBundleExecutable=;CFBundleResourceSpecification=;LSRequiresIPhoneOS=;CFBundleInfoDictionaryVersion=;CFBundleDevelopmentRegion=;package=;label=;versionCode=;versionName=;persistent=;restoreAnyVersion=;installLocation=;largeHeap=;theme=;hardwareAccelerated= + false + 1033 + true + false + + + 1033 + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + 0 + 0 + false + RELEASE;$(DCC_Define) + + + true + DEBUG;$(DCC_Define) + false + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore200.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + LiveBinding Expression Components FireDac + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore200.rc b/indy/Core/dclIndyCore200.rc new file mode 100644 index 0000000..03c599e --- /dev/null +++ b/indy/Core/dclIndyCore200.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore200\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore200.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore200.rc.tmpl b/indy/Core/dclIndyCore200.rc.tmpl new file mode 100644 index 0000000..9417b0b --- /dev/null +++ b/indy/Core/dclIndyCore200.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore200\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore200.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore200.res b/indy/Core/dclIndyCore200.res new file mode 100644 index 0000000..0d89aef Binary files /dev/null and b/indy/Core/dclIndyCore200.res differ diff --git a/indy/Core/dclIndyCore210.dpk b/indy/Core/dclIndyCore210.dpk new file mode 100644 index 0000000..a4e0da6 --- /dev/null +++ b/indy/Core/dclIndyCore210.dpk @@ -0,0 +1,46 @@ +package dclIndyCore210; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$DEFINE VER280} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem210, + IndyCore210; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore210.dproj b/indy/Core/dclIndyCore210.dproj new file mode 100644 index 0000000..7037f49 --- /dev/null +++ b/indy/Core/dclIndyCore210.dproj @@ -0,0 +1,126 @@ + + + {2C3592FA-A1FB-4298-94E5-C13B699A702A} + dclIndyCore210.dpk + True + Debug + 1 + Package + None + 16.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + true + false + true + false + dclIndyCore210 + 00400000 + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + 1033 + false + false + true + Indy 10 Core Design Time + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true + Debug + + + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;Vcl;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + RELEASE;$(DCC_Define) + false + 0 + 0 + + + false + DEBUG;$(DCC_Define) + true + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore210.dpk + + + + False + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore210.rc b/indy/Core/dclIndyCore210.rc new file mode 100644 index 0000000..48b47fd --- /dev/null +++ b/indy/Core/dclIndyCore210.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore210\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore210.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore210.rc.tmpl b/indy/Core/dclIndyCore210.rc.tmpl new file mode 100644 index 0000000..80eb32a --- /dev/null +++ b/indy/Core/dclIndyCore210.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore210\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore210.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore210.res b/indy/Core/dclIndyCore210.res new file mode 100644 index 0000000..6a60fc3 Binary files /dev/null and b/indy/Core/dclIndyCore210.res differ diff --git a/indy/Core/dclIndyCore220.dpk b/indy/Core/dclIndyCore220.dpk new file mode 100644 index 0000000..6b59e41 --- /dev/null +++ b/indy/Core/dclIndyCore220.dpk @@ -0,0 +1,45 @@ +package dclIndyCore220; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem220, + IndyCore220; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore220.dproj b/indy/Core/dclIndyCore220.dproj new file mode 100644 index 0000000..32bcfc8 --- /dev/null +++ b/indy/Core/dclIndyCore220.dproj @@ -0,0 +1,180 @@ + + + {BD9C177F-CB7A-4763-98B4-1552DA2C1AF5} + dclIndyCore220.dpk + True + Debug + 1 + Package + None + 17.1 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + Indy 10 Core Design Time + false + true + false + true + false + dclIndyCore220 + 1033 + System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) + true + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + false + false + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true + Debug + android-support-v4.dex.jar;apk-expansion.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + false + 0 + RELEASE;$(DCC_Define) + 0 + + + true + DEBUG;$(DCC_Define) + false + + + true + + + true + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore220.dpk + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore220.rc b/indy/Core/dclIndyCore220.rc new file mode 100644 index 0000000..011417a --- /dev/null +++ b/indy/Core/dclIndyCore220.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore220\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore220.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore220.rc.tmpl b/indy/Core/dclIndyCore220.rc.tmpl new file mode 100644 index 0000000..d5ad3da --- /dev/null +++ b/indy/Core/dclIndyCore220.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore220\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore220.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore220.res b/indy/Core/dclIndyCore220.res new file mode 100644 index 0000000..d86ffa6 Binary files /dev/null and b/indy/Core/dclIndyCore220.res differ diff --git a/indy/Core/dclIndyCore230.dpk b/indy/Core/dclIndyCore230.dpk new file mode 100644 index 0000000..79bd4c0 --- /dev/null +++ b/indy/Core/dclIndyCore230.dpk @@ -0,0 +1,45 @@ +package dclIndyCore230; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION OFF} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES ON} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE DEBUG} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem230, + IndyCore230; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore230.dproj b/indy/Core/dclIndyCore230.dproj new file mode 100644 index 0000000..c522805 --- /dev/null +++ b/indy/Core/dclIndyCore230.dproj @@ -0,0 +1,180 @@ + + + {AAF2F84A-3A33-4B85-A44A-44CDC2D7C52A} + dclIndyCore230.dpk + True + Debug + 1 + Package + None + 17.1 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + Indy 10 Core Design Time + false + true + false + true + false + dclIndyCore230 + 1033 + System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) + true + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + false + false + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=preferExternal;largeHeap=False;theme=TitleBar;hardwareAccelerated=true + Debug + android-support-v4.dex.jar;apk-expansion.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + true + iPhoneAndiPad + $(MSBuildProjectName) + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + false + 0 + RELEASE;$(DCC_Define) + 0 + + + true + DEBUG;$(DCC_Define) + false + + + true + + + true + + + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + dclIndyCore230.dpk + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + False + True + False + + + 12 + + + + diff --git a/indy/Core/dclIndyCore230.rc b/indy/Core/dclIndyCore230.rc new file mode 100644 index 0000000..2076c24 --- /dev/null +++ b/indy/Core/dclIndyCore230.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore230\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore230.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore230.rc.tmpl b/indy/Core/dclIndyCore230.rc.tmpl new file mode 100644 index 0000000..ef41cf0 --- /dev/null +++ b/indy/Core/dclIndyCore230.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore230\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore230.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore230.res b/indy/Core/dclIndyCore230.res new file mode 100644 index 0000000..8020b98 Binary files /dev/null and b/indy/Core/dclIndyCore230.res differ diff --git a/indy/Core/dclIndyCore40.RES b/indy/Core/dclIndyCore40.RES new file mode 100644 index 0000000..021ba08 Binary files /dev/null and b/indy/Core/dclIndyCore40.RES differ diff --git a/indy/Core/dclIndyCore40.dpk b/indy/Core/dclIndyCore40.dpk new file mode 100644 index 0000000..8ce26a2 --- /dev/null +++ b/indy/Core/dclIndyCore40.dpk @@ -0,0 +1,40 @@ +package dclIndyCore40; + +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS ON} +{$RANGECHECKS ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + designide, + IndyCore40; + +contains + IdAbout in 'IdAbout.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnBaseCmpEdt in 'IdDsnBaseCmpEdt.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBinding in 'IdDsnPropEdBinding.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore40.rc b/indy/Core/dclIndyCore40.rc new file mode 100644 index 0000000..aa42c73 --- /dev/null +++ b/indy/Core/dclIndyCore40.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore40\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore40.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore40.rc.tmpl b/indy/Core/dclIndyCore40.rc.tmpl new file mode 100644 index 0000000..e073127 --- /dev/null +++ b/indy/Core/dclIndyCore40.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore40\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore40.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore50.cfg1 b/indy/Core/dclIndyCore50.cfg1 new file mode 100644 index 0000000..2ca3464 --- /dev/null +++ b/indy/Core/dclIndyCore50.cfg1 @@ -0,0 +1,42 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-JPHNE +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" diff --git a/indy/Core/dclIndyCore50.cfg2 b/indy/Core/dclIndyCore50.cfg2 new file mode 100644 index 0000000..9b6c368 --- /dev/null +++ b/indy/Core/dclIndyCore50.cfg2 @@ -0,0 +1,41 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" \ No newline at end of file diff --git a/indy/Core/dclIndyCore50.dpk b/indy/Core/dclIndyCore50.dpk new file mode 100644 index 0000000..f2479bc --- /dev/null +++ b/indy/Core/dclIndyCore50.dpk @@ -0,0 +1,36 @@ +package dclIndyCore50; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + Vcl50, + IndySystem50, + IndyCore50; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore50.rc b/indy/Core/dclIndyCore50.rc new file mode 100644 index 0000000..dc53044 --- /dev/null +++ b/indy/Core/dclIndyCore50.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore50\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore50.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore50.rc.tmpl b/indy/Core/dclIndyCore50.rc.tmpl new file mode 100644 index 0000000..8a92f0d --- /dev/null +++ b/indy/Core/dclIndyCore50.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore50\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore50.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore50.res b/indy/Core/dclIndyCore50.res new file mode 100644 index 0000000..dba844a Binary files /dev/null and b/indy/Core/dclIndyCore50.res differ diff --git a/indy/Core/dclIndyCore60.cfg1 b/indy/Core/dclIndyCore60.cfg1 new file mode 100644 index 0000000..2ca3464 --- /dev/null +++ b/indy/Core/dclIndyCore60.cfg1 @@ -0,0 +1,42 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-JPHNE +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" diff --git a/indy/Core/dclIndyCore60.cfg2 b/indy/Core/dclIndyCore60.cfg2 new file mode 100644 index 0000000..9b6c368 --- /dev/null +++ b/indy/Core/dclIndyCore60.cfg2 @@ -0,0 +1,41 @@ +-$A+ +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-$M16384,1048576 +-K4194304 +-DBCB +-E".\" +-N".\" +-LE".\" +-LN".\" +-U".\" +-O".\" +-I".\" +-R".\" \ No newline at end of file diff --git a/indy/Core/dclIndyCore60.dpk b/indy/Core/dclIndyCore60.dpk new file mode 100644 index 0000000..eeb0dcd --- /dev/null +++ b/indy/Core/dclIndyCore60.dpk @@ -0,0 +1,37 @@ +package dclIndyCore60; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + vcl, + designide, + IndySystem60, + IndyCore60; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore60.rc b/indy/Core/dclIndyCore60.rc new file mode 100644 index 0000000..6d1304a --- /dev/null +++ b/indy/Core/dclIndyCore60.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore60\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore60.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore60.rc.tmpl b/indy/Core/dclIndyCore60.rc.tmpl new file mode 100644 index 0000000..df37af5 --- /dev/null +++ b/indy/Core/dclIndyCore60.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore60\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore60.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore60.res b/indy/Core/dclIndyCore60.res new file mode 100644 index 0000000..d09547a Binary files /dev/null and b/indy/Core/dclIndyCore60.res differ diff --git a/indy/Core/dclIndyCore70.dpk b/indy/Core/dclIndyCore70.dpk new file mode 100644 index 0000000..af39640 --- /dev/null +++ b/indy/Core/dclIndyCore70.dpk @@ -0,0 +1,37 @@ +package dclIndyCore70; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + vcl, + designide, + IndySystem70, + IndyCore70; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore70.rc b/indy/Core/dclIndyCore70.rc new file mode 100644 index 0000000..c0985db --- /dev/null +++ b/indy/Core/dclIndyCore70.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore70\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore70.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore70.rc.tmpl b/indy/Core/dclIndyCore70.rc.tmpl new file mode 100644 index 0000000..076e48f --- /dev/null +++ b/indy/Core/dclIndyCore70.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore70\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore70.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore70.res b/indy/Core/dclIndyCore70.res new file mode 100644 index 0000000..bea7f69 Binary files /dev/null and b/indy/Core/dclIndyCore70.res differ diff --git a/indy/Core/dclIndyCore80Net.dpk b/indy/Core/dclIndyCore80Net.dpk new file mode 100644 index 0000000..9bf0c88 --- /dev/null +++ b/indy/Core/dclIndyCore80Net.dpk @@ -0,0 +1,41 @@ +package dclIndyCore80Net; + +{$ALIGN 0} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS ON} +{$RANGECHECKS ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + Borland.Studio.Vcl.Design, + IndyCore80Net; + +contains + IdAbout in 'IdAbout.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnBaseCmpEdt in 'IdDsnBaseCmpEdt.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBinding in 'IdDsnPropEdBinding.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore90.bdsproj b/indy/Core/dclIndyCore90.bdsproj new file mode 100644 index 0000000..474d911 --- /dev/null +++ b/indy/Core/dclIndyCore90.bdsproj @@ -0,0 +1,373 @@ + + + + + + + + + + + + dclIndyCore90.dpk + + + 7.0 + + + 8 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + False + False + True + True + True + + + 3 + 0 + 1 + False + False + False + 16384 + 1048576 + 4194304 + Indy 10 Core Design Time + + + + + + + + + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + diff --git a/indy/Core/dclIndyCore90.dpk b/indy/Core/dclIndyCore90.dpk new file mode 100644 index 0000000..48bffd4 --- /dev/null +++ b/indy/Core/dclIndyCore90.dpk @@ -0,0 +1,37 @@ +package dclIndyCore90; + +{$R *.res} +{$ALIGN 8} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystem90, + IndyCore90; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclIndyCore90.rc b/indy/Core/dclIndyCore90.rc new file mode 100644 index 0000000..0caabeb --- /dev/null +++ b/indy/Core/dclIndyCore90.rc @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,0 +PRODUCTVERSION 10,6,2,0 +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.0\0" + VALUE "InternalName", "dclIndyCore90\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore90.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore90.rc.tmpl b/indy/Core/dclIndyCore90.rc.tmpl new file mode 100644 index 0000000..560b293 --- /dev/null +++ b/indy/Core/dclIndyCore90.rc.tmpl @@ -0,0 +1,31 @@ +1 VERSIONINFO +FILEVERSION 10,6,2,$WCREV$ +PRODUCTVERSION 10,6,2,$WCREV$ +FILEFLAGSMASK 0x3FL +FILEFLAGS 0x00L +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +{ + BLOCK "StringFileInfo" + { + BLOCK "000104E4" + { + VALUE "CompanyName", "Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "FileDescription", "Internet Direct (Indy) 10.6.2 - Core Design-Time Package\0" + VALUE "FileVersion", "10.6.2.$WCREV$\0" + VALUE "InternalName", "dclIndyCore90\0" + VALUE "LegalCopyright", "Copyright 1993 - 2015 Chad Z. Hower a.k.a Kudzu and the Indy Pit Crew\0" + VALUE "OriginalFilename", "dclIndyCore90.bpl\0" + VALUE "ProductName", "Indy\0" + VALUE "ProductVersion", "10.6.2\0" + } + + } + + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0001, 1252 + } + +} diff --git a/indy/Core/dclIndyCore90.res b/indy/Core/dclIndyCore90.res new file mode 100644 index 0000000..940ca42 Binary files /dev/null and b/indy/Core/dclIndyCore90.res differ diff --git a/indy/Core/dclIndyCore90Net.bdsproj b/indy/Core/dclIndyCore90Net.bdsproj new file mode 100644 index 0000000..97305e1 --- /dev/null +++ b/indy/Core/dclIndyCore90Net.bdsproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + dclIndyCore90Net.dpk + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + Indy 10 Core Design Time + + + + + + + W:\source\Indy10\Lib\Core;w:\source\indy10\lib\core;D:\Documents and Settings\J. Peter Mugaas.J-VI69HVK7KPRD5.000 + IndyCore90Net;D:\Documents and Settings\J. Peter Mugaas.J-VI69HVK7KPRD5.000\Borland.Studio.Vcl.Design.dll + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + + + + + + + + + + + + diff --git a/indy/Core/dclIndyCore90Net.dpk b/indy/Core/dclIndyCore90Net.dpk new file mode 100644 index 0000000..73b0836 --- /dev/null +++ b/indy/Core/dclIndyCore90Net.dpk @@ -0,0 +1,38 @@ +package dclIndyCore90Net; + +{$ALIGN 0} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + System.Windows.Forms, + Borland.Studio.Vcl.Design, + IndySystem90Net, + IndyCore90Net; + +contains + IdAboutDotNET in 'IdAboutDotNET.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas', + IdRegisterCore in 'IdRegisterCore.pas'; +{$I IddclCore90ASM90.inc} + +end. diff --git a/indy/Core/dclIndyCore_Icon.ico b/indy/Core/dclIndyCore_Icon.ico new file mode 100644 index 0000000..b1c1298 Binary files /dev/null and b/indy/Core/dclIndyCore_Icon.ico differ diff --git a/indy/Core/dclindycorek3.dpk b/indy/Core/dclindycorek3.dpk new file mode 100644 index 0000000..121877c --- /dev/null +++ b/indy/Core/dclindycorek3.dpk @@ -0,0 +1,36 @@ +package dclIndyCoreK3; + +{$R *.res} +{$BOOLEVAL OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DESCRIPTION 'Indy 10 Core Design Time'} +{$DESIGNONLY} +{$IMPLICITBUILD ON} + +requires + designide, + IndySystemK3, + IndyCoreK3; + +contains + IdAboutVCL in 'IdAboutVCL.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingVCL in 'IdDsnPropEdBindingVCL.pas', + IdRegisterCore in 'IdRegisterCore.pas'; + +end. diff --git a/indy/Core/dclindycorek3.dpkl b/indy/Core/dclindycorek3.dpkl new file mode 100644 index 0000000..e370de1 --- /dev/null +++ b/indy/Core/dclindycorek3.dpkl @@ -0,0 +1,45 @@ +package dclindycorek3; + +{$R *.res} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO ON} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST ON} +{$MINENUMSIZE 1} +{$IMAGEBASE $600000} +{$DESCRIPTION 'Internet Direct (Indy) 10.00.0.17-B - Core 10.00.0.17-B'} +{$SONAME 'dclindycorek3.so.6.9'} +{$LIBPREFIX ''} +{$LIBVERSION '.6.9.0'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + designide, + indycorek3; + +contains + IdAbout in 'IdAbout.pas', + IdAntiFreeze in 'IdAntiFreeze.pas', + IdCoreDsnRegister in 'IdCoreDsnRegister.pas', + IdCoreRegister in 'IdCoreRegister.pas', + IdDsnBaseCmpEdt in 'IdDsnBaseCmpEdt.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBinding in 'IdDsnPropEdBinding.pas'; + +end. diff --git a/indy/Core/dclindycorek3.res b/indy/Core/dclindycorek3.res new file mode 100644 index 0000000..36f26e2 Binary files /dev/null and b/indy/Core/dclindycorek3.res differ diff --git a/indy/Core/dclindycorelaz.lpk b/indy/Core/dclindycorelaz.lpk new file mode 100644 index 0000000..ad15025 --- /dev/null +++ b/indy/Core/dclindycorelaz.lpk @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indy/Core/dclindycorelaz.lpk.tmpl b/indy/Core/dclindycorelaz.lpk.tmpl new file mode 100644 index 0000000..fcbc667 --- /dev/null +++ b/indy/Core/dclindycorelaz.lpk.tmpl @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indy/Core/indy_pit_crew_credits_window.psd b/indy/Core/indy_pit_crew_credits_window.psd new file mode 100644 index 0000000..db044e5 Binary files /dev/null and b/indy/Core/indy_pit_crew_credits_window.psd differ diff --git a/indy/Core/indycorefpc.pas b/indy/Core/indycorefpc.pas new file mode 100644 index 0000000..5dc2a25 --- /dev/null +++ b/indy/Core/indycorefpc.pas @@ -0,0 +1,71 @@ +unit indycorefpc; +interface + +uses + IdAssignedNumbers, + IdBuffer, + IdCmdTCPClient, + IdCmdTCPServer, + IdCommandHandlers, + IdContext, + IdCustomTCPServer, + IdCustomTransparentProxy, + IdExceptionCore, + IdGlobalCore, + IdIOHandler, + IdIOHandlerSocket, + IdIOHandlerStack, + IdIOHandlerStream, + IdIPAddress, + IdIPMCastBase, + IdIPMCastClient, + IdIPMCastServer, + IdIcmpClient, + IdIntercept, + IdInterceptSimLog, + IdInterceptThrottler, + IdLogBase, + IdLogDebug, + IdLogEvent, + IdLogFile, + IdLogStream, + IdRawBase, + IdRawClient, + IdRawFunctions, + IdRawHeaders, + IdReply, + IdReplyRFC, + IdResourceStringsCore, + IdScheduler, + IdSchedulerOfThread, + IdSchedulerOfThreadDefault, + IdSchedulerOfThreadPool, + IdServerIOHandler, + IdServerIOHandlerSocket, + IdServerIOHandlerStack, + IdSimpleServer, + IdSocketHandle, + IdSocks, + IdSync, + IdTCPClient, + IdTCPConnection, + IdTCPServer, + IdTCPStream, + IdTask, + IdThread, + IdThreadComponent, + IdThreadSafe, + IdTraceRoute, + IdUDPBase, + IdUDPClient, + IdUDPServer, + IdYarn; + +implementation + +{ +disable hints about unused units. This unit just causes +FreePascal to compile implicitly listed units in a package. +} +{$hints off} +end. diff --git a/indy/Design/DsnIndy.bdsproj b/indy/Design/DsnIndy.bdsproj new file mode 100644 index 0000000..1d6c9f7 --- /dev/null +++ b/indy/Design/DsnIndy.bdsproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DsnIndy.dpr + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + + + + + + + + + + + + False + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + diff --git a/indy/Design/DsnIndy.dpr b/indy/Design/DsnIndy.dpr new file mode 100644 index 0000000..96fb4fe --- /dev/null +++ b/indy/Design/DsnIndy.dpr @@ -0,0 +1,77 @@ +library DsnIndy; + + + + +uses + SysUtils, + Classes, + System.Reflection, + System.Runtime.InteropServices; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion('1.0.*')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')], provided your output +// directory is the project directory (the default). +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +// +// Use the attributes below to control the COM visibility of your assembly. By +// default the entire assembly is visible to COM. Setting ComVisible to false +// is the recommended default for your assembly. To then expose a class and interface +// to COM set ComVisible to true on each one. It is also recommended to add a +// Guid attribute. +// + +[assembly: ComVisible(False)] +//[assembly: Guid('')] +//[assembly: TypeLibVersion(1, 0)] + + +begin +end. diff --git a/indy/Design/DsnIndy90.bdsproj b/indy/Design/DsnIndy90.bdsproj new file mode 100644 index 0000000..d658304 --- /dev/null +++ b/indy/Design/DsnIndy90.bdsproj @@ -0,0 +1,206 @@ + + + + + + + + + + + + DsnIndy90.dpr + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + + + + W:\source\Indy10\Lib\System + + + + c:\windows\microsoft.net\framework\v1.1.4322;w:\source\indy10\lib\system + IndySystem90Net;c:\windows\microsoft.net\framework\v1.1.4322\System.Design.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Data.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.Design.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.dll + + + True + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indy/Design/DsnIndy90.dpr b/indy/Design/DsnIndy90.dpr new file mode 100644 index 0000000..a32db47 --- /dev/null +++ b/indy/Design/DsnIndy90.dpr @@ -0,0 +1,88 @@ +library DsnIndy90; + + +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Drawing.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Drawing.Design.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Data.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Design.dll'} +{%DelphiDotNetAssemblyCompiler '..\system\IndySystem90Net.dll'} +{$R 'IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources' 'IdDsnPropEdBindingNET.resx'} + +uses + System.Data, + System.ComponentModel, + System.ComponentModel.Design, + System.Reflection, + System.Runtime.InteropServices, + IdDsnNETCompEditor in 'IdDsnNETCompEditor.pas', + IdDsnCoreResourceStrings in 'IdDsnCoreResourceStrings.pas', + IdDsnPropEdBindingNET in 'IdDsnPropEdBindingNET.pas' {IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET: System.Windows.Forms.Form}; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion('1.0.*')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')], provided your output +// directory is the project directory (the default). +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySignAttribute(false)] +[assembly: AssemblyKeyFileAttribute('')] +[assembly: AssemblyKeyName('')] + +// +// Use the attributes below to control the COM visibility of your assembly. By +// default the entire assembly is visible to COM. Setting ComVisible to false +// is the recommended default for your assembly. To then expose a class and interface +// to COM set ComVisible to true on each one. It is also recommended to add a +// Guid attribute. +// + +[assembly: ComVisible(False)] +//[assembly: Guid('')] +//[assembly: TypeLibVersion(1, 0)] + + +begin +end. diff --git a/indy/Design/IdDsnCoreResourceStrings.pas b/indy/Design/IdDsnCoreResourceStrings.pas new file mode 100644 index 0000000..8172e87 --- /dev/null +++ b/indy/Design/IdDsnCoreResourceStrings.pas @@ -0,0 +1,129 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ +{ Rev 1.5 1/29/2004 8:54:30 AM JPMugaas +{ Removed myself from the distribution Team Chairperson entry as I am resigning +{ from that role. +} +{ + Rev 1.4 10/15/2003 10:11:46 PM DSiders + Added resource string for About box credits. + Corrected spelling error in comments. +} +{ +{ Rev 1.3 6/16/2003 12:01:44 PM JPMugaas +{ Updated copyright to say 2003. +} +{ +{ Rev 1.2 6/8/2003 05:46:58 AM JPMugaas +{ The kitchen sink has now been implemented. +} +{ +{ Rev 1.1 1/15/2003 08:03:56 AM JPMugaas +{ Updated with new website address. +} +{ +{ Rev 1.0 11/13/2002 08:43:24 AM JPMugaas +} +unit IdDsnCoreResourceStrings; +{ +Note: This unit is for resource strings that are used in the Core Design-Time package +and NOT any design-time packages. This is to prevent design-time resource strings +from being linked into the Run-Time only package. +} +interface + +const + IndyPitCrew = 'Kudzu (Chad Z. Hower)'#13#10 + + 'and the'#13#10 + + 'Indy Pit Crew'; + +resourcestring + { About Box stuff } + RSAAboutFormCaption = 'About'; + RSAAboutBoxCompName = 'Internet Direct (Indy)'; + RSAAboutMenuItemName = 'About Internet &Direct (Indy) %s...'; + + RSAAboutBoxVersion = 'Version %s'; + RSAAboutBoxCopyright = 'Copyright (c) 1993 - 2005'#13#10 + + IndyPitCrew; + + RSAAboutBoxPleaseVisit = 'For the latest updates and information please visit:'; + RSAAboutBoxIndyWebsite = 'http://www.indyproject.org/'; {Do not Localize} + + RSAAboutCreditsCoordinator = 'Project Coordinator'; + RSAAboutCreditsCoCordinator = 'Project Co-Coordinator'; + RSAAboutCreditsDocumentation = 'Documentation Coordinator'; + RSAAboutCreditsDemos = 'Demos Coordinator'; + RSAAboutCreditsRetiredPast = 'Retired/Past Contributors'; + RSAAboutCreditsIndyCrew = 'The Indy Pit Crew'; + + RSAAboutKitchenSink = IndyPitCrew+#10#13+'present the'#10#13'Kitchen Sink'; + + {Binding Editor stuff} + { + Note to translators - Please Read!!! + + For all the constants except RSBindingFormCaption, there may be an + & symbol before a letter or number. This is rendered as that character being + underlined. In addition, the character after the & symbol along with the ALT + key enables a user to move to that control. Since these are on one form, be + careful to ensure that the same letter or number does not have a & before it + in more than one string, otherwise an ALT key sequence will be broken. + } + RSBindingFormCaption = 'Binding Editor'; + RSBindingNewCaption = '&New'; + RSBindingDeleteCaption = '&Delete'; + //RSBindingAddCaption = '&Add'; + RSBindingRemoveCaption = '&Remove'; + RSBindingLabelBindings = '&Bindings'; + RSBindingHostnameLabel = '&IP Address'; + RSBindingPortLabel = '&Port'; + RSBindingIPVerLabel = 'IP &Version'; + RSBindingIPV4Item = 'IPv&4 (127.0.0.1)'; + RSBindingIPV6Item = 'IPv&6 (::1)'; + {Design time SASLList Hints} + RSADlgSLMoveUp = 'Move Up'; + RSADlgSLMoveDown = 'Move Down'; + RSADlgSLAdd = 'Add'; + RSADlgSLRemove = 'Remove'; + //Caption that uses format with component name + RSADlgSLCaption = 'Editing SASL List for %s'; + RSADlgSLAvailable = '&Available'; + RSADlgSLAssigned = 'A&ssigned (tried in order listed)'; + {Note that the Ampersand indicates an ALT keyboard sequence} + RSADlgSLEditList = 'Edit &List'; + //Display item constants + RSBindingAll = 'All'; //all IP addresses + RSBindingAny = 'Any'; //any port + + {Standard dialog stock strings} + RSOk = 'OK'; + RSCancel = 'Cancel'; + RSHelp = '&Help'; + + // IdRegister + RSRegIndyClients = 'Indy Clients'; + RSRegIndyServers = 'Indy Servers'; + RSRegIndyIntercepts = 'Indy Intercepts'; + RSRegIndyIOHandlers = 'Indy I/O Handlers'; + RSRegIndyMisc = 'Indy Misc'; + +implementation + +end. diff --git a/indy/Design/IdDsnNETCompEditor.pas b/indy/Design/IdDsnNETCompEditor.pas new file mode 100644 index 0000000..3729357 --- /dev/null +++ b/indy/Design/IdDsnNETCompEditor.pas @@ -0,0 +1,46 @@ +unit IdDsnNETCompEditor; + +interface +uses + System.ComponentModel, + System.ComponentModel.Design; + +type + TIdNetComponentEditor = class(System.ComponentModel.Design.ComponentDesigner) + protected + procedure OnVerItemSelected(sender : System.Object; args : System.EventArgs); + + public + function get_Verbs : DesignerVerbCollection; override; + end; + +implementation +uses + IdAbout, + IdGlobal, + IdDsnCoreResourceStrings, + IdSys; + +{ TIdNetComponentEditor } + +function TIdNetComponentEditor.get_Verbs: DesignerVerbCollection; +var LV : DesignerVerb; +begin + Result := inherited get_Verbs; + if not Assigned(Result) then + begin + Result := DesignerVerbCollection.Create; + end; + LV := DesignerVerb.Create(Sys.Format(RSAAboutMenuItemName, [gsIdVersion]),OnVerItemSelected); + LV.Enabled := True; + LV.Visible := True; + Result.Add(LV); +end; + +procedure TIdNetComponentEditor.OnVerItemSelected(sender: TObject; + args: System.EventArgs); +begin + ShowAboutBox(RSAAboutBoxCompName, gsIdVersion) +end; + +end. diff --git a/indy/Design/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources b/indy/Design/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources new file mode 100644 index 0000000..61b5d53 Binary files /dev/null and b/indy/Design/IdDsnPropEdBindingNET.TIdDsnPropEdBindingNET.resources differ diff --git a/indy/Design/IdDsnPropEdBindingNET.pas b/indy/Design/IdDsnPropEdBindingNET.pas new file mode 100644 index 0000000..4254b03 --- /dev/null +++ b/indy/Design/IdDsnPropEdBindingNET.pas @@ -0,0 +1,679 @@ +unit IdDsnPropEdBindingNET; + +interface + +uses + System.Drawing, System.Collections, System.ComponentModel, + System.Windows.Forms, System.Data, IdObjs, IdSocketHandle; + +type + TIdDsnPropEdBindingNET = class(System.Windows.Forms.Form) + {$REGION 'Designer Managed Code'} + strict private + /// + /// Required designer variable. + /// + Components: System.ComponentModel.Container; + btnOk: System.Windows.Forms.Button; + btnCancel: System.Windows.Forms.Button; + lblBindings: System.Windows.Forms.Label; + lbBindings: System.Windows.Forms.ListBox; + btnNew: System.Windows.Forms.Button; + btnDelete: System.Windows.Forms.Button; + lblIPAddress: System.Windows.Forms.Label; + edtIPAddress: System.Windows.Forms.ComboBox; + lblPort: System.Windows.Forms.Label; + edtPort: System.Windows.Forms.NumericUpDown; + cboIPVersion: System.Windows.Forms.ComboBox; + lblIPVersion: System.Windows.Forms.Label; + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + procedure InitializeComponent; + procedure btnNew_Click(sender: System.Object; e: System.EventArgs); + procedure btnDelete_Click(sender: System.Object; e: System.EventArgs); + procedure edtPort_ValueChanged(sender: System.Object; e: System.EventArgs); + procedure edtIPAddress_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + procedure cboIPVersion_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + procedure lbBindings_SelectedValueChanged(sender: System.Object; e: System.EventArgs); + {$ENDREGION} + strict protected + /// + /// Clean up any resources being used. + /// + procedure Dispose(Disposing: Boolean); override; + private + FHandles : TIdSocketHandles; + FDefaultPort : Integer; + FIPv4Addresses : TIdStrings; + FIPv6Addresses : TIdStrings; + FCurrentHandle : TIdSocketHandle; + + { Private Declarations } + procedure SetHandles(const Value: TIdSocketHandles); + procedure SetIPv4Addresses(const Value: TIdStrings); + procedure SetIPv6Addresses(const Value: TIdStrings); + procedure UpdateBindingList; + procedure UpdateEditControls; + procedure FillComboBox(ACombo : System.Windows.Forms.ComboBox; AStrings :TIdStrings); + public + constructor Create; + function Execute : Boolean; + function GetList: string; + procedure SetList(const AList: string); + property Handles : TIdSocketHandles read FHandles write SetHandles; + property DefaultPort : Integer read FDefaultPort write FDefaultPort; + property IPv4Addresses : TIdStrings read FIPv4Addresses write SetIPv4Addresses; + property IPv6Addresses : TIdStrings read FIPv6Addresses write SetIPv6Addresses; + + end; + + [assembly: RuntimeRequiredAttribute(TypeOf(TIdDsnPropEdBindingNET))] + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; + +implementation +uses + IdGlobal, + IdIPAddress, + IdDsnCoreResourceStrings, IdStack, IdSys; + +const + IPv6Wildcard1 = '::'; {do not localize} + IPv6Wildcard2 = '0:0:0:0:0:0:0:0'; {do not localize} + IPv6Loopback = '::1'; {do not localize} + IPv4Wildcard = '0.0.0.0'; {do not localize} + IPv4Loopback = '127.0.0.1'; {do not localize} + +function IsValidIP(const AAddr : String): Boolean; +var LIP : TIdIPAddress; +begin + LIP := TIdIPAddress.MakeAddressObject(AAddr); + Result := Assigned(LIP); + if Result then + begin + Sys.FreeAndNil(LIP); + end; +end; + +function StripAndSymbol(s : String) : String; +begin + Result := ''; + repeat + if s='' then + begin + Break; + end; + Result := Result + Fetch(s,'&'); + until False; +end; + +procedure FillHandleList(const AList: string; ADest: TIdSocketHandles); +var + LItems: TIdStringList; + i: integer; + LIPVersion: TIdIPVersion; + LAddr, LText: string; + LPort: integer; +begin + + ADest.Clear; + LItems := TIdStringList.Create; + try + LItems.CommaText := AList; + for i := 0 to LItems.Count-1 do begin + if Length(LItems[i]) > 0 then begin + if TextStartsWith(LItems[i], '[') then begin + // ipv6 + LIPVersion := Id_IPv6; + LText := Copy(LItems[i], 2, MaxInt); + LAddr := Fetch(LText, ']:'); + LPort := Sys.StrToInt(LText, -1); + + end else begin + // ipv4 + LIPVersion := Id_IPv4; + LText := LItems[i]; + LAddr := Fetch(LText, ':'); + LPort := Sys.StrToInt(LText, -1); + //Note that 0 is legal and indicates the server binds to a random port + end; + if IsValidIP(LAddr) and (LPort > -1) and (LPort < 65536) then begin + with ADest.Add do begin + IPVersion := LIPVersion; + IP := LAddr; + Port := LPort; + end; + end; + end; + end; + finally + LItems.Free; + end; +end; + +function NumericOnly(const AText : String) : String; +var i : Integer; + +begin + Result := ''; + for i := 1 to Length(AText) do + begin + if IsNumeric(AText[i]) then + begin + Result := Result + AText[i]; + end + else + begin + Break; + end; + end; + if (Length(Result) = 0) then + begin + Result := '0'; + end; +end; + +function IndexOfNo(const ANo : Integer; AItems : System.Windows.Forms.ComboBox.ObjectCollection) : Integer; +begin + for Result := 0 to AItems.Count -1 do + begin + if ANo = Sys.StrToInt( NumericOnly(AItems[Result].ToString )) then + begin + Exit; + end; + end; + Result := -1; +end; + +function GetDisplayString(const AIP : String; const APort : Integer; AIPVer : TIdIPVersion): string; +begin + Result := ''; + case AIPVer of + Id_IPv4 : Result := Sys.Format('%s:%d',[AIP,APort]); + Id_IPv6 : Result := Sys.Format('[%s]:%d',[AIP,APort]); + end; +end; + +function GetListValues(const ASocketHandles : TIdSocketHandles) : String; +var i : Integer; +begin + Result := ''; + for i := 0 to ASocketHandles.Count -1 do begin + Result := Result + ',' + GetDisplayString(ASocketHandles[i].IP,ASocketHandles[i].Port,ASocketHandles[i].IPVersion ); + end; + Delete(Result,1,1); +end; + +{$AUTOBOX ON} + +{$REGION 'Windows Form Designer generated code'} +/// +/// Required method for Designer support -- do not modify +/// the contents of this method with the code editor. +/// +procedure TIdDsnPropEdBindingNET.InitializeComponent; +type + TArrayOfInteger = array of Integer; +begin + Self.btnOk := System.Windows.Forms.Button.Create; + Self.btnCancel := System.Windows.Forms.Button.Create; + Self.lblBindings := System.Windows.Forms.Label.Create; + Self.lbBindings := System.Windows.Forms.ListBox.Create; + Self.btnNew := System.Windows.Forms.Button.Create; + Self.btnDelete := System.Windows.Forms.Button.Create; + Self.lblIPAddress := System.Windows.Forms.Label.Create; + Self.edtIPAddress := System.Windows.Forms.ComboBox.Create; + Self.lblPort := System.Windows.Forms.Label.Create; + Self.edtPort := System.Windows.Forms.NumericUpDown.Create; + Self.cboIPVersion := System.Windows.Forms.ComboBox.Create; + Self.lblIPVersion := System.Windows.Forms.Label.Create; + (System.ComponentModel.ISupportInitialize(Self.edtPort)).BeginInit; + Self.SuspendLayout; + // + // btnOk + // + Self.btnOk.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnOk.DialogResult := System.Windows.Forms.DialogResult.OK; + Self.btnOk.Location := System.Drawing.Point.Create(312, 160); + Self.btnOk.Name := 'btnOk'; + Self.btnOk.TabIndex := 0; + // + // btnCancel + // + Self.btnCancel.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnCancel.DialogResult := System.Windows.Forms.DialogResult.Cancel; + Self.btnCancel.Location := System.Drawing.Point.Create(392, 160); + Self.btnCancel.Name := 'btnCancel'; + Self.btnCancel.TabIndex := 1; + // + // lblBindings + // + Self.lblBindings.AutoSize := True; + Self.lblBindings.Location := System.Drawing.Point.Create(8, 8); + Self.lblBindings.Name := 'lblBindings'; + Self.lblBindings.Size := System.Drawing.Size.Create(42, 16); + Self.lblBindings.TabIndex := 2; + Self.lblBindings.Text := '&Binding'; + // + // lbBindings + // + Self.lbBindings.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Bottom) or System.Windows.Forms.AnchorStyles.Left))); + Self.lbBindings.Location := System.Drawing.Point.Create(8, 24); + Self.lbBindings.Name := 'lbBindings'; + Self.lbBindings.Size := System.Drawing.Size.Create(137, 121); + Self.lbBindings.TabIndex := 3; + Include(Self.lbBindings.SelectedValueChanged, Self.lbBindings_SelectedValueChanged); + // + // btnNew + // + Self.btnNew.Location := System.Drawing.Point.Create(152, 56); + Self.btnNew.Name := 'btnNew'; + Self.btnNew.TabIndex := 4; + Include(Self.btnNew.Click, Self.btnNew_Click); + // + // btnDelete + // + Self.btnDelete.Location := System.Drawing.Point.Create(152, 88); + Self.btnDelete.Name := 'btnDelete'; + Self.btnDelete.TabIndex := 5; + Include(Self.btnDelete.Click, Self.btnDelete_Click); + // + // lblIPAddress + // + Self.lblIPAddress.Location := System.Drawing.Point.Create(240, 8); + Self.lblIPAddress.Name := 'lblIPAddress'; + Self.lblIPAddress.Size := System.Drawing.Size.Create(100, 16); + Self.lblIPAddress.TabIndex := 6; + Self.lblIPAddress.Text := 'Label1'; + // + // edtIPAddress + // + Self.edtIPAddress.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.edtIPAddress.DropDownStyle := System.Windows.Forms.ComboBoxStyle.DropDownList; + Self.edtIPAddress.Location := System.Drawing.Point.Create(240, 24); + Self.edtIPAddress.Name := 'edtIPAddress'; + Self.edtIPAddress.Size := System.Drawing.Size.Create(224, 21); + Self.edtIPAddress.TabIndex := 7; + Include(Self.edtIPAddress.SelectedValueChanged, Self.edtIPAddress_SelectedValueChanged); + // + // lblPort + // + Self.lblPort.Location := System.Drawing.Point.Create(240, 58); + Self.lblPort.Name := 'lblPort'; + Self.lblPort.Size := System.Drawing.Size.Create(100, 16); + Self.lblPort.TabIndex := 8; + Self.lblPort.Text := 'Label1'; + // + // edtPort + // + Self.edtPort.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.edtPort.Location := System.Drawing.Point.Create(240, 74); + Self.edtPort.Maximum := System.Decimal.Create(TArrayOfInteger.Create(65535, + 0, 0, 0)); + Self.edtPort.Name := 'edtPort'; + Self.edtPort.Size := System.Drawing.Size.Create(224, 20); + Self.edtPort.TabIndex := 9; + Include(Self.edtPort.ValueChanged, Self.edtPort_ValueChanged); + // + // cboIPVersion + // + Self.cboIPVersion.DropDownStyle := System.Windows.Forms.ComboBoxStyle.DropDownList; + Self.cboIPVersion.Location := System.Drawing.Point.Create(240, 124); + Self.cboIPVersion.Name := 'cboIPVersion'; + Self.cboIPVersion.Size := System.Drawing.Size.Create(224, 21); + Self.cboIPVersion.TabIndex := 10; + Include(Self.cboIPVersion.SelectedValueChanged, Self.cboIPVersion_SelectedValueChanged); + // + // lblIPVersion + // + Self.lblIPVersion.Location := System.Drawing.Point.Create(240, 108); + Self.lblIPVersion.Name := 'lblIPVersion'; + Self.lblIPVersion.Size := System.Drawing.Size.Create(100, 16); + Self.lblIPVersion.TabIndex := 11; + Self.lblIPVersion.Text := 'Label1'; + // + // TIdDsnPropEdBindingNET + // + Self.AcceptButton := Self.btnOk; + Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); + Self.CancelButton := Self.btnCancel; + Self.ClientSize := System.Drawing.Size.Create(470, 189); + Self.Controls.Add(Self.lblIPVersion); + Self.Controls.Add(Self.cboIPVersion); + Self.Controls.Add(Self.edtPort); + Self.Controls.Add(Self.lblPort); + Self.Controls.Add(Self.edtIPAddress); + Self.Controls.Add(Self.lblIPAddress); + Self.Controls.Add(Self.btnDelete); + Self.Controls.Add(Self.btnNew); + Self.Controls.Add(Self.lbBindings); + Self.Controls.Add(Self.lblBindings); + Self.Controls.Add(Self.btnCancel); + Self.Controls.Add(Self.btnOk); + Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.Fixed3D; + Self.MaximizeBox := False; + Self.MaximumSize := System.Drawing.Size.Create(480, 225); + Self.MinimizeBox := False; + Self.MinimumSize := System.Drawing.Size.Create(480, 225); + Self.Name := 'TIdDsnPropEdBindingNET'; + Self.ShowInTaskbar := False; + Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + Self.Text := 'WinForm'; + (System.ComponentModel.ISupportInitialize(Self.edtPort)).EndInit; + Self.ResumeLayout(False); +end; +{$ENDREGION} + +procedure TIdDsnPropEdBindingNET.Dispose(Disposing: Boolean); +begin + if Disposing then + begin + if Components <> nil then + begin + Components.Dispose(); + Sys.FreeAndNil(FHandles); + + Sys.FreeAndNil( FIPv4Addresses); + Sys.FreeAndNil( FIPv6Addresses); + + //don't free FCurrentHandle; - it's in the handles collection + TIdStack.DecUsage; + end; + end; + inherited Dispose(Disposing); + +end; + +constructor TIdDsnPropEdBindingNET.Create; +begin + inherited Create; + // + // Required for Windows Form Designer support + // + InitializeComponent; + // + // TODO: Add any constructor code after InitializeComponent call + // + FHandles := TIdSocketHandles.Create(nil); + FIPv4Addresses := TIdStringList.Create; + FIPv6Addresses := TIdStringList.Create; + SetIPv4Addresses(nil); + SetIPv6Addresses(nil); + + TIdStack.IncUsage; + + IPv4Addresses := GStack.LocalAddresses; + UpdateEditControls; + //captions + btnNew.Text := RSBindingNewCaption; + btnDelete.Text := RSBindingDeleteCaption; + lblIPAddress.Text := RSBindingHostnameLabel; + lblPort.Text := RSBindingPortLabel; + lblIPVersion.Text := RSBindingIPVerLabel; + btnOk.Text := RSOk; + btnCancel.Text := RSCancel; + //IPVersion choices + //we yhave to strip out the & symbol. In Win32, we use this + //in a radio-box so a user could select by pressingg the 4 or 6 + //key. For this, we don't have a radio box and I'm too lazy + //to use two Radio Buttons. + cboIPVersion.Items.Add(StripAndSymbol(RSBindingIPV4Item)); + cboIPVersion.Items.Add(StripAndSymbol(RSBindingIPV6Item)); + +end; + +procedure TIdDsnPropEdBindingNET.SetHandles(const Value: TIdSocketHandles); +begin + FHandles.Assign(Value); + UpdateBindingList; +end; + +function TIdDsnPropEdBindingNET.GetList: string; +begin + Result := GetListValues(Handles); +end; + +procedure TIdDsnPropEdBindingNET.SetIPv6Addresses(const Value: TIdStrings); +begin + if Assigned(Value) then + begin + FIPv6Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv6Addresses.IndexOf(IPv6Loopback)=-1 then + begin + FIPv6Addresses.Insert(0,IPv6Loopback); + end; + if FIPv6Addresses.IndexOf(IPv6Wildcard1)=-1 then + begin + FIPv6Addresses.Insert(0,IPv6Wildcard1); + end; +end; + +procedure TIdDsnPropEdBindingNET.SetIPv4Addresses(const Value: TIdStrings); +begin + if Assigned(Value) then + begin + FIPv4Addresses.Assign(Value); + end; + // Ensure that these two are always present + if FIPv4Addresses.IndexOf(IPv6Loopback)=-1 then + begin + FIPv4Addresses.Insert(0,IPv4Loopback); + end; + + if FIPv4Addresses.IndexOf(IPv4Wildcard)=-1 then + begin + FIPv4Addresses.Insert(0,IPv4Wildcard); + end; +end; + +procedure TIdDsnPropEdBindingNET.SetList(const AList: string); +begin + FCurrentHandle := nil; + FillHandleList(AList, Handles); + UpdateBindingList; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.lbBindings_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + if lbBindings.SelectedIndex >= 0 then begin + btnDelete.Enabled := True; + FCurrentHandle := FHandles[lbBindings.SelectedIndex]; + end else begin + btnDelete.Enabled := False; + FCurrentHandle := nil; + end; + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.cboIPVersion_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + case cboIPVersion.SelectedIndex of + 0 : + begin + if FCurrentHandle.IPVersion <> Id_IPv4 then + begin + FCurrentHandle.IPVersion := Id_IPv4; + FillComboBox(edtIPAddress,FIPv4Addresses); + FCurrentHandle.IP := IPv4Wildcard; + end; + end; + 1 : + begin + if FCurrentHandle.IPVersion <> Id_IPv6 then + begin + FCurrentHandle.IPVersion := Id_IPv6; + FillComboBox(edtIPAddress,FIPv6Addresses); + FCurrentHandle.IP := IPv6Wildcard1; + end; + end; + end; + UpdateEditControls; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.edtIPAddress_SelectedValueChanged(sender: System.Object; + e: System.EventArgs); +begin + FCurrentHandle.IP := edtIPAddress.SelectedItem.ToString; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.edtPort_ValueChanged(sender: System.Object; + e: System.EventArgs); +begin + if Assigned(FCurrentHandle) then + begin + FCurrentHandle.Port := edtPort.Value.ToInt16(edtPort.Value); + end; + UpdateBindingList; +end; + +procedure TIdDsnPropEdBindingNET.btnDelete_Click(sender: System.Object; e: System.EventArgs); +var LSH : TIdSocketHandle; + i : Integer; +begin + if lbBindings.SelectedIndex >= 0 then + begin + // Delete is not available in D4's collection classes + // This should work just as well. + i := lbBindings.get_SelectedIndex; + LSH := Handles[i]; + Sys.FreeAndNil(LSH); + lbBindings.Items.Remove(i); + FCurrentHandle := nil; + UpdateBindingList; + + end; + Self.lbBindings_SelectedValueChanged(nil,nil); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.btnNew_Click(sender: System.Object; e: System.EventArgs); +begin + FCurrentHandle := FHandles.Add; + FCurrentHandle.IP := IPv4Wildcard; + FCurrentHandle.Port := FDefaultPort; + UpdateBindingList; + FillComboBox(edtIPAddress,FIPv4Addresses); + UpdateEditControls; +end; + +procedure TIdDsnPropEdBindingNET.UpdateBindingList; +var + i: integer; + selected: integer; + s: string; +begin + selected := lbBindings.SelectedIndex; + lbBindings.BeginUpdate; + try + if lbBindings.Items.Count = FHandles.Count then begin + for i := 0 to FHandles.Count - 1 do begin + s := GetDisplayString(FHandles[i].IP, FHandles[i].Port, FHandles[i].IPVersion); + if s <> lbBindings.Items[i].ToString then begin + lbBindings.Items[i] := s; + end; + end; + end else begin + lbBindings.Items.Clear; + for i := 0 to FHandles.Count-1 do begin + lbBindings.Items.Add(GetDisplayString(FHandles[i].IP, FHandles[i].Port,FHandles[i].IPVersion)); + end; + end; + finally + lbBindings.EndUpdate; + if Assigned(FCurrentHandle) then begin + lbBindings.SelectedIndex := FCurrentHandle.Index; + end else begin + lbBindings.SelectedIndex := Min(selected, lbBindings.Items.Count-1); + end; + end; +{ selected := lbBindings.SelectedItem; + lbBindings.Items.BeginUpdate; + try + if lbBindings.Items.Count = FHandles.Count then begin + for i := 0 to FHandles.Count - 1 do begin + s := GetDisplayString(FHandles[i].IP, FHandles[i].Port, FHandles[i].IPVersion); + if s <> lbBindings.Items[i] then begin + lbBindings.Items[i] := s; + end; + end; + end else begin + lbBindings.Items.Clear; + for i := 0 to FHandles.Count-1 do begin + lbBindings.Items.Add(GetDisplayString(FHandles[i].IP, FHandles[i].Port,FHandles[i].IPVersion)); + end; + end; + finally + lbBindings.Items.EndUpdate; + if Assigned(FCurrentHandle) then begin + lbBindings.ItemIndex := FCurrentHandle.Index; + end else begin + lbBindings.ItemIndex := Min(selected, lbBindings.Items.Count-1); + end; + end; } +end; + +procedure TIdDsnPropEdBindingNET.UpdateEditControls; + +begin + if Assigned(FCurrentHandle) then + begin + edtPort.Text := ''; + edtPort.Value := FCurrentHandle.Port; + case FCurrentHandle.IPVersion of + Id_IPv4 : + begin + + FillComboBox(edtIPAddress,FIPv4Addresses); + edtIPAddress.SelectedItem := edtIPAddress.Items[0]; + cboIPVersion.SelectedItem := cboIPVersion.Items[0]; + end; + Id_IPv6 : + begin + FillComboBox(edtIPAddress,FIPv6Addresses); + edtIPAddress.SelectedItem := edtIPAddress.Items[0]; + cboIPVersion.SelectedItem := cboIPVersion.Items[1]; + end; + end; + edtIPAddress.SelectedIndex := edtIPAddress.Items.IndexOf(FCurrentHandle.IP); + end; + + lblIPAddress.Enabled := Assigned(FCurrentHandle); + edtIPAddress.Enabled := Assigned(FCurrentHandle); + lblPort.Enabled := Assigned(FCurrentHandle); + edtPort.Enabled := Assigned(FCurrentHandle); + lblIPVersion.Enabled := Assigned(FCurrentHandle); + cboIPVersion.Enabled := Assigned(FCurrentHandle); +end; + +procedure TIdDsnPropEdBindingNET.FillComboBox( + ACombo: System.Windows.Forms.ComboBox; AStrings: TIdStrings); +var i : INteger; +begin + ACombo.Items.Clear; + for i := 0 to AStrings.Count -1 do + begin + ACombo.Items.Add(AStrings[i]); + end; +end; + +function TIdDsnPropEdBindingNET.Execute: Boolean; +begin + Result := Self.ShowDialog = System.Windows.Forms.DialogResult.OK; +end; + +end. diff --git a/indy/Design/IdDsnPropEdBindingNET.resx b/indy/Design/IdDsnPropEdBindingNET.resx new file mode 100644 index 0000000..9d6389a --- /dev/null +++ b/indy/Design/IdDsnPropEdBindingNET.resx @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + diff --git a/indy/FCL/IdCompilerDefines.inc b/indy/FCL/IdCompilerDefines.inc new file mode 100644 index 0000000..0071e77 --- /dev/null +++ b/indy/FCL/IdCompilerDefines.inc @@ -0,0 +1,1608 @@ +{$IFDEF CONDITIONALEXPRESSIONS} + // Must be at the top... + {$IF CompilerVersion >= 24.0} + {$LEGACYIFEND ON} + {$IFEND} +{$ENDIF} + +// General + +// Make this $DEFINE to use the 16 color icons required by Borland +// or DEFINE to use the 256 color Indy versions +{.$DEFINE Borland} + +// S.G. 4/9/2002: IPv4/IPv6 general switch (for defaults only) +{$DEFINE IdIPv4} + +{$DEFINE INDY100} +{$DEFINE 10_6_2} //so developers can IFDEF for this specific version + +// When invoking DCC on the command-line, use the -DBCB +// parameter when generating C++Builder output files! +{$IFDEF BCB} + {$DEFINE CBUILDER} +{$ELSE} + {$DEFINE DELPHI} +{$ENDIF} + +{$UNDEF USE_OPENSSL} +{$UNDEF STATICLOAD_OPENSSL} + +{$UNDEF USE_ZLIB_UNIT} +{$UNDEF USE_SSPI} + +// $DEFINE the following if the global objects in the IdStack and IdThread +// units should be freed on finalization +{.$DEFINE FREE_ON_FINAL} +{$UNDEF FREE_ON_FINAL} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. This works in conjunction with the +// FREE_ON_FINAL define above. +{$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + +// FastMM is natively available in BDS 2006 and higher. $DEFINE the +// following if FastMM has been installed manually in earlier versions +{.$DEFINE USE_FASTMM4} +{$UNDEF USE_FASTMM4} + +// $DEFINE the following if MadExcept has been installed manually in +// BDS 2005 or earlier (System.RegisterExpectedMemoryLeak() was introduced +// in BDS 2006) +{.$DEFINE USE_MADEXCEPT} +{$UNDEF USE_MADEXCEPT} + +// Make sure the following are $DEFINE'd only for Delphi/C++Builder 2009 onwards +// as specified further below. The VCL is fully Unicode, where the 'String' +// type maps to System.UnicodeString, not System.AnsiString anymore +{$UNDEF STRING_IS_UNICODE} +{$UNDEF STRING_IS_ANSI} +{$UNDEF STRING_UNICODE_MISMATCH} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. Delphi/C++Builder Mobile/NextGen compilers +// do not support Ansi data types anymore, and is moving away from raw +// pointers as well. +{$DEFINE HAS_AnsiString} +{$DEFINE HAS_AnsiChar} +{$DEFINE HAS_PAnsiChar} +{$UNDEF HAS_PPAnsiChar} +{$UNDEF NO_ANSI_TYPES} +{$UNDEF USE_MARSHALLED_PTRS} +{$UNDEF HAS_MarshaledAString} +{$UNDEF USE_OBJECT_ARC} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF STRING_IS_IMMUTABLE} +{$UNDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF HAS_TEncoding} +{$UNDEF HAS_TEncoding_GetEncoding_ByEncodingName} +{$UNDEF HAS_TCharacter} +{$UNDEF HAS_TInterlocked} +{$UNDEF HAS_TNetEncoding} + +// Make sure that this is defined only for environments where we are using +// the iconv library to charactor conversions. +{.$UNDEF USE_ICONV} + +//Define for Delphi cross-compiler targetting Posix +{$UNDEF USE_VCL_POSIX} +{$UNDEF HAS_ComponentPlatformsAttribute} +{$UNDEF HAS_ComponentPlatformsAttribute_Win32} +{$UNDEF HAS_ComponentPlatformsAttribute_Win64} +{$UNDEF HAS_ComponentPlatformsAttribute_OSX32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Simulator} +{$UNDEF HAS_ComponentPlatformsAttribute_Android} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device32} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux64} +{$UNDEF HAS_ComponentPlatformsAttribute_WinNX32} +{$UNDEF HAS_ComponentPlatformsAttribute_WinIoT32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device64} +{$UNDEF HAS_DIRECTIVE_WARN_DEFAULT} + +// Define for Delphi to auto-generate platform-appropriate '#pragma link' statements in HPP files +{$UNDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + +// detect compiler versions + +// TODO: to detect features in Delphi/C++Builder v6 and later, use CompilerVersion +// and RTLVersion constants instead of VERXXX defines. We still support v5, which +// does not have such constants. + +// Delphi 4 +{$IFDEF VER120} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE DELPHI_4} +{$ENDIF} + +// C++Builder 4 +{$IFDEF VER125} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE CBUILDER_4} +{$ENDIF} + +// Delphi & C++Builder 5 +{$IFDEF VER130} + {$DEFINE DCC} + {$DEFINE VCL_50} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_5} + {$ELSE} + {$DEFINE DELPHI_5} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 6 +{$IFDEF VER140} + {$DEFINE DCC} + {$DEFINE VCL_60} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_6} + {$ELSE} + {$DEFINE DELPHI_6} + {$ENDIF} +{$ENDIF} + +//Delphi 7 +{$IFDEF VER150} + {$DEFINE DCC} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} // there was no C++ Builder 7 +{$ENDIF} + +//Delphi 8 +{$IFDEF VER160} + {$DEFINE DCC} + {$DEFINE VCL_80} + {$DEFINE DELPHI_8} // there was no C++ Builder 8 +{$ENDIF} + +//Delphi 2005 +{$IFDEF VER170} + {$DEFINE DCC} + {$DEFINE VCL_2005} + {$DEFINE DELPHI_2005} // there was no C++Builder 2005 +{$ENDIF} + +// NOTE: CodeGear decided to make Highlander be a non-breaking release +// (no interface changes, thus fully backwards compatible without any +// end user code changes), so VER180 applies to both BDS 2006 and +// Highlander prior to the release of RAD Studio 2007. Use VER185 to +// identify Highlanger specifically. + +//Delphi & C++Builder 2006 +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER180} + {$DEFINE DCC} + {$DEFINE VCL_2006} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2006} + {$ELSE} + {$DEFINE DELPHI_2006} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER185} + {$DEFINE DCC} + {$UNDEF VCL_2006} + {$DEFINE VCL_2007} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_2006} + {$DEFINE CBUILDER_2007} + {$ELSE} + {$UNDEF DELPHI_2006} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +// BDS 2007 NET personality uses VER190 instead of 185. +//Delphi .NET 2007 +{$IFDEF VER190} + {$DEFINE DCC} + {$IFDEF CIL} + //Delphi 2007 + {$DEFINE VCL_2007} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2009 (Tiburon) +{$IFDEF VER200} + {$DEFINE DCC} + {$DEFINE VCL_2009} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2009} + {$ELSE} + {$DEFINE DELPHI_2009} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2010 (Weaver) +{$IFDEF VER210} + {$DEFINE DCC} + {$DEFINE VCL_2010} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2010} + {$ELSE} + {$DEFINE DELPHI_2010} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder XE (Fulcrum) +{$IFDEF VER220} +//REMOVE DCC DEFINE after the next Fulcrum beta. +//It will be defined there. + {$IFNDEF DCC} + {$DEFINE DCC} + {$ENDIF} + {$DEFINE VCL_XE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE} + {$ELSE} + {$DEFINE DELPHI_XE} + {$ENDIF} +{$ENDIF} + +// DCC is now defined by the Delphi compiler starting in XE2 + +//Delphi & CBuilder XE2 (Pulsar) +{$IFDEF VER230} + {$DEFINE VCL_XE2} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE2} + {$ELSE} + {$DEFINE DELPHI_XE2} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE3 (Waterdragon) +//Delphi & CBuilder XE3.5 (Quintessence - early betas only) +{$IFDEF VER240} + {$DEFINE VCL_XE3} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE3} + {$ELSE} + {$DEFINE DELPHI_XE3} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE4 (Quintessence) +{$IFDEF VER250} + {$UNDEF VCL_XE3} + {$DEFINE VCL_XE4} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_XE3} + {$DEFINE CBUILDER_XE4} + {$ELSE} + {$UNDEF DELPHI_XE3} + {$DEFINE DELPHI_XE4} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE5 (Zephyr) +{$IFDEF VER260} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder AppMethod +//AppMethod is just XE5 for mobile only, VCL is removed +{$IFDEF VER265} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE6 (Proteus) +{$IFDEF VER270} + {$DEFINE VCL_XE6} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE6} + {$ELSE} + {$DEFINE DELPHI_XE6} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE7 (Carpathia) +{$IFDEF VER280} + {$DEFINE VCL_XE7} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE7} + {$ELSE} + {$DEFINE DELPHI_XE7} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE8 (Elbrus) +{$IFDEF VER290} + {$DEFINE VCL_XE8} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE8} + {$ELSE} + {$DEFINE DELPHI_XE8} + {$ENDIF} +{$ENDIF} + +// Delphi.NET +// Covers D8+ +{$IFDEF CIL} + // Platform specific conditional. Used for platform specific code. + {$DEFINE DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE STRING_IS_IMMUTABLE} + {.$DEFINE HAS_Int8} + {.$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + {$DEFINE HAS_UInt64} +{$ENDIF} + +// Kylix +// +//Important: Don't use CompilerVersion here as IF's are evaluated before +//IFDEF's and Kylix 1 does not have CompilerVersion defined at all. +{$IFNDEF FPC} + {$IFDEF LINUX} + {$DEFINE UNIX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF (RTLVersion >= 14.0) and (RTLVersion <= 14.5) } + {$DEFINE KYLIX} + {$IF RTLVersion = 14.5} + {$DEFINE KYLIX_3} + {$ELSEIF RTLVersion >= 14.2} + {$DEFINE KYLIX_2} + {$ELSE} + {$DEFINE KYLIX_1} + {$IFEND} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF KYLIX} + {$DEFINE VCL_60} + {$DEFINE INT_THREAD_PRIORITY} + {$DEFINE CPUI386} + {$UNDEF USE_BASEUNIX} + + {$IFDEF KYLIX_3} + {$DEFINE KYLIX_3_OR_ABOVE} + {$ENDIF} + + {$IFDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_2} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_1} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFNDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIXCOMPAT} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE USE_ZLIB_UNIT} + {$ENDIF} +{$ENDIF} + +// FPC (2+) + +{$IFDEF FPC} + // TODO: In FreePascal 4.2.0+, a Delphi-like UnicodeString type is supported. + // However, String/(P)Char do not map to UnicodeString/(P)WideChar unless + // either {$MODE DelphiUnicode} or {$MODESWITCH UnicodeStrings} is used. + // We should consider enabling one of them so Indy uses the same Unicode logic + // in Delphi 2009+ and FreePascal 4.2.0+ and reduces IFDEFs (in particular, + // STRING_UNICODE_MISMATCH, see further below). However, FreePascal's RTL + // is largely not UnicodeString-enabled yet... + {$MODE Delphi} + //note that we may need further defines for widget types depending on + //what we do and what platforms we support in FPC. + //I'll let Marco think about that one. + {$IFDEF UNIX} + {$DEFINE USE_BASEUNIX} + {$IFDEF LINUX} + //In Linux for I386, you can choose between a Kylix-libc API or + //the standard RTL Unix API. Just pass -dKYLIXCOMPAT to the FPC compiler. + //I will see what I can do about the Makefile. + {$IFDEF KYLIXCOMPAT} + {$IFDEF CPUI386} + {$UNDEF USE_BASEUNIX} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF USE_BASEUNIX} + {$UNDEF KYLIXCOMPAT} + {$ENDIF} + {$ENDIF} + + // FPC_FULLVERSION was added in FPC 2.2.4 + // Have to use Defined() or else Delphi compiler chokes, since it + // evaluates $IF statements before $IFDEF statements... + + {$MACRO ON} // must be on in order to use versioning macros + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20701)} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20604)} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20600)} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20404)} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20402)} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20400)} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20204)} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20202)} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20105)} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$IFEND} + + // just in case + {$IFDEF FPC_2_7_1} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + + {$IFDEF FPC_2_7_1_OR_ABOVE} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_4_OR_ABOVE} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_2_OR_ABOVE} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_4_OR_ABOVE} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ELSE} + {$IFDEF VER2_2} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {.$IFDEF FPC_2_7_1_OR_ABOVE} + // support for RawByteString and UnicodeString + {.$DEFINE VCL_2009} + {.$DEFINE DELPHI_2009} + {.$ELSE} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} + {.$ENDIF} +{$ENDIF} + +// end FPC + +{$IFDEF VCL_XE8} + {$DEFINE VCL_XE8_OR_ABOVE} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE VCL_XE7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE7} + {$DEFINE VCL_XE7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE VCL_XE6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE6} + {$DEFINE VCL_XE6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE6_OR_ABOVE} + {$DEFINE VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE5} + {$DEFINE VCL_XE5_OR_ABOVE} + // TODO: figure out how to detect this version + {.$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE4} + {$DEFINE VCL_XE4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE VCL_XE3_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE3} + {$DEFINE VCL_XE3_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE VCL_XE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE2} + {$DEFINE VCL_XE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE VCL_XE_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE} + {$DEFINE VCL_XE_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE VCL_2010_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2010} + {$DEFINE VCL_2010_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE VCL_2009_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2009} + {$DEFINE VCL_2009_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$DEFINE VCL_2007_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2007} + {$DEFINE VCL_2007_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE VCL_2006_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2006} + {$DEFINE VCL_2006_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE VCL_2005_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2005} + {$DEFINE VCL_2005_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2005_OR_ABOVE} + {$DEFINE VCL_8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_80} + {$DEFINE VCL_8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_8_OR_ABOVE} + {$DEFINE VCL_7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_70} + {$DEFINE VCL_7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$DEFINE VCL_6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_60} + {$DEFINE VCL_6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE VCL_5_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_50} + {$DEFINE VCL_5_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$DEFINE VCL_4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_40} + {$DEFINE VCL_4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +// Normalize Delphi compiler defines to match FPC for consistency: +// +// CPU32 - any 32-bit CPU +// CPU64 - any 64-bit CPU +// WINDOWS - any Windows platform (32-bit, 64-bit, CE) +// WIN32 - Windows 32-bit +// WIN64 - Windows 64-bit +// WINCE - Windows CE +// +// Consult the "Free Pascal Programmer's Guide", Appendix G for the complete +// list of defines that are used. Do not work on this unless you understand +// what the FreePascal developers are doing. Not only do you have to +// descriminate with operating systems, but also with chip architectures +// are well. +// +// DCC Pulsar+ define the following values: +// ASSEMBLER +// DCC +// CONDITIONALEXPRESSIONS +// NATIVECODE +// UNICODE +// MACOS +// MACOS32 +// MACOS64 +// MSWINDOWS +// WIN32 +// WIN64 +// LINUX +// POSIX +// POSIX32 +// CPU386 +// CPUX86 +// CPUX64 +// +// Kylix defines the following values: +// LINUX +// (others??) +// + +{$IFNDEF FPC} + // TODO: We need to use ENDIAN_BIG for big endian chip architectures, + // such as 680x0, PowerPC, Sparc, and MIPS, once DCC supports them, + // provided it does not already define its own ENDIAN values by then... + {$DEFINE ENDIAN_LITTLE} + {$IFNDEF VCL_6_OR_ABOVE} + {$DEFINE MSWINDOWS} + {$ENDIF} + {$IFDEF MSWINDOWS} + {$DEFINE WINDOWS} + {$ENDIF} + // TODO: map Pulsar's non-Windows platform defines... + {$IFDEF VCL_XE2_OR_ABOVE} + {$IFDEF CPU386} + //any 32-bit CPU + {$DEFINE CPU32} + //Intel 386 compatible chip architecture + {$DEFINE CPUI386} + {$ENDIF} + {$IFDEF CPUX86} + {$DEFINE CPU32} + {$ENDIF} + {$IFDEF CPUX64} + //any 64-bit CPU + {$DEFINE CPU64} + //AMD64 compatible chip architecture + {$DEFINE CPUX86_64} //historical name for AMD64 + {$DEFINE CPUAMD64} + {$ENDIF} + {$ELSE} + {$IFNDEF DOTNET} + {$IFNDEF KYLIX} + {$DEFINE I386} + {$ENDIF} + {$ENDIF} + {$DEFINE CPU32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + //differences in DotNET Framework versions. + {$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE DOTNET_2} + {$DEFINE DOTNET_2_OR_ABOVE} + {$ELSE} + {$DEFINE DOTNET_1_1} + {$ENDIF} + {$DEFINE DOTNET_1_1_OR_ABOVE} + // Extra include used in D7 for testing. Remove later when all comps are + // ported. Used to selectively exclude non ported parts. Allowed in places + // IFDEFs are otherwise not permitted. + {$DEFINE DOTNET_EXCLUDE} +{$ENDIF} + +// Check for available features + +{$IFDEF CBUILDER} + // When generating a C++ HPP file, if a class has no explicit constructor + // defined and contains compiler-managed members (xxxString, TDateTime, + // Variant, DelphiInterface, etc), the HPP will contain a forwarding + // inline constructor that implicitally initializes those managed members, + // which will overwrite any non-default initializations performed inside + // of InitComponent() overrides! In this situation, the workaround is to + // define an explicit constructor that forwards to the base class constructor + // manually. + {$DEFINE WORKAROUND_INLINE_CONSTRUCTORS} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$IFNDEF FPC} + {$IFNDEF KYLIX} + {$DEFINE HAS_RemoveFreeNotification} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_GetObjectProp} + {$DEFINE HAS_TObjectList} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE HAS_PCardinal} + {$DEFINE HAS_PByte} + {$DEFINE HAS_PWord} + {$DEFINE HAS_PPointer} + {$DEFINE HAS_TList_Assign} + {$DEFINE HAS_sLineBreak} + {$DEFINE HAS_RaiseLastOSError} + {$DEFINE HAS_SysUtils_IncludeExcludeTrailingPathDelimiter} + {$DEFINE HAS_SysUtils_DirectoryExists} + {$DEFINE HAS_UNIT_DateUtils} + {$DEFINE HAS_UNIT_StrUtils} + {$DEFINE HAS_UNIT_Types} + {$DEFINE HAS_TryStrToInt} + {$DEFINE HAS_TryStrToInt64} + {$DEFINE HAS_TryEncodeDate} + {$DEFINE HAS_TryEncodeTime} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFNDEF FPC} + {$DEFINE HAS_IInterface} + {$DEFINE HAS_TSelectionEditor} + {$DEFINE HAS_TStringList_CaseSensitive} + {$IFNDEF KYLIX} + {$DEFINE HAS_DEPRECATED} + {$DEFINE HAS_SYMBOL_PLATFORM} + {$DEFINE HAS_UNIT_PLATFORM} + {$IFNDEF VCL_8_OR_ABOVE} + // Delphi 6 and 7 have an annoying bug that if a class method is declared as + // deprecated, the compiler will emit a "symbol is deprecated" warning + // on the method's implementation! So we will have to wrap implementations + // of deprecated methods with {$WARN SYMBOL_DEPRECATED OFF} directives + // to disable that warning. + {$DEFINE DEPRECATED_IMPL_BUG} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFNDEF DOTNET} + //Widget defines are omitted in .NET + {$DEFINE VCL_60_PLUS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$IFNDEF FPC} + {$DEFINE HAS_UInt64} + {$DEFINE HAS_NAMED_THREADS} + {$DEFINE HAS_TStrings_ValueFromIndex} + {$ENDIF} + {$DEFINE HAS_TFormatSettings} + {$DEFINE HAS_PosEx} + {$IFNDEF VCL_70} + // not implemented in D7 + {$DEFINE HAS_STATIC_TThread_Queue} + {$ENDIF} + {$IFNDEF CIL} + {$IFNDEF VCL_80} + // not implemented in D8 or .NET + {$DEFINE HAS_STATIC_TThread_Synchronize} + {$ENDIF} + {$ENDIF} +{$ELSE} + {$IFDEF CBUILDER_6} + {$DEFINE HAS_NAMED_THREADS} + {$ENDIF} +{$ENDIF} + +{$IFNDEF VCL_2005_OR_ABOVE} + {$IFDEF DCC} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE USE_INLINE} + {$DEFINE HAS_2PARAM_FileAge} + {$DEFINE HAS_System_RegisterExpectedMemoryLeak} + {$IFNDEF FREE_ON_FINAL} + {$IFNDEF DOTNET} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$IFNDEF CBUILDER_2007} + // class properties are broken in C++Builder 2007, causing AVs at compile-time + {$DEFINE HAS_CLASSPROPERTIES} + {$ENDIF} + // Native(U)Int exist but are buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_StrToInt64Def} + {$DEFINE HAS_DWORD_PTR} + {$DEFINE HAS_ULONG_PTR} + {$DEFINE HAS_ULONGLONG} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_CurrentYear} + {$IFNDEF DOTNET} + {$DEFINE HAS_TIMEUNITS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$IFNDEF DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE HAS_UnicodeString} + {$DEFINE HAS_TEncoding} + {$DEFINE HAS_TCharacter} + {$DEFINE HAS_InterlockedCompareExchangePointer} + {$DEFINE HAS_WIDE_TCharArray} + {$DEFINE HAS_UNIT_AnsiStrings} + {$DEFINE HAS_PUInt64} + {$IFDEF VCL_2009} + // TODO: need to differentiate between RTM and Update 1 + // FmtStr() is broken in RTM but was fixed in Update 1 + {$DEFINE BROKEN_FmtStr} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_CLASSVARS} + {$DEFINE HAS_DEPRECATED_MSG} + {$DEFINE HAS_TBytes} + // Native(U)Int are still buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_Int8} + {$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE HAS_CLASSCONSTRUCTOR} + {$DEFINE HAS_CLASSDESTRUCTOR} + {$DEFINE HAS_DELAYLOAD} + {$DEFINE HAS_TThread_NameThreadForDebugging} + {$DEFINE DEPRECATED_TThread_SuspendResume} + // Native(U)Int are finally ok to use now + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$DEFINE HAS_USHORT} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE HAS_TFormatSettings_Object} + {$DEFINE HAS_LocaleCharsFromUnicode} + {$DEFINE HAS_UnicodeFromLocaleChars} + {$DEFINE HAS_PVOID} + {$DEFINE HAS_ULONG64} + {$DEFINE HAS_TEncoding_GetEncoding_ByEncodingName} + {$IFNDEF DOTNET} + {$DEFINE HAS_TInterlocked} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$DEFINE HAS_LONG} + {$DEFINE HAS_ComponentPlatformsAttribute} + {$DEFINE HAS_ComponentPlatformsAttribute_Win32} + {$DEFINE HAS_ComponentPlatformsAttribute_Win64} + {$DEFINE HAS_ComponentPlatformsAttribute_OSX32} + {$DEFINE HAS_DIRECTIVE_WARN_DEFAULT} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$DEFINE HAS_SysUtils_TStringHelper} + {$IFDEF NEXTGEN} + {$DEFINE DCC_NEXTGEN} + {$DEFINE HAS_MarshaledAString} + {$DEFINE USE_MARSHALLED_PTRS} + {$IFDEF AUTOREFCOUNT} + {$DEFINE USE_OBJECT_ARC} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE HAS_AnsiStrings_StrPLCopy} + {$DEFINE HAS_AnsiStrings_StrLen} + {$DEFINE HAS_Character_TCharHelper} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Simulator} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device} + // technically, these are present in XE4, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Android} + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux32} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinNX32} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_Android} +{$ENDIF} + +{$IFDEF VCL_XE5_UPDATE2_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_HPPEMIT_LINKUNIT} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE HAS_TNetEncoding} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device32} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device64} + // technically, these are present in XE8, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux64} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinIoT32} +{$ENDIF} + +// Delphi XE+ cross-compiling +{$IFNDEF FPC} + {$IFDEF POSIX} + {$IF RTLVersion >= 22.0} + {$DEFINE UNIX} + {$UNDEF USE_BASEUNIX} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$IFDEF LINUX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF RTLVersion >= 22.0} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_CROSS_COMPILE} + {$UNDEF KYLIXCOMPAT} +{$ELSE} + {$IFDEF KYLIXCOMPAT} + {$linklib c} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC} + {$DEFINE USE_INLINE} + {$DEFINE USE_CLASSINLINE} + {$DEFINE USE_TBitBtn} //use Bit Buttons instead of Buttons + {$DEFINE FPC_REINTRODUCE_BUG} + {$DEFINE FPC_CIRCULAR_BUG} + {$DEFINE NO_REDECLARE} + {$DEFINE BYTE_COMPARE_SETS} + {$DEFINE HAS_Int8} // TODO: when was Int8 introduced? + {$DEFINE HAS_UInt8} // TODO: when was UInt8 introduced? + {$DEFINE HAS_Int16} // TODO: when was Int16 introduced? + {$DEFINE HAS_UInt16} // TODO: when was UInt16 introduced? + {$DEFINE HAS_Int32} // TODO: when was Int32 introduced? + {$DEFINE HAS_UInt32} // TODO: when was UInt32 introduced? + {$DEFINE HAS_QWord} // TODO: when was QWord introduced? + {$DEFINE HAS_PQWord} // TODO: when was PQWord introduced? + {$IFDEF FPC_2_1_5_OR_ABOVE} + {$DEFINE HAS_UInt64} + {.$DEFINE HAS_PUInt64} // TODO: is this defined? + {$ENDIF} + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE HAS_SharedPrefix} + {$ENDIF} + {$IFDEF FPC_2_2_4_OR_ABOVE} + // size_t and psize_t are only available on Unix systems (FreeBSD, Linux, etc) + {$IFDEF UNIX} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_PtrInt} + {$DEFINE HAS_PtrUInt} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_LPGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFDEF WINDOWS} + {$DEFINE HAS_ULONG_PTR} + {.$DEFINE HAS_ULONGLONG} // TODO: is this defined? + {$ENDIF} + {$DEFINE HAS_UNIT_ctypes} + {$DEFINE HAS_sLineBreak} + {$IFDEF FPC_HAS_UNICODESTRING} + {$DEFINE HAS_UnicodeString} + {$ELSE} + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE HAS_UnicodeString} + {$ENDIF} + {$ENDIF} + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE DEPRECATED_TThread_SuspendResume} + {$DEFINE HAS_DEPRECATED} // TODO: when was deprecated introduced? + {$DEFINE HAS_DEPRECATED_MSG} + {$ENDIF} + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$ENDIF} + {$IFDEF FPC_UNICODESTRINGS} + {$DEFINE STRING_IS_UNICODE} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE WIDGET_WINFORMS} +{$ELSE} + {$DEFINE WIDGET_VCL_LIKE} // LCL included. + {$DEFINE WIDGET_VCL_LIKE_OR_KYLIX} + {$IFDEF FPC} + {$DEFINE WIDGET_LCL} + {$ELSE} + {$IFDEF KYLIX} + {$DEFINE WIDGET_KYLIX} + {$ELSE} + {$DEFINE WIDGET_VCL} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// .NET and Delphi 2009+ support UNICODE strings natively! +// +// FreePascal 2.4.0+ supports UnicodeString, but does not map its +// native String type to UnicodeString except when {$MODE DelphiUnicode} +// or {$MODESWITCH UnicodeStrings} is enabled. However, UNICODE is not +// defined in that mode yet until its RTL has been updated to support +// UnicodeString. STRING_UNICODE_MISMATCH is defined when the native +// String/Char types do not map to the same types that APIs are expecting +// based on whether UNICODE is defined or not. +// +// NOTE: Do not define UNICODE here. The compiler defines +// the symbol automatically. +{$IFDEF STRING_IS_UNICODE} + {$IFNDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ELSE} + {$DEFINE STRING_IS_ANSI} + {$IFDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ENDIF} + +{$IFDEF DCC_NEXTGEN} + {$DEFINE NO_ANSI_TYPES} + {.$DEFINE STRING_IS_IMMUTABLE} // Strings are NOT immutable in NEXTGEN yet + {$IFDEF USE_OBJECT_ARC} + // TODO: move these to an appropriate section. Not doing this yet because + // it is a major interface change to switch to Generics and we should + // maintain backwards compatibility with earlier compilers for the time + // being. Defining them only here for now because the non-Generic versions + // of these classes have become deprecated by ARC and so we need to start + // taking advantage of the Generics versions... + {$DEFINE HAS_UNIT_Generics_Collections} + {$DEFINE HAS_UNIT_Generics_Defaults} + {$DEFINE HAS_GENERICS_TDictionary} + {$DEFINE HAS_GENERICS_TList} + {$DEFINE HAS_GENERICS_TObjectList} + {$DEFINE HAS_GENERICS_TThreadList} + // TArray.Copy() was introduced in XE7 but was buggy. It was fixed in XE8: + // + // RSP-9763 TArray.Copy copies from destination to source for unmanaged types + // https://quality.embarcadero.com/browse/RSP-9763 + // + {$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_GENERICS_TArray_Copy} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF NO_ANSI_TYPES} + {$UNDEF HAS_AnsiString} + {$UNDEF HAS_AnsiChar} + {$UNDEF HAS_PAnsiChar} + {$UNDEF HAS_PPAnsiChar} + {$UNDEF HAS_UNIT_AnsiStrings} + {$UNDEF HAS_AnsiStrings_StrPLCopy} +{$ENDIF} + +{$IFDEF WIN32} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} +{$IFDEF WIN64} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} + +{$IFDEF WIN32_OR_WIN64} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} + {$IFNDEF DCC_NEXTGEN} + {$DEFINE USE_SSPI} + {$IFDEF STRING_IS_UNICODE} + {$DEFINE SSPI_UNICODE} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// High-performance counters are not reliable on multi-core systems, and have +// been known to cause problems with TIdIOHandler.ReadLn() timeouts in Windows +// XP SP3, both 32-bit and 64-bit. Refer to these discussions for more info: +// +// http://www.virtualdub.org/blog/pivot/entry.php?id=106 +// http://blogs.msdn.com/oldnewthing/archive/2008/09/08/8931563.aspx +// +// Do not enable thus unless you know it will work correctly on your systems! +{$IFDEF WINDOWS} + {.$DEFINE USE_HI_PERF_COUNTER_FOR_TICKS} +{$ENDIF} + +{$IFDEF UNIX} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} +{$ENDIF} + +{$IFDEF MACOS} + {$DEFINE HAS_getifaddrs} +{$ENDIF} + +{$IFDEF IOS} + {$DEFINE HAS_getifaddrs} + {$DEFINE USE_OPENSSL} + {$IFDEF CPUARM} + // RLebeau: For iOS devices, OpenSSL cannot be used as an external library, + // it must be statically linked into the app. For the iOS simulator, this + // is not true. Users who want to use OpenSSL in iOS device apps will need + // to add the static OpenSSL library to the project and then include the + // IdSSLOpenSSLHeaders_static unit in their uses clause. It hooks up the + // statically linked functions for the IdSSLOpenSSLHeaders unit to use... + {$DEFINE STATICLOAD_OPENSSL} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT} + {$DEFINE REQUIRES_PROPER_ALIGNMENT} +{$ENDIF} + +// +//iconv defines section. +{$DEFINE USE_ICONV_UNIT} +{$DEFINE USE_ICONV_ENC} +{$IFDEF UNIX} + {$DEFINE USE_ICONV} + {$IFDEF USE_BASEUNIX} + {$IFDEF FPC} + {$UNDEF USE_ICONV_UNIT} + {$ELSE} + {$UNDEF USE_ICONV_ENC} + {$ENDIF} + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + //important!! Iconv functions are defined in the libc.pas Kylix compatible unit. + {$UNDEF USE_ICONV_ENC} + {$UNDEF USE_ICONV_UNIT} + {$ENDIF} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE USE_ICONV} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONV_UNIT + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +{$UNDEF USE_SAFELOADLIBRARY} +{$IFDEF WINDOWS} + {$UNDEF USE_ICONV_ENC} + {$DEFINE USE_SAFELOADLIBRARY} +{$ENDIF} + +{$UNDEF USE_INVALIDATE_MOD_CACHE} +{$UNDEF USE_SAFELOADLIBRARY} +//This must come after the iconv defines because this compiler targets a Unix-like +//operating system. One key difference is that it does have a TEncoding class. +//If this comes before the ICONV defines, it creates problems. +//This also must go before the THandle size calculations. +{$IFDEF VCL_CROSS_COMPILE} + {$IFDEF POSIX} + {$DEFINE BSD} + {$DEFINE USE_SAFELOADLIBRARY} + {$DEFINE USE_INVALIDATE_MOD_CACHE} + {$ENDIF} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONVUNIT + {$UNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} + {$DEFINE INT_THREAD_PRIORITY} +{$ENDIF} + +{$IFNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +//IMPORTANT!!!! +// +//Do not remove this!!! This is to work around a conflict. In DCC, MACOS +//will mean OS X. In FreePascal, the DEFINE MACOS means MacIntosh System OS Classic. +{$IFDEF DCC} + // DCC defines MACOS for both iOS and OS X platforms, need to differentiate + {$IFDEF MACOS} + {$IFNDEF IOS} + {$DEFINE DARWIN} + {$ENDIF} + {$ENDIF} +{$ENDIF} +{$IFDEF FPC} + {$IFDEF MACOS} + {$DEFINE MACOS_CLASSIC} + {$ENDIF} +{$ENDIF} + +{ +BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit +word to an 8 bit byte and an 8 bit byte field named sa_len was added. +} +//Place this only after DARWIN has been defined for Delphi MACOS +{$IFDEF FREEBSD} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF DARWIN} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} + +// Do NOT remove these IFDEF's. They are here because InterlockedExchange +// only handles 32bit values. Some Operating Systems may have 64bit +// THandles. This is not always tied to the platform architecture. + +{$IFDEF AMIGA} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF ATARI} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BEOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BSD} + //I think BSD might handle FreeBSD, NetBSD, OpenBSD, and Darwin + {$IFDEF IOS} + {$IFDEF CPUARM32} + {$DEFINE CPU32} + {$DEFINE THANDLE_32} + {$ELSE} + {$IFDEF CPUARM64} + {$DEFINE CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} + {$ENDIF} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} +{$IFDEF EMBEDDED} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF EMX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GBA} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GO32} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF MACOS_CLASSIC} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NATIVENT} //Native NT for kernel level drivers + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NDS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF PALMOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SYMBIAN} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WII} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WATCOM} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} + +// end platform specific stuff for THandle size + +{$IFDEF THANDLE_CPUBITS} + {$IFDEF CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} +{$IFDEF USE_ICONV} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} + +{$UNDEF STREAM_SIZE_64} +{$IFDEF FPC} + {$DEFINE STREAM_SIZE_64} +{$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE STREAM_SIZE_64} + {$ENDIF} +{$ENDIF} + +{$IFNDEF FREE_ON_FINAL} + {$IFNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF USE_FASTMM4} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF USE_MADEXCEPT} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF DOTNET} + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF VCL_CROSS_COMPILE} + // RLebeau: should this be enabled for Windows, at least? + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} +{$ENDIF} + +{ +We must determine what the SocketType parameter is for the Socket function. +In DotNET, it's SocketType. In Kylix and the libc.pas Kylix-compatibility +library, it's a __socket_type. In BaseUnix, it's a C-type Integer. In Windows, +it's a LongInt. + +} +{$UNDEF SOCKETTYPE_IS_SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_CINT} +{$UNDEF SOCKETTYPE_IS___SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_LONGINT} +{$UNDEF SOCKETTYPE_IS_NUMERIC} +{$UNDEF SOCKET_LEN_IS_socklen_t} +{$IFDEF DOTNET} + {$DEFINE SOCKETTYPE_IS_SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_BASEUNIX} + {$DEFINE SOCKETTYPE_IS_CINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF KYLIXCOMPAT} + {$DEFINE SOCKETTYPE_IS___SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$DEFINE SOCKETTYPE_IS_NUMERIC} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKET_LEN_IS_socklen_t} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} + +{Take advantage of some TCP features specific to some stacks. +They work somewhat similarly but there's a key difference. +In Linux, TCP_CORK is turned on to send fixed packet sizes and +when turned-off (uncorked), any remaining data is sent. With +TCP_NOPUSH, this might not happen and remaining data is only sent +before disconnect. TCP_KEEPIDLE and TCP_KEEPINTVL so the IFDEF LINUX and IFDEF +SOLARIS instead of IFDEF UNIX is not an error, it's deliberate.} +{$UNDEF HAS_TCP_NOPUSH} +{$UNDEF HAS_TCP_CORK} +{$UNDEF HAS_TCP_KEEPIDLE} +{$UNDEF HAS_TCP_KEEPINTVL} +{$UNDEF HAS_SOCKET_NOSIGPIPE} +{$IFDEF BSD} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE HAS_TCP_CORK} +{$ENDIF} +{$IFDEF NETBSD} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$IFNDEF ANDROID} + {$DEFINE HAS_SOCKET_NOSIGPIPE} + {$ENDIF} +{$ENDIF} +{end Unix OS specific stuff} +{$IFDEF DEBUG} + {$UNDEF USE_INLINE} +{$ENDIF} + +// RLebeau 9/5/2013: it would take a lot of work to re-write Indy to support +// both 0-based and 1-based string indexing, so we'll just turn off 0-based +// indexing for now... +{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$ZEROBASEDSTRINGS OFF} +{$ENDIF} \ No newline at end of file diff --git a/indy/FCL/IdVers.inc b/indy/FCL/IdVers.inc new file mode 100644 index 0000000..1510f25 --- /dev/null +++ b/indy/FCL/IdVers.inc @@ -0,0 +1,18 @@ + gsIdVersionMajor = 10; + {$NODEFINE gsIdVersionMajor} + gsIdVersionMinor = 6; + {$NODEFINE gsIdVersionMinor} + gsIdVersionRelease = 2; + {$NODEFINE gsIdVersionRelease} + gsIdVersionBuild = 0; + {$NODEFINE gsIdVersionBuild} + + (*$HPPEMIT '#define gsIdVersionMajor 10'*) + (*$HPPEMIT '#define gsIdVersionMinor 6'*) + (*$HPPEMIT '#define gsIdVersionRelease 2'*) + (*$HPPEMIT '#define gsIdVersionBuild 0'*) + (*$HPPEMIT ''*) + + gsIdVersion = '10.6.2.0'; {do not localize} + gsIdProductName = 'Indy'; {do not localize} + gsIdProductVersion = '10.6.2'; {do not localize} diff --git a/indy/FCL/IdVers.inc.tmpl b/indy/FCL/IdVers.inc.tmpl new file mode 100644 index 0000000..b5d3b41 --- /dev/null +++ b/indy/FCL/IdVers.inc.tmpl @@ -0,0 +1,18 @@ + gsIdVersionMajor = 10; + {$NODEFINE gsIdVersionMajor} + gsIdVersionMinor = 6; + {$NODEFINE gsIdVersionMinor} + gsIdVersionRelease = 2; + {$NODEFINE gsIdVersionRelease} + gsIdVersionBuild = $WCREV$; + {$NODEFINE gsIdVersionBuild} + + (*$HPPEMIT '#define gsIdVersionMajor 10'*) + (*$HPPEMIT '#define gsIdVersionMinor 6'*) + (*$HPPEMIT '#define gsIdVersionRelease 2'*) + (*$HPPEMIT '#define gsIdVersionBuild $WCREV$'*) + (*$HPPEMIT ''*) + + gsIdVersion = '10.6.2.$WCREV$'; {do not localize} + gsIdProductName = 'Indy'; {do not localize} + gsIdProductVersion = '10.6.2'; {do not localize} diff --git a/indy/FCL/Indy.Sockets.bdsproj b/indy/FCL/Indy.Sockets.bdsproj new file mode 100644 index 0000000..e19267c --- /dev/null +++ b/indy/FCL/Indy.Sockets.bdsproj @@ -0,0 +1,469 @@ + + + + + + + + + + + + Indy.Sockets.dpr + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 1 + 1 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + Borland.Vcl;Indy.Sockets + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + + + + w:\source\indy10\lib\fcl\ + + + + c:\windows\microsoft.net\framework\v1.1.4322;w:\source\indy10\lib\fcl + w:\source\indy10\lib\fcl\Mono.Security.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.dll + DotNetDistro + + True + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indy/FCL/Indy.Sockets.dpr b/indy/FCL/Indy.Sockets.dpr new file mode 100644 index 0000000..6115ef9 --- /dev/null +++ b/indy/FCL/Indy.Sockets.dpr @@ -0,0 +1,345 @@ +library Indy.Sockets; + +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.dll'} +{%DelphiDotNetAssemblyCompiler 'Mono.Security.dll'} + + +uses + Indy.Sockets.IdASN1Util in 'Indy.Sockets.IdASN1Util.pas', + Indy.Sockets.IdAllFTPListParsers in 'Indy.Sockets.IdAllFTPListParsers.pas', + Indy.Sockets.IdAntiFreezeBase in 'Indy.Sockets.IdAntiFreezeBase.pas', + Indy.Sockets.IdAssignedNumbers in 'Indy.Sockets.IdAssignedNumbers.pas', + Indy.Sockets.IdAttachment in 'Indy.Sockets.IdAttachment.pas', + Indy.Sockets.IdAttachmentFile in 'Indy.Sockets.IdAttachmentFile.pas', + Indy.Sockets.IdAttachmentMemory in 'Indy.Sockets.IdAttachmentMemory.pas', + Indy.Sockets.IdAuthentication in 'Indy.Sockets.IdAuthentication.pas', + Indy.Sockets.IdAuthenticationDigest in 'Indy.Sockets.IdAuthenticationDigest.pas', + Indy.Sockets.IdAuthenticationManager in 'Indy.Sockets.IdAuthenticationManager.pas', + Indy.Sockets.IdBaseComponent in 'Indy.Sockets.IdBaseComponent.pas', + Indy.Sockets.IdBuffer in 'Indy.Sockets.IdBuffer.pas', + Indy.Sockets.IdCarrierStream in 'Indy.Sockets.IdCarrierStream.pas', + Indy.Sockets.IdChargenServer in 'Indy.Sockets.IdChargenServer.pas', + Indy.Sockets.IdChargenUDPServer in 'Indy.Sockets.IdChargenUDPServer.pas', + Indy.Sockets.IdCharsets in 'Indy.Sockets.IdCharsets.pas', + Indy.Sockets.IdCmdTCPClient in 'Indy.Sockets.IdCmdTCPClient.pas', + Indy.Sockets.IdCmdTCPServer in 'Indy.Sockets.IdCmdTCPServer.pas', + Indy.Sockets.IdCoder in 'Indy.Sockets.IdCoder.pas', + Indy.Sockets.IdCoder00E in 'Indy.Sockets.IdCoder00E.pas', + Indy.Sockets.IdCoder3to4 in 'Indy.Sockets.IdCoder3to4.pas', + Indy.Sockets.IdCoderBinHex4 in 'Indy.Sockets.IdCoderBinHex4.pas', + Indy.Sockets.IdCoderHeader in 'Indy.Sockets.IdCoderHeader.pas', + Indy.Sockets.IdCoderMIME in 'Indy.Sockets.IdCoderMIME.pas', + Indy.Sockets.IdCoderQuotedPrintable in 'Indy.Sockets.IdCoderQuotedPrintable.pas', + Indy.Sockets.IdCoderUUE in 'Indy.Sockets.IdCoderUUE.pas', + Indy.Sockets.IdCoderXXE in 'Indy.Sockets.IdCoderXXE.pas', + Indy.Sockets.IdCommandHandlers in 'Indy.Sockets.IdCommandHandlers.pas', + Indy.Sockets.IdComponent in 'Indy.Sockets.IdComponent.pas', + Indy.Sockets.IdConnectThroughHttpProxy in 'Indy.Sockets.IdConnectThroughHttpProxy.pas', + Indy.Sockets.IdContainers in 'Indy.Sockets.IdContainers.pas', + Indy.Sockets.IdContext in 'Indy.Sockets.IdContext.pas', + Indy.Sockets.IdCookie in 'Indy.Sockets.IdCookie.pas', + Indy.Sockets.IdCookieManager in 'Indy.Sockets.IdCookieManager.pas', + Indy.Sockets.IdCustomHTTPServer in 'Indy.Sockets.IdCustomHTTPServer.pas', + Indy.Sockets.IdCustomTCPServer in 'Indy.Sockets.IdCustomTCPServer.pas', + Indy.Sockets.IdCustomTransparentProxy in 'Indy.Sockets.IdCustomTransparentProxy.pas', + Indy.Sockets.IdDICT in 'Indy.Sockets.IdDICT.pas', + Indy.Sockets.IdDICTCommon in 'Indy.Sockets.IdDICTCommon.pas', + Indy.Sockets.IdDICTServer in 'Indy.Sockets.IdDICTServer.pas', + Indy.Sockets.IdDNSCommon in 'Indy.Sockets.IdDNSCommon.pas', + Indy.Sockets.IdDNSResolver in 'Indy.Sockets.IdDNSResolver.pas', + Indy.Sockets.IdDNSServer in 'Indy.Sockets.IdDNSServer.pas', + Indy.Sockets.IdDateTimeStamp in 'Indy.Sockets.IdDateTimeStamp.pas', + Indy.Sockets.IdDayTime in 'Indy.Sockets.IdDayTime.pas', + Indy.Sockets.IdDayTimeServer in 'Indy.Sockets.IdDayTimeServer.pas', + Indy.Sockets.IdDayTimeUDP in 'Indy.Sockets.IdDayTimeUDP.pas', + Indy.Sockets.IdDayTimeUDPServer in 'Indy.Sockets.IdDayTimeUDPServer.pas', + Indy.Sockets.IdDiscardServer in 'Indy.Sockets.IdDiscardServer.pas', + Indy.Sockets.IdDiscardUDPServer in 'Indy.Sockets.IdDiscardUDPServer.pas', + Indy.Sockets.IdEMailAddress in 'Indy.Sockets.IdEMailAddress.pas', + Indy.Sockets.IdEcho in 'Indy.Sockets.IdEcho.pas', + Indy.Sockets.IdEchoServer in 'Indy.Sockets.IdEchoServer.pas', + Indy.Sockets.IdEchoUDP in 'Indy.Sockets.IdEchoUDP.pas', + Indy.Sockets.IdEchoUDPServer in 'Indy.Sockets.IdEchoUDPServer.pas', + Indy.Sockets.IdException in 'Indy.Sockets.IdException.pas', + Indy.Sockets.IdExceptionCore in 'Indy.Sockets.IdExceptionCore.pas', + Indy.Sockets.IdExplicitTLSClientServerBase in 'Indy.Sockets.IdExplicitTLSClientServerBase.pas', + Indy.Sockets.IdFSP in 'Indy.Sockets.IdFSP.pas', + Indy.Sockets.IdFTP in 'Indy.Sockets.IdFTP.pas', + Indy.Sockets.IdFTPBaseFileSystem in 'Indy.Sockets.IdFTPBaseFileSystem.pas', + Indy.Sockets.IdFTPCommon in 'Indy.Sockets.IdFTPCommon.pas', + Indy.Sockets.IdFTPList in 'Indy.Sockets.IdFTPList.pas', + Indy.Sockets.IdFTPListOutput in 'Indy.Sockets.IdFTPListOutput.pas', + Indy.Sockets.IdFTPListParseAS400 in 'Indy.Sockets.IdFTPListParseAS400.pas', + Indy.Sockets.IdFTPListParseBase in 'Indy.Sockets.IdFTPListParseBase.pas', + Indy.Sockets.IdFTPListParseBullGCOS7 in 'Indy.Sockets.IdFTPListParseBullGCOS7.pas', + Indy.Sockets.IdFTPListParseBullGCOS8 in 'Indy.Sockets.IdFTPListParseBullGCOS8.pas', + Indy.Sockets.IdFTPListParseChameleonNewt in 'Indy.Sockets.IdFTPListParseChameleonNewt.pas', + Indy.Sockets.IdFTPListParseCiscoIOS in 'Indy.Sockets.IdFTPListParseCiscoIOS.pas', + Indy.Sockets.IdFTPListParseDistinctTCPIP in 'Indy.Sockets.IdFTPListParseDistinctTCPIP.pas', + Indy.Sockets.IdFTPListParseEPLF in 'Indy.Sockets.IdFTPListParseEPLF.pas', + Indy.Sockets.IdFTPListParseHellSoft in 'Indy.Sockets.IdFTPListParseHellSoft.pas', + Indy.Sockets.IdFTPListParseKA9Q in 'Indy.Sockets.IdFTPListParseKA9Q.pas', + Indy.Sockets.IdFTPListParseMPEiX in 'Indy.Sockets.IdFTPListParseMPEiX.pas', + Indy.Sockets.IdFTPListParseMVS in 'Indy.Sockets.IdFTPListParseMVS.pas', + Indy.Sockets.IdFTPListParseMicrowareOS9 in 'Indy.Sockets.IdFTPListParseMicrowareOS9.pas', + Indy.Sockets.IdFTPListParseMusic in 'Indy.Sockets.IdFTPListParseMusic.pas', + Indy.Sockets.IdFTPListParseNCSAForDOS in 'Indy.Sockets.IdFTPListParseNCSAForDOS.pas', + Indy.Sockets.IdFTPListParseNCSAForMACOS in 'Indy.Sockets.IdFTPListParseNCSAForMACOS.pas', + Indy.Sockets.IdFTPListParseNovellNetware in 'Indy.Sockets.IdFTPListParseNovellNetware.pas', + Indy.Sockets.IdFTPListParseNovellNetwarePSU in 'Indy.Sockets.IdFTPListParseNovellNetwarePSU.pas', + Indy.Sockets.IdFTPListParseOS2 in 'Indy.Sockets.IdFTPListParseOS2.pas', + Indy.Sockets.IdFTPListParseStercomOS390Exp in 'Indy.Sockets.IdFTPListParseStercomOS390Exp.pas', + Indy.Sockets.IdFTPListParseStercomUnixEnt in 'Indy.Sockets.IdFTPListParseStercomUnixEnt.pas', + Indy.Sockets.IdFTPListParseStratusVOS in 'Indy.Sockets.IdFTPListParseStratusVOS.pas', + Indy.Sockets.IdFTPListParseSuperTCP in 'Indy.Sockets.IdFTPListParseSuperTCP.pas', + Indy.Sockets.IdFTPListParseTOPS20 in 'Indy.Sockets.IdFTPListParseTOPS20.pas', + Indy.Sockets.IdFTPListParseTSXPlus in 'Indy.Sockets.IdFTPListParseTSXPlus.pas', + Indy.Sockets.IdFTPListParseTandemGuardian in 'Indy.Sockets.IdFTPListParseTandemGuardian.pas', + Indy.Sockets.IdFTPListParseUnix in 'Indy.Sockets.IdFTPListParseUnix.pas', + Indy.Sockets.IdFTPListParseVM in 'Indy.Sockets.IdFTPListParseVM.pas', + Indy.Sockets.IdFTPListParseVMS in 'Indy.Sockets.IdFTPListParseVMS.pas', + Indy.Sockets.IdFTPListParseVSE in 'Indy.Sockets.IdFTPListParseVSE.pas', + Indy.Sockets.IdFTPListParseVxWorks in 'Indy.Sockets.IdFTPListParseVxWorks.pas', + Indy.Sockets.IdFTPListParseWfFTP in 'Indy.Sockets.IdFTPListParseWfFTP.pas', + Indy.Sockets.IdFTPListParseWinQVTNET in 'Indy.Sockets.IdFTPListParseWinQVTNET.pas', + Indy.Sockets.IdFTPListParseWindowsNT in 'Indy.Sockets.IdFTPListParseWindowsNT.pas', + Indy.Sockets.IdFTPListParseXecomMicroRTOS in 'Indy.Sockets.IdFTPListParseXecomMicroRTOS.pas', + Indy.Sockets.IdFTPListTypes in 'Indy.Sockets.IdFTPListTypes.pas', + Indy.Sockets.IdFTPServer in 'Indy.Sockets.IdFTPServer.pas', + Indy.Sockets.IdFTPServerContextBase in 'Indy.Sockets.IdFTPServerContextBase.pas', + Indy.Sockets.IdFinger in 'Indy.Sockets.IdFinger.pas', + Indy.Sockets.IdFingerServer in 'Indy.Sockets.IdFingerServer.pas', + Indy.Sockets.IdGlobal in 'Indy.Sockets.IdGlobal.pas', + Indy.Sockets.IdGlobalCore in 'Indy.Sockets.IdGlobalCore.pas', + Indy.Sockets.IdGlobalProtocols in 'Indy.Sockets.IdGlobalProtocols.pas', + Indy.Sockets.IdGopher in 'Indy.Sockets.IdGopher.pas', + Indy.Sockets.IdGopherConsts in 'Indy.Sockets.IdGopherConsts.pas', + Indy.Sockets.IdGopherServer in 'Indy.Sockets.IdGopherServer.pas', + Indy.Sockets.IdHL7 in 'Indy.Sockets.IdHL7.pas', + Indy.Sockets.IdHTTP in 'Indy.Sockets.IdHTTP.pas', + Indy.Sockets.IdHTTPHeaderInfo in 'Indy.Sockets.IdHTTPHeaderInfo.pas', + Indy.Sockets.IdHTTPProxyServer in 'Indy.Sockets.IdHTTPProxyServer.pas', + Indy.Sockets.IdHTTPServer in 'Indy.Sockets.IdHTTPServer.pas', + Indy.Sockets.IdHash in 'Indy.Sockets.IdHash.pas', + Indy.Sockets.IdHashCRC in 'Indy.Sockets.IdHashCRC.pas', + Indy.Sockets.IdHashElf in 'Indy.Sockets.IdHashElf.pas', + Indy.Sockets.IdHashMessageDigest in 'Indy.Sockets.IdHashMessageDigest.pas', + Indy.Sockets.IdHashSHA1 in 'Indy.Sockets.IdHashSHA1.pas', + Indy.Sockets.IdHeaderList in 'Indy.Sockets.IdHeaderList.pas', + Indy.Sockets.IdIMAP4 in 'Indy.Sockets.IdIMAP4.pas', + Indy.Sockets.IdIMAP4Server in 'Indy.Sockets.IdIMAP4Server.pas', + Indy.Sockets.IdIOHandler in 'Indy.Sockets.IdIOHandler.pas', + Indy.Sockets.IdIOHandlerSocket in 'Indy.Sockets.IdIOHandlerSocket.pas', + Indy.Sockets.IdIOHandlerStack in 'Indy.Sockets.IdIOHandlerStack.pas', + Indy.Sockets.IdIOHandlerStream in 'Indy.Sockets.IdIOHandlerStream.pas', + Indy.Sockets.IdIOHandlerTls in 'Indy.Sockets.IdIOHandlerTls.pas', + Indy.Sockets.IdIPAddrMon in 'Indy.Sockets.IdIPAddrMon.pas', + Indy.Sockets.IdIPAddress in 'Indy.Sockets.IdIPAddress.pas', + Indy.Sockets.IdIPMCastBase in 'Indy.Sockets.IdIPMCastBase.pas', + Indy.Sockets.IdIPMCastClient in 'Indy.Sockets.IdIPMCastClient.pas', + Indy.Sockets.IdIPMCastServer in 'Indy.Sockets.IdIPMCastServer.pas', + Indy.Sockets.IdIPWatch in 'Indy.Sockets.IdIPWatch.pas', + Indy.Sockets.IdIRC in 'Indy.Sockets.IdIRC.pas', + Indy.Sockets.IdIcmpClient in 'Indy.Sockets.IdIcmpClient.pas', + Indy.Sockets.IdIdent in 'Indy.Sockets.IdIdent.pas', + Indy.Sockets.IdIdentServer in 'Indy.Sockets.IdIdentServer.pas', + Indy.Sockets.IdIntercept in 'Indy.Sockets.IdIntercept.pas', + Indy.Sockets.IdInterceptSimLog in 'Indy.Sockets.IdInterceptSimLog.pas', + Indy.Sockets.IdInterceptThrottler in 'Indy.Sockets.IdInterceptThrottler.pas', + Indy.Sockets.IdIrcServer in 'Indy.Sockets.IdIrcServer.pas', + Indy.Sockets.IdLPR in 'Indy.Sockets.IdLPR.pas', + Indy.Sockets.IdLogBase in 'Indy.Sockets.IdLogBase.pas', + Indy.Sockets.IdLogDebug in 'Indy.Sockets.IdLogDebug.pas', + Indy.Sockets.IdLogEvent in 'Indy.Sockets.IdLogEvent.pas', + Indy.Sockets.IdLogFile in 'Indy.Sockets.IdLogFile.pas', + Indy.Sockets.IdLogStream in 'Indy.Sockets.IdLogStream.pas', + Indy.Sockets.IdMIMETypes in 'Indy.Sockets.IdMIMETypes.pas', + Indy.Sockets.IdMailBox in 'Indy.Sockets.IdMailBox.pas', + Indy.Sockets.IdMappedFTP in 'Indy.Sockets.IdMappedFTP.pas', + Indy.Sockets.IdMappedPOP3 in 'Indy.Sockets.IdMappedPOP3.pas', + Indy.Sockets.IdMappedPortTCP in 'Indy.Sockets.IdMappedPortTCP.pas', + Indy.Sockets.IdMappedPortUDP in 'Indy.Sockets.IdMappedPortUDP.pas', + Indy.Sockets.IdMappedTelnet in 'Indy.Sockets.IdMappedTelnet.pas', + Indy.Sockets.IdMessage in 'Indy.Sockets.IdMessage.pas', + Indy.Sockets.IdMessageClient in 'Indy.Sockets.IdMessageClient.pas', + Indy.Sockets.IdMessageCoder in 'Indy.Sockets.IdMessageCoder.pas', + Indy.Sockets.IdMessageCoderMIME in 'Indy.Sockets.IdMessageCoderMIME.pas', + Indy.Sockets.IdMessageCoderQuotedPrintable in 'Indy.Sockets.IdMessageCoderQuotedPrintable.pas', + Indy.Sockets.IdMessageCoderUUE in 'Indy.Sockets.IdMessageCoderUUE.pas', + Indy.Sockets.IdMessageCoderXXE in 'Indy.Sockets.IdMessageCoderXXE.pas', + Indy.Sockets.IdMessageCoderYenc in 'Indy.Sockets.IdMessageCoderYenc.pas', + Indy.Sockets.IdMessageCollection in 'Indy.Sockets.IdMessageCollection.pas', + Indy.Sockets.IdMessageParts in 'Indy.Sockets.IdMessageParts.pas', + Indy.Sockets.IdMultipartFormData in 'Indy.Sockets.IdMultipartFormData.pas', + Indy.Sockets.IdNNTP in 'Indy.Sockets.IdNNTP.pas', + Indy.Sockets.IdNNTPServer in 'Indy.Sockets.IdNNTPServer.pas', + Indy.Sockets.IdNetworkCalculator in 'Indy.Sockets.IdNetworkCalculator.pas', + Indy.Sockets.IdOSFileName in 'Indy.Sockets.IdOSFileName.pas', + Indy.Sockets.IdOTPCalculator in 'Indy.Sockets.IdOTPCalculator.pas', + Indy.Sockets.IdObjs in 'Indy.Sockets.IdObjs.pas', + Indy.Sockets.IdObjsBase in 'Indy.Sockets.IdObjsBase.pas', + Indy.Sockets.IdObjsFCL in 'Indy.Sockets.IdObjsFCL.pas', + Indy.Sockets.IdPOP3 in 'Indy.Sockets.IdPOP3.pas', + Indy.Sockets.IdPOP3Server in 'Indy.Sockets.IdPOP3Server.pas', + Indy.Sockets.IdQOTDUDP in 'Indy.Sockets.IdQOTDUDP.pas', + Indy.Sockets.IdQOTDUDPServer in 'Indy.Sockets.IdQOTDUDPServer.pas', + Indy.Sockets.IdQotd in 'Indy.Sockets.IdQotd.pas', + Indy.Sockets.IdQotdServer in 'Indy.Sockets.IdQotdServer.pas', + Indy.Sockets.IdRSH in 'Indy.Sockets.IdRSH.pas', + Indy.Sockets.IdRSHServer in 'Indy.Sockets.IdRSHServer.pas', + Indy.Sockets.IdRawBase in 'Indy.Sockets.IdRawBase.pas', + Indy.Sockets.IdRawClient in 'Indy.Sockets.IdRawClient.pas', + Indy.Sockets.IdRawFunctions in 'Indy.Sockets.IdRawFunctions.pas', + Indy.Sockets.IdRawHeaders in 'Indy.Sockets.IdRawHeaders.pas', + Indy.Sockets.IdRemoteCMDClient in 'Indy.Sockets.IdRemoteCMDClient.pas', + Indy.Sockets.IdRemoteCMDServer in 'Indy.Sockets.IdRemoteCMDServer.pas', + Indy.Sockets.IdReply in 'Indy.Sockets.IdReply.pas', + Indy.Sockets.IdReplyFTP in 'Indy.Sockets.IdReplyFTP.pas', + Indy.Sockets.IdReplyIMAP4 in 'Indy.Sockets.IdReplyIMAP4.pas', + Indy.Sockets.IdReplyPOP3 in 'Indy.Sockets.IdReplyPOP3.pas', + Indy.Sockets.IdReplyRFC in 'Indy.Sockets.IdReplyRFC.pas', + Indy.Sockets.IdReplySMTP in 'Indy.Sockets.IdReplySMTP.pas', + Indy.Sockets.IdResourceStrings in 'Indy.Sockets.IdResourceStrings.pas', + Indy.Sockets.IdResourceStringsCore in 'Indy.Sockets.IdResourceStringsCore.pas', + Indy.Sockets.IdResourceStringsProtocols in 'Indy.Sockets.IdResourceStringsProtocols.pas', + Indy.Sockets.IdRexec in 'Indy.Sockets.IdRexec.pas', + Indy.Sockets.IdRexecServer in 'Indy.Sockets.IdRexecServer.pas', + Indy.Sockets.IdSASL in 'Indy.Sockets.IdSASL.pas', + Indy.Sockets.IdSASLAnonymous in 'Indy.Sockets.IdSASLAnonymous.pas', + Indy.Sockets.IdSASLCollection in 'Indy.Sockets.IdSASLCollection.pas', + Indy.Sockets.IdSASLExternal in 'Indy.Sockets.IdSASLExternal.pas', + Indy.Sockets.IdSASLLogin in 'Indy.Sockets.IdSASLLogin.pas', + Indy.Sockets.IdSASLOTP in 'Indy.Sockets.IdSASLOTP.pas', + Indy.Sockets.IdSASLPlain in 'Indy.Sockets.IdSASLPlain.pas', + Indy.Sockets.IdSASLSKey in 'Indy.Sockets.IdSASLSKey.pas', + Indy.Sockets.IdSASLUserPass in 'Indy.Sockets.IdSASLUserPass.pas', + Indy.Sockets.IdSASL_CRAM_MD5 in 'Indy.Sockets.IdSASL_CRAM_MD5.pas', + Indy.Sockets.IdSMTP in 'Indy.Sockets.IdSMTP.pas', + Indy.Sockets.IdSMTPBase in 'Indy.Sockets.IdSMTPBase.pas', + Indy.Sockets.IdSMTPRelay in 'Indy.Sockets.IdSMTPRelay.pas', + Indy.Sockets.IdSMTPServer in 'Indy.Sockets.IdSMTPServer.pas', + Indy.Sockets.IdSNPP in 'Indy.Sockets.IdSNPP.pas', + Indy.Sockets.IdSSL in 'Indy.Sockets.IdSSL.pas', + Indy.Sockets.IdScheduler in 'Indy.Sockets.IdScheduler.pas', + Indy.Sockets.IdSchedulerOfThread in 'Indy.Sockets.IdSchedulerOfThread.pas', + Indy.Sockets.IdSchedulerOfThreadDefault in 'Indy.Sockets.IdSchedulerOfThreadDefault.pas', + Indy.Sockets.IdSchedulerOfThreadPool in 'Indy.Sockets.IdSchedulerOfThreadPool.pas', + Indy.Sockets.IdServerIOHandler in 'Indy.Sockets.IdServerIOHandler.pas', + Indy.Sockets.IdServerIOHandlerSocket in 'Indy.Sockets.IdServerIOHandlerSocket.pas', + Indy.Sockets.IdServerIOHandlerStack in 'Indy.Sockets.IdServerIOHandlerStack.pas', + Indy.Sockets.IdServerIOHandlerTls in 'Indy.Sockets.IdServerIOHandlerTls.pas', + Indy.Sockets.IdServerInterceptLogBase in 'Indy.Sockets.IdServerInterceptLogBase.pas', + Indy.Sockets.IdServerInterceptLogEvent in 'Indy.Sockets.IdServerInterceptLogEvent.pas', + Indy.Sockets.IdServerInterceptLogFile in 'Indy.Sockets.IdServerInterceptLogFile.pas', + Indy.Sockets.IdSimpleServer in 'Indy.Sockets.IdSimpleServer.pas', + Indy.Sockets.IdSocketHandle in 'Indy.Sockets.IdSocketHandle.pas', + Indy.Sockets.IdSocketStream in 'Indy.Sockets.IdSocketStream.pas', + Indy.Sockets.IdSocks in 'Indy.Sockets.IdSocks.pas', + Indy.Sockets.IdStack in 'Indy.Sockets.IdStack.pas', + Indy.Sockets.IdStackConsts in 'Indy.Sockets.IdStackConsts.pas', + Indy.Sockets.IdStackDotNet in 'Indy.Sockets.IdStackDotNet.pas', + Indy.Sockets.IdStream in 'Indy.Sockets.IdStream.pas', + Indy.Sockets.IdStreamNET in 'Indy.Sockets.IdStreamNET.pas', + Indy.Sockets.IdStrings in 'Indy.Sockets.IdStrings.pas', + Indy.Sockets.IdStruct in 'Indy.Sockets.IdStruct.pas', + Indy.Sockets.IdSync in 'Indy.Sockets.IdSync.pas', + Indy.Sockets.IdSys in 'Indy.Sockets.IdSys.pas', + Indy.Sockets.IdSysBase in 'Indy.Sockets.IdSysBase.pas', + Indy.Sockets.IdSysLog in 'Indy.Sockets.IdSysLog.pas', + Indy.Sockets.IdSysLogMessage in 'Indy.Sockets.IdSysLogMessage.pas', + Indy.Sockets.IdSysLogServer in 'Indy.Sockets.IdSysLogServer.pas', + Indy.Sockets.IdSysNet in 'Indy.Sockets.IdSysNet.pas', + Indy.Sockets.IdSystat in 'Indy.Sockets.IdSystat.pas', + Indy.Sockets.IdSystatServer in 'Indy.Sockets.IdSystatServer.pas', + Indy.Sockets.IdSystatUDP in 'Indy.Sockets.IdSystatUDP.pas', + Indy.Sockets.IdSystatUDPServer in 'Indy.Sockets.IdSystatUDPServer.pas', + Indy.Sockets.IdTCPClient in 'Indy.Sockets.IdTCPClient.pas', + Indy.Sockets.IdTCPConnection in 'Indy.Sockets.IdTCPConnection.pas', + Indy.Sockets.IdTCPServer in 'Indy.Sockets.IdTCPServer.pas', + Indy.Sockets.IdTCPStream in 'Indy.Sockets.IdTCPStream.pas', + Indy.Sockets.IdTask in 'Indy.Sockets.IdTask.pas', + Indy.Sockets.IdTelnet in 'Indy.Sockets.IdTelnet.pas', + Indy.Sockets.IdTelnetServer in 'Indy.Sockets.IdTelnetServer.pas', + Indy.Sockets.IdText in 'Indy.Sockets.IdText.pas', + Indy.Sockets.IdThread in 'Indy.Sockets.IdThread.pas', + Indy.Sockets.IdThreadComponent in 'Indy.Sockets.IdThreadComponent.pas', + Indy.Sockets.IdThreadSafe in 'Indy.Sockets.IdThreadSafe.pas', + Indy.Sockets.IdTime in 'Indy.Sockets.IdTime.pas', + Indy.Sockets.IdTimeServer in 'Indy.Sockets.IdTimeServer.pas', + Indy.Sockets.IdTimeUDP in 'Indy.Sockets.IdTimeUDP.pas', + Indy.Sockets.IdTimeUDPServer in 'Indy.Sockets.IdTimeUDPServer.pas', + Indy.Sockets.IdTlsClientOptions in 'Indy.Sockets.IdTlsClientOptions.pas', + Indy.Sockets.IdTlsServerOptions in 'Indy.Sockets.IdTlsServerOptions.pas', + Indy.Sockets.IdTraceRoute in 'Indy.Sockets.IdTraceRoute.pas', + Indy.Sockets.IdTrivialFTP in 'Indy.Sockets.IdTrivialFTP.pas', + Indy.Sockets.IdTrivialFTPBase in 'Indy.Sockets.IdTrivialFTPBase.pas', + Indy.Sockets.IdTrivialFTPServer in 'Indy.Sockets.IdTrivialFTPServer.pas', + Indy.Sockets.IdUDPBase in 'Indy.Sockets.IdUDPBase.pas', + Indy.Sockets.IdUDPClient in 'Indy.Sockets.IdUDPClient.pas', + Indy.Sockets.IdUDPServer in 'Indy.Sockets.IdUDPServer.pas', + Indy.Sockets.IdURI in 'Indy.Sockets.IdURI.pas', + Indy.Sockets.IdUnixTime in 'Indy.Sockets.IdUnixTime.pas', + Indy.Sockets.IdUnixTimeServer in 'Indy.Sockets.IdUnixTimeServer.pas', + Indy.Sockets.IdUnixTimeUDP in 'Indy.Sockets.IdUnixTimeUDP.pas', + Indy.Sockets.IdUnixTimeUDPServer in 'Indy.Sockets.IdUnixTimeUDPServer.pas', + Indy.Sockets.IdUserAccounts in 'Indy.Sockets.IdUserAccounts.pas', + Indy.Sockets.IdUserPassProvider in 'Indy.Sockets.IdUserPassProvider.pas', + Indy.Sockets.IdVCard in 'Indy.Sockets.IdVCard.pas', + Indy.Sockets.IdWhoIsServer in 'Indy.Sockets.IdWhoIsServer.pas', + Indy.Sockets.IdWhois in 'Indy.Sockets.IdWhois.pas', + Indy.Sockets.IdYarn in 'Indy.Sockets.IdYarn.pas', + Indy.Sockets.IdZLibCompressorBase in 'Indy.Sockets.IdZLibCompressorBase.pas', + Indy.Sockets.IdAssemblyInfo in 'Indy.Sockets.IdAssemblyInfo.pas'; + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')], provided your output +// directory is the project directory (the default). +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// + +// +// Use the attributes below to control the COM visibility of your assembly. By +// default the entire assembly is visible to COM. Setting ComVisible to false +// is the recommended default for your assembly. To then expose a class and interface +// to COM set ComVisible to true on each one. It is also recommended to add a +// Guid attribute. +// + +//[assembly: Guid(')] +//[assembly: TypeLibVersion(1, 0)] + +begin + +end. diff --git a/indy/FCL/Mono.Security.dll b/indy/FCL/Mono.Security.dll new file mode 100644 index 0000000..ffa2ce2 Binary files /dev/null and b/indy/FCL/Mono.Security.dll differ diff --git a/indy/FreeBSDPort/Makefile b/indy/FreeBSDPort/Makefile new file mode 100644 index 0000000..fbe3e00 --- /dev/null +++ b/indy/FreeBSDPort/Makefile @@ -0,0 +1,119 @@ +# New ports collection makefile for: indy-fpc +# Date Created: 2006-08-16 +# Whom: J. Peter Mugaas +# +# $FreeBSD$ +# + +PORTNAME= indy +PORTVERSION= 10.2.0.1 +#DISTVERSIONPREFIX= [] +#DISTVERSION= [] +#DISTVERSIONSUFFIX= [] +#PORTREVISION= [] +#PORTEPOCH= [] +CATEGORIES= devel +MASTER_SITES= http://www.indyproject.org/sockets/fpc/ +#MASTER_SITE_SUBDIR= ${PORTNAME} +#PKGNAMEPREFIX= [] +PKGNAMESUFFIX= -fpc +DISTNAME= ${PORTNAME}-${PORTVERSION} +#EXTRACT_SUFX= [] +#DISTFILES= ${PORTNAME}-${PORTVERSION:S/$/-0/}${EXTRACT_SUFX} \ +# fpc-${FPC_MINVER:S/$/.source/}${EXTRACT_SUFX} +#DIST_SUBDIR= fpc +#EXTRACT_ONLY= [] + +MAINTAINER= oma00215@mail.wvnet.edu +COMMENT= Indy.Sockets (FreePascal Version) + +BUILD_DEPENDS= ppc386:${PORTSDIR}/lang/fpc \ + fpcmake:${PORTSDIR}/lang/fpc-utils + +ONLY_FOR_ARCHS= i386 + +#USE_ICONV= yes +#USE_GETTEXT= yes +#USE_GNOME= gtk12 gdkpixbuf +USE_GMAKE= yes + +MAKE_ENV= OPT="-Fu${UNITSDIR}/* -Fl${LOCALBASE}/lib -Fl${X11BASE}/lib" +#EXTRACT_AFTER_ARGS= | ${TAR} xf - fpc/fcl/image fpc/fcl/inc lazarus +WRKSRC= ${WRKDIR}/${PORTNAME}-${PORTVERSION} +#BUILDNAME= ${ARCH}-freebsd +OSTARNAME!= uname -s | tr [A-Z] [a-z] +BUILDNAME= ${ARCH}-${OSTARNAME} +FPC_MINVER= 2.0.2 +UNITSRELDIR= lib/fpc/${FPC_MINVER}/units/${BUILDNAME} +UNITSDIR= ${LOCALBASE}/${UNITSRELDIR} +UNITTARGET= ${PREFIX}/${UNITSRELDIR} +BASE_UNITS= LIBASYNC NETDB +ALL_UNITS= ${BASE_UNITS} FCL + +# Base units +LIBASYNC_UNIT= net/fpc-libasync +NETDB_UNIT= net/fpc-netdb + +# Extra units +FCL_UNIT= devel/fpc-fcl + +# Plist-Sub for calculating dir locations in the pkg-plist file +PLIST_SUB= UNITTARGET=${UNITTARGET}/${PORTNAME} +.include + +.for OPT in ${ALL_UNITS} +BUILD_DEPENDS+= ${UNITSDIR}/${OPT:L}/Package.fpc:${PORTSDIR}/${${OPT}_UNIT} +.endfor + +#post-extract: +# ${CP} ${WRKDIR}/fpc/fcl/inc/zstream.pp ${WRKSRC}/lcl && \ +# ${CP} ${WRKDIR}/fpc/fcl/image/fpreadpng.pp \ +# ${WRKDIR}/fpc/fcl/image/fpwritepng.pp ${WRKSRC}/lcl + +post-depends: +.if exists(${LOCALBASE}/bin/fpc) +FPC_VER!= ${LOCALBASE}/bin/fpc -i -v | ${SED} -n -e 's/^Free Pascal Compiler version //' -e '1p' +FPC_DIR= ${LOCALBASE}/lib/fpc/${FPC_VER} +FPC_REQVER!= ${ECHO_CMD} $$(expr ${FPC_VER:C/^[^\.]*\.//} \>= ${FPC_MINVER:C/^[^\.]*\.//}) + +.if (${FPC_REQVER} != 1) +IGNORE= requires fpc ${FPC_MINVER} or above. Please upgrade +.endif +.endif + +#post-patch: +# @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|g' -e 's|%%DATADIR%%|${DATADIR}|g' \ +# ${WRKSRC}/${MAKEFILE} +do-build: + @cd ${WRKSRC}/fpc && \ + ${GMAKE} ${MAKE_ENV} + +post-build: + @cd ${WRKSRC}/fpc && \ + ${RM} -Rf COPYING* debian + +do-install: + @cd ${WRKSRC}/fpc && \ + ${GMAKE} ${INSTALL} INSTALL_PREFIX=${PREFIX} +#.for DIRE in components converter debugger designer doceditor docs examples ide ideintf images install \ +# languages lcl packager tools units +# ${MKDIR} ${DATADIR}/${DIRE} +# @cd ${WRKSRC}/${DIRE} && \ +# ${FIND} * -type d -exec ${MKDIR} "${DATADIR}/${DIRE}/{}" \; && \ +# ${FIND} * -type f -exec ${INSTALL_DATA} "{}" "${DATADIR}/${DIRE}/{}" \; +#.endfor + +info: + @${ECHO_CMD} " PORTNAME=${PORTNAME}" + @${ECHO_CMD} "PORTVERSION=${PORTVERSION}" + @${ECHO_CMD} " FPC_VER=${FPC_VER}" + @${ECHO_CMD} " UNITTARGET=${UNITTARGET}" + @${ECHO_CMD} "" + @${ECHO_CMD} "==== commands =====' + @${ECHO_CMD} " CP=${CP}" + @${ECHO_CMD} " ECHO_CMD=${ECHO_CMD}" + @${ECHO_CMD} " MKDIR=${MKDIR}" + @${ECHO_CMD} " RM=${RM}" + @${ECHO_CMD} " UNAME=${UNAME}" + +.include diff --git a/indy/FreeBSDPort/distinfo b/indy/FreeBSDPort/distinfo new file mode 100644 index 0000000..3953ad9 --- /dev/null +++ b/indy/FreeBSDPort/distinfo @@ -0,0 +1,3 @@ +MD5 (indy-10.2.0.1.tar.gz) = 9c81107178cc9850554d36b9b02413c6 +SHA256 (indy-10.2.0.1.tar.gz) = 78d626c3b7004d569680fc9d53b2d6b4a197ed811e42f84ffa366f6c7c1aacbb +SIZE (indy-10.2.0.1.tar.gz) = 1393096 diff --git a/indy/FreeBSDPort/make-plist.sh b/indy/FreeBSDPort/make-plist.sh new file mode 100644 index 0000000..a0dad84 --- /dev/null +++ b/indy/FreeBSDPort/make-plist.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# taken from +#http://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/book.html#PLIST-DYNAMIC +mkdir /var/tmp/$(make -V PORTNAME) +mtree -U -f $(make -V MTREE_FILE) -d -e -p /var/tmp/$(make -V PORTNAME) +make depends PREFIX=/var/tmp/$(make -V PORTNAME) +#Store the directory structure in a new file. +(cd /var/tmp/$(make -V PORTNAME) && find -d * -type d) | sort > OLD-DIRS +touch pkg-plist +#If your port honors PREFIX (which it should) you can then install the +#port and create the package list. +make install PREFIX=/var/tmp/$(make -V PORTNAME) +(cd /var/tmp/$(make -V PORTNAME) && find -d * \! -type d) | sort > pkg-plist +#You must also add any newly created directories to the packing list. +(cd /var/tmp/$(make -V PORTNAME) && find -d * -type d) | sort | comm -13 OLD-DIRS - | +sort -r | sed -e 's#^#@dirrm #' >> pkg-plist +#Clean package +make deinstall PREFIX=/var/tmp/$(make -V PORTNAME) +rm -rf /var/tmp/$(make -V PORTNAME) diff --git a/indy/FreeBSDPort/pkg-descr b/indy/FreeBSDPort/pkg-descr new file mode 100644 index 0000000..1841d6b --- /dev/null +++ b/indy/FreeBSDPort/pkg-descr @@ -0,0 +1,5 @@ +Indy.Sockets is an open source socket library that supports clients, servers, +TCP, UDP, raw sockets, as well as over 100 higher level protocols such as +SMTP, POP3, NNTP, HTTP, and many more. Indy.Sockets is available for C#, C++, +Delphi, Visual Basic.NET, any .NET language, and Kylix. This version is for +FreePascal. diff --git a/indy/FreeBSDPort/pkg-plist b/indy/FreeBSDPort/pkg-plist new file mode 100644 index 0000000..5a7feab --- /dev/null +++ b/indy/FreeBSDPort/pkg-plist @@ -0,0 +1,598 @@ +lib/fpc/2.0.2/units/i386-freebsd/indy/IdASN1Util.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdASN1Util.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAllFTPListParsers.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAllFTPListParsers.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAntiFreezeBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAntiFreezeBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAssignedNumbers.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAssignedNumbers.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachment.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachment.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachmentFile.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachmentFile.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachmentMemory.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAttachmentMemory.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthentication.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthentication.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthenticationDigest.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthenticationDigest.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthenticationManager.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdAuthenticationManager.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdBaseComponent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdBaseComponent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdBuffer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdBuffer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdChargenServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdChargenServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdChargenUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdChargenUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCharsets.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCharsets.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCmdTCPClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCmdTCPClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCmdTCPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCmdTCPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder00E.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder00E.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder3to4.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoder3to4.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderBinHex4.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderBinHex4.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderHeader.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderHeader.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderMIME.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderMIME.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderQuotedPrintable.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderQuotedPrintable.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderUUE.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderUUE.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderXXE.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCoderXXE.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCommandHandlers.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCommandHandlers.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdComponent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdComponent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCompressionIntercept.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCompressionIntercept.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCompressorZLib.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCompressorZLib.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdConnectThroughHttpProxy.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdConnectThroughHttpProxy.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdContainers.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdContainers.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdContext.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdContext.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCookie.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCookie.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCookieManager.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCookieManager.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomHTTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomHTTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomTCPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomTCPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomTransparentProxy.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdCustomTransparentProxy.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICT.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICT.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICTCommon.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICTCommon.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICTServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDICTServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSCommon.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSCommon.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSResolver.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSResolver.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDNSServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDateTimeStamp.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDateTimeStamp.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTime.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTime.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDayTimeUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDiscardServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDiscardServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDiscardUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdDiscardUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEMailAddress.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEMailAddress.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEcho.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEcho.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdEchoUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdException.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdException.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdExceptionCore.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdExceptionCore.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdExplicitTLSClientServerBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdExplicitTLSClientServerBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFSP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFSP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPBaseFileSystem.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPBaseFileSystem.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPCommon.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPCommon.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPList.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPList.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListOutput.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListOutput.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseAS400.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseAS400.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBullGCOS7.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBullGCOS7.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBullGCOS8.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseBullGCOS8.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseChameleonNewt.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseChameleonNewt.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseCiscoIOS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseCiscoIOS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseDistinctTCPIP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseDistinctTCPIP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseEPLF.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseEPLF.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseHellSoft.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseHellSoft.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseKA9Q.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseKA9Q.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMPEiX.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMPEiX.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMVS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMVS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMicrowareOS9.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMicrowareOS9.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMusic.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseMusic.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNCSAForDOS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNCSAForDOS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNCSAForMACOS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNCSAForMACOS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNovellNetware.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNovellNetware.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNovellNetwarePSU.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseNovellNetwarePSU.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseOS2.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseOS2.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParsePCNFSD.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParsePCNFSD.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStercomOS390Exp.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStercomOS390Exp.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStercomUnixEnt.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStercomUnixEnt.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStratusVOS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseStratusVOS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseSuperTCP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseSuperTCP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTOPS20.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTOPS20.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTSXPlus.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTSXPlus.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTandemGuardian.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseTandemGuardian.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseUnisysClearPath.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseUnisysClearPath.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseUnix.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseUnix.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVM.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVM.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVMS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVMS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVSE.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVSE.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVxWorks.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseVxWorks.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWfFTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWfFTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWinQVTNET.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWinQVTNET.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWindowsNT.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseWindowsNT.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseXecomMicroRTOS.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListParseXecomMicroRTOS.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListTypes.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPListTypes.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPServerContextBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFTPServerContextBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFinger.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFinger.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFingerServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdFingerServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobal.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobal.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobalCore.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobalCore.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobalProtocols.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGlobalProtocols.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopher.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopher.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopherConsts.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopherConsts.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopherServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdGopherServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPHeaderInfo.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPHeaderInfo.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPProxyServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPProxyServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHTTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHash.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHash.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashAdler32.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashAdler32.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashCRC.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashCRC.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashElf.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashElf.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashMessageDigest.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashMessageDigest.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashSHA1.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHashSHA1.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHeaderList.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdHeaderList.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIMAP4.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIMAP4.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIMAP4Server.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIMAP4Server.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandler.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandler.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerSocket.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerSocket.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerStack.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerStack.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerStream.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIOHandlerStream.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPAddrMon.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPAddrMon.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPAddress.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPAddress.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPMCastServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPWatch.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIPWatch.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIRC.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIRC.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIcmpClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIcmpClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIdent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIdent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIdentServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIdentServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIntercept.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIntercept.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdInterceptSimLog.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdInterceptSimLog.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdInterceptThrottler.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdInterceptThrottler.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIrcServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdIrcServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLPR.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLPR.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogDebug.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogDebug.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogEvent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogEvent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogFile.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogFile.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogStream.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdLogStream.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMIMETypes.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMIMETypes.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMailBox.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMailBox.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedFTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedFTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPOP3.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPOP3.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPortTCP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPortTCP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPortUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedPortUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedTelnet.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMappedTelnet.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessage.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessage.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoder.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoder.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderMIME.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderMIME.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderQuotedPrintable.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderQuotedPrintable.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderUUE.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderUUE.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderXXE.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderXXE.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderYenc.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCoderYenc.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCollection.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageCollection.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageParts.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMessageParts.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMultipartFormData.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdMultipartFormData.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNNTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNNTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNNTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNNTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNTLM.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNTLM.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNetworkCalculator.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdNetworkCalculator.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdOSFileName.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdOSFileName.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdOTPCalculator.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdOTPCalculator.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjs.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjs.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjsBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjsBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjsVCL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdObjsVCL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdPOP3.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdPOP3.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdPOP3Server.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdPOP3Server.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQOTDUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQOTDUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQOTDUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQOTDUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQotd.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQotd.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQotdServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdQotdServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRSH.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRSH.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRSHServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRSHServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawFunctions.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawFunctions.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawHeaders.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRawHeaders.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRemoteCMDClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRemoteCMDClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRemoteCMDServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRemoteCMDServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReply.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReply.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyFTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyFTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyIMAP4.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyIMAP4.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyPOP3.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyPOP3.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyRFC.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplyRFC.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplySMTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdReplySMTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStrings.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStrings.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStringsCore.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStringsCore.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStringsProtocols.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdResourceStringsProtocols.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRexec.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRexec.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRexecServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdRexecServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLAnonymous.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLAnonymous.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLCollection.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLCollection.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLExternal.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLExternal.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLLogin.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLLogin.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLOTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLOTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLPlain.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLPlain.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLSKey.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLSKey.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLUserPass.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASLUserPass.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASL_CRAM_MD5.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSASL_CRAM_MD5.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPRelay.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPRelay.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSMTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNMP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNMP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNPP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNPP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSNTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSLOpenSSL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSLOpenSSL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSLOpenSSLHeaders.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSSLOpenSSLHeaders.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdScheduler.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdScheduler.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThread.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThread.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThreadDefault.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThreadDefault.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThreadPool.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSchedulerOfThreadPool.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandler.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandler.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandlerSocket.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandlerSocket.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandlerStack.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerIOHandlerStack.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogEvent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogEvent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogFile.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdServerInterceptLogFile.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSimpleServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSimpleServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocketHandle.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocketHandle.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocks.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocks.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocksServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSocksServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStack.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStack.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackBSDBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackBSDBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackConsts.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackConsts.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackUnix.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStackUnix.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStream.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStream.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStreamVCL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStreamVCL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStrings.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStrings.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStruct.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdStruct.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSync.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSync.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSys.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSys.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLinux.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLinux.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLog.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLog.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLogMessage.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLogMessage.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLogServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysLogServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysNativeVCL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysNativeVCL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysVCL.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSysVCL.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystat.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystat.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdSystatUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPConnection.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPConnection.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPStream.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTCPStream.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTask.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTask.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTelnet.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTelnet.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTelnetServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTelnetServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdText.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdText.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThread.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThread.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThreadComponent.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThreadComponent.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThreadSafe.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdThreadSafe.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTime.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTime.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTimeUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTraceRoute.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTraceRoute.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTPBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTPBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdTrivialFTPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPClient.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPClient.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdURI.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdURI.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTime.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTime.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeUDP.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeUDP.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeUDPServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUnixTimeUDPServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUserAccounts.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUserAccounts.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUserPassProvider.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdUserPassProvider.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdVCard.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdVCard.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdWhoIsServer.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdWhoIsServer.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdWhois.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdWhois.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdYarn.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdYarn.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLib.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLib.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLibCompressorBase.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLibCompressorBase.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLibConst.o +lib/fpc/2.0.2/units/i386-freebsd/indy/IdZLibConst.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/Package.fpc +lib/fpc/2.0.2/units/i386-freebsd/indy/indycorefpc.o +lib/fpc/2.0.2/units/i386-freebsd/indy/indycorefpc.ppu +lib/fpc/2.0.2/units/i386-freebsd/indy/indysystemfpc.o +lib/fpc/2.0.2/units/i386-freebsd/indy/indysystemfpc.ppu +share/nls/POSIX +share/nls/en_US.US-ASCII +@dirrm lib/fpc/2.0.2/units/i386-freebsd/indy +@dirrm lib/fpc/2.0.2/units/i386-freebsd +@dirrm lib/fpc/2.0.2/units +@dirrm lib/fpc/2.0.2 +@dirrm lib/fpc diff --git a/indy/Protocols/DelphiZLib.cpp b/indy/Protocols/DelphiZLib.cpp new file mode 100644 index 0000000..ff13611 --- /dev/null +++ b/indy/Protocols/DelphiZLib.cpp @@ -0,0 +1,7 @@ +#include +#pragma hdrstop +//--------------------------------------------------------------------------- +#define Library + +// To add a file to the library use the Project menu 'Add to Project'. + diff --git a/indy/Protocols/DelphiZLib.lib b/indy/Protocols/DelphiZLib.lib new file mode 100644 index 0000000..e1bc364 Binary files /dev/null and b/indy/Protocols/DelphiZLib.lib differ diff --git a/indy/Protocols/IconsDotNet/TIdBlockCipherIntercept.bmp b/indy/Protocols/IconsDotNet/TIdBlockCipherIntercept.bmp new file mode 100644 index 0000000..01c1749 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdBlockCipherIntercept.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdChargenServer.bmp b/indy/Protocols/IconsDotNet/TIdChargenServer.bmp new file mode 100644 index 0000000..f11b6bf Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdChargenServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdChargenUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdChargenUDPServer.bmp new file mode 100644 index 0000000..6f91e9f Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdChargenUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdConnectThroughHttpProxy.bmp b/indy/Protocols/IconsDotNet/TIdConnectThroughHttpProxy.bmp new file mode 100644 index 0000000..ee3c2a6 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdConnectThroughHttpProxy.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdCookieManager.bmp b/indy/Protocols/IconsDotNet/TIdCookieManager.bmp new file mode 100644 index 0000000..ac5c093 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdCookieManager.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDICT.bmp b/indy/Protocols/IconsDotNet/TIdDICT.bmp new file mode 100644 index 0000000..f6405c0 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDICT.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDICTServer.bmp b/indy/Protocols/IconsDotNet/TIdDICTServer.bmp new file mode 100644 index 0000000..5383e83 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDICTServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDISCARDServer.bmp b/indy/Protocols/IconsDotNet/TIdDISCARDServer.bmp new file mode 100644 index 0000000..7692bd1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDISCARDServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDNSResolver.bmp b/indy/Protocols/IconsDotNet/TIdDNSResolver.bmp new file mode 100644 index 0000000..18b15df Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDNSResolver.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDNSServer.bmp b/indy/Protocols/IconsDotNet/TIdDNSServer.bmp new file mode 100644 index 0000000..d5fe1ed Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDNSServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDateTimeStamp.bmp b/indy/Protocols/IconsDotNet/TIdDateTimeStamp.bmp new file mode 100644 index 0000000..af5c0a1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDateTimeStamp.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDayTime.bmp b/indy/Protocols/IconsDotNet/TIdDayTime.bmp new file mode 100644 index 0000000..9907e45 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDayTime.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDayTimeServer.bmp b/indy/Protocols/IconsDotNet/TIdDayTimeServer.bmp new file mode 100644 index 0000000..46bcbd2 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDayTimeServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDayTimeUDP.bmp b/indy/Protocols/IconsDotNet/TIdDayTimeUDP.bmp new file mode 100644 index 0000000..1fefc9a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDayTimeUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDayTimeUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdDayTimeUDPServer.bmp new file mode 100644 index 0000000..5c7f206 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDayTimeUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDecoderBinHex4.bmp b/indy/Protocols/IconsDotNet/TIdDecoderBinHex4.bmp new file mode 100644 index 0000000..74c0340 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDecoderBinHex4.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDecoderMIME.bmp b/indy/Protocols/IconsDotNet/TIdDecoderMIME.bmp new file mode 100644 index 0000000..845734d Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDecoderMIME.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDecoderQuotedPrintable.bmp b/indy/Protocols/IconsDotNet/TIdDecoderQuotedPrintable.bmp new file mode 100644 index 0000000..78a243a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDecoderQuotedPrintable.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDecoderUUE.bmp b/indy/Protocols/IconsDotNet/TIdDecoderUUE.bmp new file mode 100644 index 0000000..6489e10 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDecoderUUE.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDecoderXXE.bmp b/indy/Protocols/IconsDotNet/TIdDecoderXXE.bmp new file mode 100644 index 0000000..26ddf8a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDecoderXXE.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdDiscardUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdDiscardUDPServer.bmp new file mode 100644 index 0000000..1f822a3 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdDiscardUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdECHOServer.bmp b/indy/Protocols/IconsDotNet/TIdECHOServer.bmp new file mode 100644 index 0000000..3aecbd1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdECHOServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEcho.bmp b/indy/Protocols/IconsDotNet/TIdEcho.bmp new file mode 100644 index 0000000..8747da6 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEcho.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEchoUDP.bmp b/indy/Protocols/IconsDotNet/TIdEchoUDP.bmp new file mode 100644 index 0000000..da9a375 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEchoUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEchoUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdEchoUDPServer.bmp new file mode 100644 index 0000000..ffa810e Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEchoUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEncoderBinHex4.bmp b/indy/Protocols/IconsDotNet/TIdEncoderBinHex4.bmp new file mode 100644 index 0000000..77f73da Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEncoderBinHex4.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEncoderMIME.bmp b/indy/Protocols/IconsDotNet/TIdEncoderMIME.bmp new file mode 100644 index 0000000..850a9d4 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEncoderMIME.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEncoderQuotedPrintable.bmp b/indy/Protocols/IconsDotNet/TIdEncoderQuotedPrintable.bmp new file mode 100644 index 0000000..91edeb2 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEncoderQuotedPrintable.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEncoderUUE.bmp b/indy/Protocols/IconsDotNet/TIdEncoderUUE.bmp new file mode 100644 index 0000000..6ec7db2 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEncoderUUE.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdEncoderXXE.bmp b/indy/Protocols/IconsDotNet/TIdEncoderXXE.bmp new file mode 100644 index 0000000..6518c83 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdEncoderXXE.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdFSP.bmp b/indy/Protocols/IconsDotNet/TIdFSP.bmp new file mode 100644 index 0000000..62ce2f1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdFSP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdFTP.bmp b/indy/Protocols/IconsDotNet/TIdFTP.bmp new file mode 100644 index 0000000..56bd415 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdFTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdFTPServer.bmp b/indy/Protocols/IconsDotNet/TIdFTPServer.bmp new file mode 100644 index 0000000..e4af1ee Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdFTPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdFinger.bmp b/indy/Protocols/IconsDotNet/TIdFinger.bmp new file mode 100644 index 0000000..0645c5c Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdFinger.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdFingerServer.bmp b/indy/Protocols/IconsDotNet/TIdFingerServer.bmp new file mode 100644 index 0000000..3478400 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdFingerServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdGopher.bmp b/indy/Protocols/IconsDotNet/TIdGopher.bmp new file mode 100644 index 0000000..f1c9cd3 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdGopher.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdGopherServer.bmp b/indy/Protocols/IconsDotNet/TIdGopherServer.bmp new file mode 100644 index 0000000..5426c19 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdGopherServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdHTTP.bmp b/indy/Protocols/IconsDotNet/TIdHTTP.bmp new file mode 100644 index 0000000..1f0c59d Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdHTTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdHTTPProxyServer.bmp b/indy/Protocols/IconsDotNet/TIdHTTPProxyServer.bmp new file mode 100644 index 0000000..eb077a8 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdHTTPProxyServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdHTTPServer.bmp b/indy/Protocols/IconsDotNet/TIdHTTPServer.bmp new file mode 100644 index 0000000..a86a063 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdHTTPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdHostnameServer.bmp b/indy/Protocols/IconsDotNet/TIdHostnameServer.bmp new file mode 100644 index 0000000..bf15b80 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdHostnameServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIMAP4.bmp b/indy/Protocols/IconsDotNet/TIdIMAP4.bmp new file mode 100644 index 0000000..4a9042a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIMAP4.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIMAP4Server.bmp b/indy/Protocols/IconsDotNet/TIdIMAP4Server.bmp new file mode 100644 index 0000000..e970d59 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIMAP4Server.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIOHandlerChain.bmp b/indy/Protocols/IconsDotNet/TIdIOHandlerChain.bmp new file mode 100644 index 0000000..de1aca7 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIOHandlerChain.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIOHandlerSocket.bmp b/indy/Protocols/IconsDotNet/TIdIOHandlerSocket.bmp new file mode 100644 index 0000000..3dcd690 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIOHandlerSocket.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIOHandlerThrottle.bmp b/indy/Protocols/IconsDotNet/TIdIOHandlerThrottle.bmp new file mode 100644 index 0000000..09abe7d Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIOHandlerThrottle.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIPAddrMon.bmp b/indy/Protocols/IconsDotNet/TIdIPAddrMon.bmp new file mode 100644 index 0000000..0912edd Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIPAddrMon.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIPWatch.bmp b/indy/Protocols/IconsDotNet/TIdIPWatch.bmp new file mode 100644 index 0000000..c5305e6 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIPWatch.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIRC.bmp b/indy/Protocols/IconsDotNet/TIdIRC.bmp new file mode 100644 index 0000000..7a4b3f3 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIRC.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIRCServer.bmp b/indy/Protocols/IconsDotNet/TIdIRCServer.bmp new file mode 100644 index 0000000..13169ac Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIRCServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIdent.bmp b/indy/Protocols/IconsDotNet/TIdIdent.bmp new file mode 100644 index 0000000..6f5e8c3 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIdent.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdIdentServer.bmp b/indy/Protocols/IconsDotNet/TIdIdentServer.bmp new file mode 100644 index 0000000..259ea91 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdIdentServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdLPR.bmp b/indy/Protocols/IconsDotNet/TIdLPR.bmp new file mode 100644 index 0000000..3ecaafa Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdLPR.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMBCSDecoder.bmp b/indy/Protocols/IconsDotNet/TIdMBCSDecoder.bmp new file mode 100644 index 0000000..c999099 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMBCSDecoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMailBox.bmp b/indy/Protocols/IconsDotNet/TIdMailBox.bmp new file mode 100644 index 0000000..3b7eb02 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMailBox.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMappedFTP.bmp b/indy/Protocols/IconsDotNet/TIdMappedFTP.bmp new file mode 100644 index 0000000..8c96e17 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMappedFTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMappedPOP3.bmp b/indy/Protocols/IconsDotNet/TIdMappedPOP3.bmp new file mode 100644 index 0000000..ab8c5fe Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMappedPOP3.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMappedPortTCP.bmp b/indy/Protocols/IconsDotNet/TIdMappedPortTCP.bmp new file mode 100644 index 0000000..33b77d8 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMappedPortTCP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMappedPortUDP.bmp b/indy/Protocols/IconsDotNet/TIdMappedPortUDP.bmp new file mode 100644 index 0000000..4a9320c Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMappedPortUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMappedTelnet.bmp b/indy/Protocols/IconsDotNet/TIdMappedTelnet.bmp new file mode 100644 index 0000000..771ed3e Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMappedTelnet.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessage.bmp b/indy/Protocols/IconsDotNet/TIdMessage.bmp new file mode 100644 index 0000000..7289e35 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessage.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageDecoder.bmp b/indy/Protocols/IconsDotNet/TIdMessageDecoder.bmp new file mode 100644 index 0000000..0f0849c Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageDecoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageDecoderMIME.bmp b/indy/Protocols/IconsDotNet/TIdMessageDecoderMIME.bmp new file mode 100644 index 0000000..6613c14 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageDecoderMIME.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageDecoderYENC.bmp b/indy/Protocols/IconsDotNet/TIdMessageDecoderYENC.bmp new file mode 100644 index 0000000..121fead Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageDecoderYENC.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageEncoder.bmp b/indy/Protocols/IconsDotNet/TIdMessageEncoder.bmp new file mode 100644 index 0000000..115bd81 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageEncoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageEncoderMIME.bmp b/indy/Protocols/IconsDotNet/TIdMessageEncoderMIME.bmp new file mode 100644 index 0000000..102b3f5 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageEncoderMIME.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdMessageEncoderYENC.bmp b/indy/Protocols/IconsDotNet/TIdMessageEncoderYENC.bmp new file mode 100644 index 0000000..458dd2f Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdMessageEncoderYENC.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdNNTP.bmp b/indy/Protocols/IconsDotNet/TIdNNTP.bmp new file mode 100644 index 0000000..d281358 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdNNTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdNNTPServer.bmp b/indy/Protocols/IconsDotNet/TIdNNTPServer.bmp new file mode 100644 index 0000000..8a92e14 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdNNTPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdNetworkCalculator.bmp b/indy/Protocols/IconsDotNet/TIdNetworkCalculator.bmp new file mode 100644 index 0000000..f9f9bba Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdNetworkCalculator.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdPOP3.bmp b/indy/Protocols/IconsDotNet/TIdPOP3.bmp new file mode 100644 index 0000000..aba0bf0 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdPOP3.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdPOP3Server.bmp b/indy/Protocols/IconsDotNet/TIdPOP3Server.bmp new file mode 100644 index 0000000..08f1487 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdPOP3Server.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdQOTD.bmp b/indy/Protocols/IconsDotNet/TIdQOTD.bmp new file mode 100644 index 0000000..ee16e41 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdQOTD.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdQOTDServer.bmp b/indy/Protocols/IconsDotNet/TIdQOTDServer.bmp new file mode 100644 index 0000000..cbbbe36 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdQOTDServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdQOTDUDP.bmp b/indy/Protocols/IconsDotNet/TIdQOTDUDP.bmp new file mode 100644 index 0000000..b207662 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdQOTDUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdQotdUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdQotdUDPServer.bmp new file mode 100644 index 0000000..70637d5 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdQotdUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdRSH.bmp b/indy/Protocols/IconsDotNet/TIdRSH.bmp new file mode 100644 index 0000000..819be9b Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdRSH.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdRSHServer.bmp b/indy/Protocols/IconsDotNet/TIdRSHServer.bmp new file mode 100644 index 0000000..5d3640c Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdRSHServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdRawClient.bmp b/indy/Protocols/IconsDotNet/TIdRawClient.bmp new file mode 100644 index 0000000..0a1c5dd Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdRawClient.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdRexec.bmp b/indy/Protocols/IconsDotNet/TIdRexec.bmp new file mode 100644 index 0000000..e15f41e Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdRexec.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdRexecServer.bmp b/indy/Protocols/IconsDotNet/TIdRexecServer.bmp new file mode 100644 index 0000000..783a370 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdRexecServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASL.bmp b/indy/Protocols/IconsDotNet/TIdSASL.bmp new file mode 100644 index 0000000..1b85028 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASL.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLAnonymous.bmp b/indy/Protocols/IconsDotNet/TIdSASLAnonymous.bmp new file mode 100644 index 0000000..2c40e53 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLAnonymous.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLCRAMMD5.bmp b/indy/Protocols/IconsDotNet/TIdSASLCRAMMD5.bmp new file mode 100644 index 0000000..08818f1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLCRAMMD5.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLDigest.bmp b/indy/Protocols/IconsDotNet/TIdSASLDigest.bmp new file mode 100644 index 0000000..657a0dd Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLDigest.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLExternal.bmp b/indy/Protocols/IconsDotNet/TIdSASLExternal.bmp new file mode 100644 index 0000000..d7845eb Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLExternal.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLList.bmp b/indy/Protocols/IconsDotNet/TIdSASLList.bmp new file mode 100644 index 0000000..7783323 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLList.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLLogin.bmp b/indy/Protocols/IconsDotNet/TIdSASLLogin.bmp new file mode 100644 index 0000000..103f007 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLLogin.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLOTP.bmp b/indy/Protocols/IconsDotNet/TIdSASLOTP.bmp new file mode 100644 index 0000000..f28b3cf Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLOTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLPlain.bmp b/indy/Protocols/IconsDotNet/TIdSASLPlain.bmp new file mode 100644 index 0000000..a310c6a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLPlain.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSASLSKey.bmp b/indy/Protocols/IconsDotNet/TIdSASLSKey.bmp new file mode 100644 index 0000000..dfc7093 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSASLSKey.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSMTP.bmp b/indy/Protocols/IconsDotNet/TIdSMTP.bmp new file mode 100644 index 0000000..bcc08cf Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSMTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSMTPRelay.bmp b/indy/Protocols/IconsDotNet/TIdSMTPRelay.bmp new file mode 100644 index 0000000..c52d545 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSMTPRelay.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSMTPServer.bmp b/indy/Protocols/IconsDotNet/TIdSMTPServer.bmp new file mode 100644 index 0000000..76cce65 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSMTPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSNPP.bmp b/indy/Protocols/IconsDotNet/TIdSNPP.bmp new file mode 100644 index 0000000..02f9536 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSNPP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSNTP.bmp b/indy/Protocols/IconsDotNet/TIdSNTP.bmp new file mode 100644 index 0000000..afd7e2f Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSNTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSOCKSServer.bmp b/indy/Protocols/IconsDotNet/TIdSOCKSServer.bmp new file mode 100644 index 0000000..a14b028 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSOCKSServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocket.bmp b/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocket.bmp new file mode 100644 index 0000000..1172e13 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocket.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocketNET.bmp b/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocketNET.bmp new file mode 100644 index 0000000..179575a Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSSLIOHandlerSocketNET.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerCompressionIntercept.bmp b/indy/Protocols/IconsDotNet/TIdServerCompressionIntercept.bmp new file mode 100644 index 0000000..cd40b9f Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerCompressionIntercept.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerIOHandlerSSLNET.bmp b/indy/Protocols/IconsDotNet/TIdServerIOHandlerSSLNET.bmp new file mode 100644 index 0000000..8772492 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerIOHandlerSSLNET.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerIOHandlerSocket.bmp b/indy/Protocols/IconsDotNet/TIdServerIOHandlerSocket.bmp new file mode 100644 index 0000000..d7c5c5e Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerIOHandlerSocket.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerIOHandlerStream.bmp b/indy/Protocols/IconsDotNet/TIdServerIOHandlerStream.bmp new file mode 100644 index 0000000..e59af44 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerIOHandlerStream.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerInterceptLogEvent.bmp b/indy/Protocols/IconsDotNet/TIdServerInterceptLogEvent.bmp new file mode 100644 index 0000000..6c5f1d6 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerInterceptLogEvent.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdServerInterceptLogFile.bmp b/indy/Protocols/IconsDotNet/TIdServerInterceptLogFile.bmp new file mode 100644 index 0000000..e2657e2 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdServerInterceptLogFile.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSysLog.bmp b/indy/Protocols/IconsDotNet/TIdSysLog.bmp new file mode 100644 index 0000000..cc25136 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSysLog.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSysLogMessage.bmp b/indy/Protocols/IconsDotNet/TIdSysLogMessage.bmp new file mode 100644 index 0000000..c6689fe Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSysLogMessage.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSyslogServer.bmp b/indy/Protocols/IconsDotNet/TIdSyslogServer.bmp new file mode 100644 index 0000000..4f6fedf Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSyslogServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSystat.bmp b/indy/Protocols/IconsDotNet/TIdSystat.bmp new file mode 100644 index 0000000..0e286b9 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSystat.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSystatServer.bmp b/indy/Protocols/IconsDotNet/TIdSystatServer.bmp new file mode 100644 index 0000000..ebeae65 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSystatServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSystatUDP.bmp b/indy/Protocols/IconsDotNet/TIdSystatUDP.bmp new file mode 100644 index 0000000..8d3645b Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSystatUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdSystatUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdSystatUDPServer.bmp new file mode 100644 index 0000000..f36854b Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdSystatUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTelnet.bmp b/indy/Protocols/IconsDotNet/TIdTelnet.bmp new file mode 100644 index 0000000..444b108 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTelnet.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTelnetServer.bmp b/indy/Protocols/IconsDotNet/TIdTelnetServer.bmp new file mode 100644 index 0000000..d9f49d8 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTelnetServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTime.bmp b/indy/Protocols/IconsDotNet/TIdTime.bmp new file mode 100644 index 0000000..3775658 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTime.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTimeServer.bmp b/indy/Protocols/IconsDotNet/TIdTimeServer.bmp new file mode 100644 index 0000000..6ba3d17 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTimeServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTimeUDP.bmp b/indy/Protocols/IconsDotNet/TIdTimeUDP.bmp new file mode 100644 index 0000000..94913ce Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTimeUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTimeUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdTimeUDPServer.bmp new file mode 100644 index 0000000..7dc283c Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTimeUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTrivialFTP.bmp b/indy/Protocols/IconsDotNet/TIdTrivialFTP.bmp new file mode 100644 index 0000000..583a2fd Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTrivialFTP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTrivialFTPServer.bmp b/indy/Protocols/IconsDotNet/TIdTrivialFTPServer.bmp new file mode 100644 index 0000000..498031b Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTrivialFTPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTunnelMaster.bmp b/indy/Protocols/IconsDotNet/TIdTunnelMaster.bmp new file mode 100644 index 0000000..843d116 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTunnelMaster.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdTunnelSlave.bmp b/indy/Protocols/IconsDotNet/TIdTunnelSlave.bmp new file mode 100644 index 0000000..ed25319 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdTunnelSlave.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUnixTime.bmp b/indy/Protocols/IconsDotNet/TIdUnixTime.bmp new file mode 100644 index 0000000..36cd1a1 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUnixTime.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUnixTimeServer.bmp b/indy/Protocols/IconsDotNet/TIdUnixTimeServer.bmp new file mode 100644 index 0000000..b265adc Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUnixTimeServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUnixTimeUDP.bmp b/indy/Protocols/IconsDotNet/TIdUnixTimeUDP.bmp new file mode 100644 index 0000000..888aced Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUnixTimeUDP.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUnixTimeUDPServer.bmp b/indy/Protocols/IconsDotNet/TIdUnixTimeUDPServer.bmp new file mode 100644 index 0000000..65b5008 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUnixTimeUDPServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUserAccounts.bmp b/indy/Protocols/IconsDotNet/TIdUserAccounts.bmp new file mode 100644 index 0000000..3c2a2c5 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUserAccounts.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUserManager.bmp b/indy/Protocols/IconsDotNet/TIdUserManager.bmp new file mode 100644 index 0000000..3c2a2c5 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUserManager.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdUserPassProvider.bmp b/indy/Protocols/IconsDotNet/TIdUserPassProvider.bmp new file mode 100644 index 0000000..c95b2fe Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdUserPassProvider.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdVCard.bmp b/indy/Protocols/IconsDotNet/TIdVCard.bmp new file mode 100644 index 0000000..7db4908 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdVCard.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdWebDAV.bmp b/indy/Protocols/IconsDotNet/TIdWebDAV.bmp new file mode 100644 index 0000000..a695752 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdWebDAV.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdWhoIsServer.bmp b/indy/Protocols/IconsDotNet/TIdWhoIsServer.bmp new file mode 100644 index 0000000..fe14d3b Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdWhoIsServer.bmp differ diff --git a/indy/Protocols/IconsDotNet/TIdWhois.bmp b/indy/Protocols/IconsDotNet/TIdWhois.bmp new file mode 100644 index 0000000..57ab196 Binary files /dev/null and b/indy/Protocols/IconsDotNet/TIdWhois.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIDIMFEncoder.bmp b/indy/Protocols/IconsDotNet/Unref/TIDIMFEncoder.bmp new file mode 100644 index 0000000..c2c2ed3 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIDIMFEncoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIDMBCSEncoder.bmp b/indy/Protocols/IconsDotNet/Unref/TIDMBCSEncoder.bmp new file mode 100644 index 0000000..e4a8848 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIDMBCSEncoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCoderMD2.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD2.bmp new file mode 100644 index 0000000..f523fab Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD2.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCoderMD4.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD4.bmp new file mode 100644 index 0000000..a840320 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD4.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCoderMD5.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD5.bmp new file mode 100644 index 0000000..c4df620 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCoderMD5.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCompressionIntercept.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCompressionIntercept.bmp new file mode 100644 index 0000000..ecd387e Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCompressionIntercept.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCompressorBorZLib.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCompressorBorZLib.bmp new file mode 100644 index 0000000..66ec608 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCompressorBorZLib.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdCompressorZLibEx.bmp b/indy/Protocols/IconsDotNet/Unref/TIdCompressorZLibEx.bmp new file mode 100644 index 0000000..8ba4cff Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdCompressorZLibEx.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdConnectionInterceptOpenSSL.bmp b/indy/Protocols/IconsDotNet/Unref/TIdConnectionInterceptOpenSSL.bmp new file mode 100644 index 0000000..bd5e440 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdConnectionInterceptOpenSSL.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdFTPFileSystem.bmp b/indy/Protocols/IconsDotNet/Unref/TIdFTPFileSystem.bmp new file mode 100644 index 0000000..59316eb Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdFTPFileSystem.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdHL7.bmp b/indy/Protocols/IconsDotNet/Unref/TIdHL7.bmp new file mode 100644 index 0000000..4c3e629 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdHL7.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdIMFDecoder.bmp b/indy/Protocols/IconsDotNet/Unref/TIdIMFDecoder.bmp new file mode 100644 index 0000000..d95c015 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdIMFDecoder.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdRLECompress.bmp b/indy/Protocols/IconsDotNet/Unref/TIdRLECompress.bmp new file mode 100644 index 0000000..ddeaf0c Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdRLECompress.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdRLEDecompress.bmp b/indy/Protocols/IconsDotNet/Unref/TIdRLEDecompress.bmp new file mode 100644 index 0000000..77ff1bb Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdRLEDecompress.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdSNMP.bmp b/indy/Protocols/IconsDotNet/Unref/TIdSNMP.bmp new file mode 100644 index 0000000..bbcc9ab Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdSNMP.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdSSLIOHandlerSocketOpenSSL.bmp b/indy/Protocols/IconsDotNet/Unref/TIdSSLIOHandlerSocketOpenSSL.bmp new file mode 100644 index 0000000..1172e13 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdSSLIOHandlerSocketOpenSSL.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerOpenSSL.bmp b/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerOpenSSL.bmp new file mode 100644 index 0000000..23438cf Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerOpenSSL.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerSSL.bmp b/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerSSL.bmp new file mode 100644 index 0000000..23438cf Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdServerIOHandlerSSL.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdServerInterceptOpenSSL.bmp b/indy/Protocols/IconsDotNet/Unref/TIdServerInterceptOpenSSL.bmp new file mode 100644 index 0000000..922a77b Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdServerInterceptOpenSSL.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrDefault.bmp b/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrDefault.bmp new file mode 100644 index 0000000..49599f4 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrDefault.bmp differ diff --git a/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrPool.bmp b/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrPool.bmp new file mode 100644 index 0000000..e163c16 Binary files /dev/null and b/indy/Protocols/IconsDotNet/Unref/TIdThreadMgrPool.bmp differ diff --git a/indy/Protocols/IdASN1Coder.pas b/indy/Protocols/IdASN1Coder.pas new file mode 100644 index 0000000..e4a1011 --- /dev/null +++ b/indy/Protocols/IdASN1Coder.pas @@ -0,0 +1,610 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 15/04/2005 7:25:02 AM GGrieve + first ported to INdy +} + +unit IdASN1Coder; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + Contnrs; + +type + + TIdASN1IdentifierType = (aitUnknown, aitSequence, aitBoolean, aitInteger, aitEnum, aitString, aitOID, aitReal); + + TIdASN1IdentifierClass = (aicUniversal, aicApplication, aicContextSpecific, aicPrivate); + + TIdASN1Identifier = record + Position : Integer; + IdClass : TIdASN1IdentifierClass; + Constructed : Boolean; + TagValue : Integer; + TagType : TIdASN1IdentifierType; + ContentLength : integer; + end; + + TIdASN1Sequence = Class + Private + FIdClass : TIdASN1IdentifierClass; + FTag : Integer; + FContents : String; + Public + Property IdClass : TIdASN1IdentifierClass Read FIdClass Write FIdClass; + Property Tag : integer Read FTag Write FTag; + Property Contents : String Read FContents Write FContents; + End; + + TIdASN1Sequences = Class(TObjectList) + Private + Function GetElement(Const iIndex : Integer) : TIdASN1Sequence; + function GetLast: TIdASN1Sequence; + Public + Property LastElement : TIdASN1Sequence read GetLast; + procedure Pop; + Property Elements[Const iIndex : Integer] : TIdASN1Sequence Read GetElement; Default; + End; + + TIdASN1Encoder = class + private + FSequences : TIdASN1Sequences; + FReadyToWrite : Boolean; + function FormatEncoding(aClass : TIdASN1IdentifierClass; bConstructed : Boolean; iTag : integer; const sContent : String) : String; + procedure AddEncoding(const sContent : String); + + procedure WriteInt(iTag : integer; iValue : integer); + function EncodeLength(iLen : Integer):String; + protected + // must call this as an outer wrapper + Procedure StartWriting; + Procedure StopWriting; + + // sequences + procedure StartSequence; overload; + procedure StartSequence(iTag : Integer); overload; + procedure StartSequence(aClass : TIdASN1IdentifierClass; iTag : Integer); overload; + procedure StopSequence; + + // primitives + procedure WriteBoolean(bValue : Boolean); + procedure WriteInteger(iValue : Integer); + procedure WriteEnum(iValue : Integer); + procedure WriteString(sValue : String); overload; + procedure WriteString(iTag : integer; sValue : String); overload; + public + Constructor Create; + destructor Destroy; override; + + procedure WriteToStream(Stream : TStream); + end; + + TIntegerList = class (TList) + private + function GetValue(iIndex: integer): Integer; + procedure SetValue(Index: integer; const Value: Integer); + public + procedure AddInt(value : integer); + procedure InsertInt(Index, Value : integer); + property Value[iIndex : integer]:Integer read GetValue write SetValue; default; + end; + + TIdASN1Decoder = class + private + FLengths : TIntegerList; + FPosition : Integer; + FNextHeader : TIdASN1Identifier; + FNextHeaderUsed : Boolean; + FStream: TStream; + function ReadHeader : TIdASN1Identifier; // -1 in length means that no definite length was specified + function DescribeIdentifier(const aId : TIdASN1Identifier) : String; + Function ReadByte : Byte; + function ReadChar : Char; + function ReadContentLength : Integer; + protected + procedure Check(bCondition : Boolean; const sMethod, sMessage : String); overload; virtual; + + // must call this as an outer wrapper + Procedure StartReading; + Procedure StopReading; + + // sequences and choices + procedure ReadSequenceBegin; + function SequenceEnded : Boolean; + procedure ReadSequenceEnd; + function NextTag : integer; + function NextTagType : TIdASN1IdentifierType; + + // primitives + function ReadBoolean : Boolean; + Function ReadInteger : Integer; + function ReadEnum : Integer; + Function ReadString : String; + + public + Constructor Create; + destructor Destroy; override; + property Stream : TStream read FStream write FStream; + end; + + +const + NAMES_ASN1IDENTIFIERTYPE : array [TIdASN1IdentifierType] of String = ('Unknown', 'Sequence', 'Boolean', 'Integer', 'Enum', 'String', 'OID', 'Real'); + TAGS_ASN1IDENTIFIERTYPE : array [TIdASN1IdentifierType] of Integer = (0, $10, $01, $02, $0A, $04, $06, 0 {?}); + NAMES_ASN1IDENTIFIERCLASS : array [TIdASN1IdentifierClass] of String = ('Universal', 'Application', 'ContextSpecific', 'Private'); + +function ToIdentifierType(iTag : integer) : TIdASN1IdentifierType; + +implementation + +uses + IdGlobal, IdException, SysUtils; + +function ToIdentifierType(iTag : integer) : TIdASN1IdentifierType; +begin + case iTag of + $10 : result := aitSequence; + $01 : result := aitBoolean; + $02 : result := aitInteger; + $04 : result := aitString; + $06 : result := aitOID; + $0A : result := aitEnum; + else + result := aitUnknown; + end; +end; + + +{ TIdASN1Encoder } + +constructor TIdASN1Encoder.Create; +begin + inherited Create; + FSequences := TIdASN1Sequences.create; +end; + +destructor TIdASN1Encoder.Destroy; +begin + FSequences.Free; + inherited Destroy; +end; + +procedure TIdASN1Encoder.WriteToStream(Stream : TStream); +begin + Assert(FReadyToWrite, 'not ready to write'); + if Length(FSequences[0].Contents) <> 0 then + WriteStringToStream(Stream, FSequences[0].Contents, IndyTextEncoding_8Bit); +end; + +procedure TIdASN1Encoder.StartWriting; +begin + FSequences.Clear; + StartSequence(aicUniversal, 0); +end; + +procedure TIdASN1Encoder.StopWriting; +begin + assert(FSequences.Count = 1, 'Writing left an open Sequence'); + FReadyToWrite := true; +// todo - actually commit to stream Produce(Fsequences[0].Contents); +end; + +procedure TIdASN1Encoder.StartSequence(aClass: TIdASN1IdentifierClass; iTag: Integer); +var + oSequence : TIdASN1Sequence; +begin + oSequence := TIdASN1Sequence.create; + try + oSequence.IdClass := aClass; + oSequence.Tag := iTag; + oSequence.Contents := ''; + FSequences.add(oSequence); + finally + oSequence.Free; + end; +end; + +procedure TIdASN1Encoder.StartSequence(iTag: Integer); +begin + if iTag = -1 then + StartSequence(aicUniversal, TAGS_ASN1IDENTIFIERTYPE[aitSequence]) + else + StartSequence(aicApplication, iTag); +end; + +procedure TIdASN1Encoder.StartSequence; +begin + StartSequence(aicUniversal, TAGS_ASN1IDENTIFIERTYPE[aitSequence]); +end; + +procedure TIdASN1Encoder.StopSequence; +var + sSequence : String; +begin + sSequence := FormatEncoding(FSequences.LastElement.IdClass, true, FSequences.LastElement.Tag, FSequences.LastElement.Contents); + FSequences.Pop; + AddEncoding(sSequence); +end; + + +procedure TIdASN1Encoder.WriteBoolean(bValue: Boolean); +begin + // RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler + // may change characters >= #128 from their Ansi codepage value to their true + // Unicode codepoint value, depending on the codepage used for the source code. + // For instance, #128 may become #$20AC... + if bValue then + AddEncoding(FormatEncoding(aicUniversal, False, TAGS_ASN1IDENTIFIERTYPE[aitBoolean], Char($FF))) + else + AddEncoding(FormatEncoding(aicUniversal, False, TAGS_ASN1IDENTIFIERTYPE[aitBoolean], #$00)); +end; + +procedure TIdASN1Encoder.WriteEnum(iValue: Integer); +begin + WriteInt(TAGS_ASN1IDENTIFIERTYPE[aitEnum], iValue); +end; + +procedure TIdASN1Encoder.WriteInteger(iValue: Integer); +begin + WriteInt(TAGS_ASN1IDENTIFIERTYPE[aitInteger], iValue); +end; + +procedure TIdASN1Encoder.WriteInt(iTag, iValue: integer); +var + sValue : String; + x, y: Cardinal; + bNeg: Boolean; +begin + bNeg := iValue < 0; + x := Abs(iValue); + if bNeg then + x := not (x - 1); + sValue := ''; {Do not Localize} + repeat + y := x mod 256; + x := x div 256; + sValue := Char(y) + sValue; + until x = 0; + if (not bNeg) and (sValue[1] > #$7F) then + sValue := #0 + sValue; + + AddEncoding(FormatEncoding(aicUniversal, False, iTag, sValue)) +end; + +procedure TIdASN1Encoder.WriteString(sValue: String); +begin + AddEncoding(FormatEncoding(aicUniversal, False, TAGS_ASN1IDENTIFIERTYPE[aitString], sValue)) +end; + +procedure TIdASN1Encoder.WriteString(iTag : integer; sValue: String); +begin + AddEncoding(FormatEncoding(aicContextSpecific, False, iTag, sValue)) +end; + +procedure TIdASN1Encoder.AddEncoding(const sContent: String); +begin + FSequences.LastElement.Contents := FSequences.LastElement.Contents + sContent; +end; + +function TIdASN1Encoder.FormatEncoding(aClass: TIdASN1IdentifierClass; bConstructed : Boolean; iTag: integer; const sContent: String): String; +begin + if bConstructed then + result := chr((ord(aClass) shl 6) or $20 or iTag) + EncodeLength(length(sContent)) + sContent + else + result := chr((ord(aClass) shl 6) or iTag) + EncodeLength(length(sContent)) + sContent; +end; + +function TIdASN1Encoder.EncodeLength(iLen: Integer): String; +var + x, y: Integer; +begin + if iLen < $80 then + Result := Char(iLen) + else + begin + x := iLen; + Result := ''; + repeat + y := x mod 256; + x := x div 256; + Result := Char(y) + Result; + until x = 0; + y := Length(Result); + y := y or $80; + Result := Char(y) + Result; + end; +end; + +{ TIdASN1Sequences } + +function TIdASN1Sequences.GetElement(const iIndex: Integer): TIdASN1Sequence; +begin + result := TIdASN1Sequence(items[iIndex]); +end; + +function TIdASN1Sequences.GetLast: TIdASN1Sequence; +begin + if Count = 0 then + result := nil + else + result := GetElement(Count - 1); +end; + +procedure TIdASN1Sequences.Pop; +begin + if Count > 0 then + Delete(Count-1); +end; + +{ TIdASN1Decoder } + +Constructor TIdASN1Decoder.Create; +begin + inherited Create; + FLengths := TIntegerList.create; +end; + +destructor TIdASN1Decoder.Destroy; +begin + FLengths.Free; + Inherited Destroy; +end; + +procedure TIdASN1Decoder.Check(bCondition: Boolean; const sMethod, sMessage: String); +begin + if not bCondition then + raise EIdException.create(sMessage); +end; + +Procedure TIdASN1Decoder.StartReading; +begin + FLengths.Clear; + FLengths.AddInt(-1); + FNextHeaderUsed := False; + FPosition := 0; +end; + +Procedure TIdASN1Decoder.StopReading; +begin + Check(FLengths.Count = 1, 'StopReading', 'Reading was incomplete'); + FLengths.Clear; +end; + +function TIdASN1Decoder.DescribeIdentifier(const aId : TIdASN1Identifier) : String; +begin + result := '[Pos '+IntToStr(aId.Position)+', Type '+NAMES_ASN1IDENTIFIERTYPE[aId.TagType]+', '+ + 'Tag '+IntToStr(aId.TagValue)+', Class '+NAMES_ASN1IDENTIFIERCLASS[aId.IdClass]+']'; +end; + +Function TIdASN1Decoder.ReadByte : Byte; +begin + Check(FLengths[0] <> 0, 'ReadByte', 'Attempt to read past end of Sequence'); + Stream.Read(result, 1); + inc(FPosition); + FLengths[0] := FLengths[0] - 1; +end; + +function TIdASN1Decoder.ReadChar : Char; +begin + result := Chr(readByte); +end; + +function TIdASN1Decoder.ReadContentLength: Integer; +var + iNext : Byte; + iLoop: Integer; +begin + iNext := ReadByte; + if iNext < $80 then + Result := iNext + else + begin + Result := 0; + iNext := iNext and $7F; + if iNext = 0 then + raise EIdException.create('Indefinite lengths are not yet handled'); + for iLoop := 1 to iNext do + begin + Result := Result * 256; + iNext := ReadByte; + Result := Result + iNext; + end; + end; +end; + +function TIdASN1Decoder.ReadHeader : TIdASN1Identifier; +var + iNext : Byte; +begin + if FNextHeaderUsed then + begin + result := FNextHeader; + FNextHeaderUsed := False; + end + else + begin + FillChar(result, sizeof(TIdASN1Identifier), #0); + result.Position := FPosition; + iNext := ReadByte; + result.Constructed := iNext and $20 > 0; + result.IdClass := TIdASN1IdentifierClass(iNext shr 6); + if iNext and $1F = $1F then + begin + raise EIdException.create('Todo'); + end + else + result.TagValue := iNext and $1F; + result.TagType := ToIdentifierType(result.TagValue); + result.ContentLength := ReadContentLength; + end; +end; + +function TIdASN1Decoder.NextTag: integer; +begin + if not FNextHeaderUsed then + begin + FNextHeader := ReadHeader; + FNextHeaderUsed := true; + end; + result := FNextHeader.TagValue; +end; + +function TIdASN1Decoder.NextTagType: TIdASN1IdentifierType; +begin + if not FNextHeaderUsed then + begin + FNextHeader := ReadHeader; + FNextHeaderUsed := true; + end; + result := FNextHeader.TagType; +end; + +function TIdASN1Decoder.ReadBoolean : Boolean; +var + aId : TIdASN1Identifier; +begin + aId := ReadHeader; + Check((aId.IdClass = aicApplication) or (aId.TagType = aitBoolean), 'ReadBoolean', 'Found '+DescribeIdentifier(aId)+' expecting a Boolean'); + Check(aId.ContentLength = 1, 'ReadBoolean', 'Boolean Length should be 1'); + result := ReadByte <> 0; +end; + +Function TIdASN1Decoder.ReadInteger : Integer; +var + aId : TIdASN1Identifier; + iVal : Integer; + iNext : Byte; + bNeg : Boolean; + iLoop : integer; +begin + aId := ReadHeader; + Check((aId.IdClass = aicApplication) or (aId.TagType = aitInteger), 'ReadInteger', 'Found '+DescribeIdentifier(aId)+' expecting an Integer'); + Check(aId.ContentLength >= 1, 'ReadInteger', 'Boolean Length should not be 0'); + + iVal := 0; + bNeg := False; + for iLoop := 1 to aId.ContentLength do + begin + iNext := ReadByte; + if (iLoop = 1) and (iNext > $7F) then + bNeg := True; + if bNeg then + iNext := not iNext; + iVal := iVal * 256 + iNext; + end; + if bNeg then + iVal := -(iVal + 1); + + Result := iVal; +end; + +function TIdASN1Decoder.ReadEnum : Integer; +var + aId : TIdASN1Identifier; + iVal : Integer; + iNext : Byte; + bNeg : Boolean; + iLoop : integer; +begin + aId := ReadHeader; + Check((aId.IdClass = aicApplication) or (aId.TagType = aitEnum), 'ReadEnum', 'Found '+DescribeIdentifier(aId)+' expecting an Enum'); + Check(aId.ContentLength >= 1, 'ReadEnum', 'Boolean Length should not be 0'); + + iVal := 0; + bNeg := False; + for iLoop := 1 to aId.ContentLength do + begin + iNext := ReadByte; + if (iLoop = 1) and (iNext > $7F) then + bNeg := True; + if bNeg then + iNext := not iNext; + iVal := iVal * 256 + iNext; + end; + if bNeg then + iVal := -(iVal + 1); + + Result := iVal; +end; + +Function TIdASN1Decoder.ReadString : String; +var + aId : TIdASN1Identifier; + iLoop : integer; +begin + aId := ReadHeader; + Check((aId.IdClass = aicApplication) or (aId.TagType in [aitUnknown, aitString]), 'ReadString', 'Found '+DescribeIdentifier(aId)+' expecting a String'); + SetLength(result, aId.ContentLength); + for iLoop := 1 to aId.ContentLength do + result[iLoop] := ReadChar; +end; + + +procedure TIdASN1Decoder.ReadSequenceBegin; +var + aId : TIdASN1Identifier; +begin + aId := ReadHeader; + Check((aId.IdClass = aicApplication) or (aId.TagType in [aitUnknown, aitSequence]), 'ReadSequenceBegin', 'Found '+DescribeIdentifier(aId)+' expecting a Sequence'); + FLengths[0] := FLengths[0] - aId.ContentLength; + FLengths.InsertInt(0, aId.ContentLength); +end; + +function TIdASN1Decoder.SequenceEnded: Boolean; +begin + Check(FLengths.Count > 1, 'SequenceEnded', 'Not in a Sequence'); + result := FLengths[0] <= 0; +end; + +procedure TIdASN1Decoder.ReadSequenceEnd; +begin + Check(SequenceEnded, 'ReadSequenceEnd', 'Sequence has not ended'); + FLengths.Delete(0); +end; + +{ TIntegerList } + +procedure TIntegerList.AddInt(value: integer); +begin + Add(pointer(value)); +end; + +function TIntegerList.GetValue(iIndex: integer): Integer; +begin + result := integer(items[iIndex]); +end; + +procedure TIntegerList.InsertInt(Index, Value: integer); +begin + insert(Index, pointer(value)); +end; + +procedure TIntegerList.SetValue(Index: integer; const Value: Integer); +begin + items[Index] := pointer(value); +end; + +end. + + + diff --git a/indy/Protocols/IdASN1Util.pas b/indy/Protocols/IdASN1Util.pas new file mode 100644 index 0000000..de327bf --- /dev/null +++ b/indy/Protocols/IdASN1Util.pas @@ -0,0 +1,519 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 3/3/2005 9:13:36 AM JPMugaas + Should work in DotNET. + + Rev 1.1 02/03/2005 00:09:14 CCostelloe + Bug fix (high bit treated as sign instead of MSB) + + Rev 1.0 11/14/2002 02:12:30 PM JPMugaas +} + +unit IdASN1Util; + +// REVIEW: Licensing problem +// 1) Is this only used by SNMP? If so it should be merged there. +// 2) MPL conflicts with Indy's BSD. We need permission to distribute under BSD as well. +// 3) A comment needs to be added that Indy has permission to use this + +{ +|==============================================================================| +| Project : Delphree - Synapse | 001.003.004 | +|==============================================================================| +| Content: support for ASN.1 coding and decoding | +|==============================================================================| +| The contents of this file are subject to the Mozilla Public License Ver. 1.1 | +| (the "License"); you may not use this file except in compliance with the | +| License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ | +| | +| Software distributed under the License is distributed on an "AS IS" basis, | +| WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for | +| the specific language governing rights and limitations under the License. | +|==============================================================================| +| The Original Code is Synapse Delphi Library. | +|==============================================================================| +| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| +| Portions created by Lukas Gebauer are Copyright (c) 1999,2000,2001. | +| Portions created by Hernan Sanchez are Copyright (c) 2000. | +| All Rights Reserved. | +|==============================================================================| +| Contributor(s): | +| Hernan Sanchez (hernan.sanchez@iname.com) | +|==============================================================================| +| History: see HISTORY.HTM from distribution package | +| (Found at URL: http://www.ararat.cz/synapse/) | +|==============================================================================| +} + +{$Q-} +{$WEAKPACKAGEUNIT ON} + +interface +{$i IdCompilerDefines.inc} + +uses + SysUtils; + +const + ASN1_INT = $02; + ASN1_OCTSTR = $04; + ASN1_NULL = $05; + ASN1_OBJID = $06; + ASN1_SEQ = $30; + ASN1_IPADDR = $40; + ASN1_COUNTER = $41; + ASN1_GAUGE = $42; + ASN1_TIMETICKS = $43; + ASN1_OPAQUE = $44; + +function ASNEncOIDItem(Value: Integer): string; +function ASNDecOIDItem(var Start: Integer; const Buffer: string): Integer; +function ASNEncLen(Len: Integer): string; +function ASNDecLen(var Start: Integer; const Buffer: string): Integer; +function ASNEncInt(Value: Integer): string; +function ASNEncUInt(Value: Integer): string; +function ASNObject(const Data: string; ASNType: Integer): string; +function ASNItem(var Start: Integer; const Buffer: string; + var ValueType: Integer): string; +function MibToId(Mib: string): string; +function IdToMib(const Id: string): string; +function IntMibToStr(const Value: string): string; + +implementation + +uses + IdGlobal; + +{==============================================================================} +function ASNEncOIDItem(Value: Integer): string; +var + x, xm: Integer; + b: Boolean; +begin + x := Value; + b := False; + Result := ''; {Do not Localize} + repeat + xm := x mod 128; + x := x div 128; + if b then + xm := xm or $80; + if x > 0 then + b := True; + Result := Char(xm) + Result; + until x = 0; +end; + +{==============================================================================} +function ASNDecOIDItem(var Start: Integer; const Buffer: string): Integer; +var + x: Integer; + b: Boolean; +begin + Result := 0; + repeat + Result := Result * 128; + x := Ord(Buffer[Start]); + Inc(Start); + b := x > $7F; + x := x and $7F; + Result := Result + x; + until not b; +end; + +{==============================================================================} +function ASNEncLen(Len: Integer): string; +var + x, y: Integer; +begin + if Len < $80 then + Result := Char(Len) + else + begin + x := Len; + Result := ''; {Do not Localize} + repeat + y := x mod 256; + x := x div 256; + Result := Char(y) + Result; + until x = 0; + y := Length(Result); + y := y or $80; + Result := Char(y) + Result; + end; +end; + +{==============================================================================} +function ASNDecLen(var Start: Integer; const Buffer: string): Integer; +var + x, n: Integer; +begin + x := Ord(Buffer[Start]); + Inc(Start); + if x < $80 then + Result := x + else + begin + Result := 0; + x := x and $7F; + for n := 1 to x do + begin + Result := Result * 256; + x := Ord(Buffer[Start]); + Inc(Start); + Result := Result + x; + end; + end; +end; + +{==============================================================================} +function ASNEncInt(Value: Integer): string; +var + x, y: UInt32; + neg: Boolean; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + neg := Value < 0; + x := Abs(Value); + if neg then begin + x := not (x - 1); + end; + Result := ''; {Do not Localize} + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create; + {$ENDIF} + repeat + y := x mod 256; + x := x div 256; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Insert(0, Char(y)); + {$ELSE} + Result := Char(y) + Result; + {$ENDIF} + until x = 0; + if (not neg) then + begin + {$IFDEF STRING_IS_IMMUTABLE} + if (LSB[0] > #$7F) then begin + LSB.Insert(0, #0); + end; + {$ELSE} + if (Result[1] > #$7F) then begin + Result := #0 + Result; + end; + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +{==============================================================================} +function ASNEncUInt(Value: Integer): string; +var + x, y: Integer; + neg: Boolean; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + neg := Value < 0; + x := Value; + if neg then begin + x := x and $7FFFFFFF; + end; + Result := ''; {Do not Localize} + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create; + {$ENDIF} + repeat + y := x mod 256; + x := x div 256; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Insert(0, Char(y)); + {$ELSE} + Result := Char(y) + Result; + {$ENDIF} + until x = 0; + if neg then begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB[0] := Char(Ord(LSB[0]) or $80); + {$ELSE} + Result[1] := Char(Ord(Result[1]) or $80); + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +{==============================================================================} +function ASNObject(const Data: string; ASNType: Integer): string; +begin + Result := Char(ASNType) + ASNEncLen(Length(Data)) + Data; +end; + +{==============================================================================} +function ASNItem(var Start: Integer; const Buffer: string; + var ValueType: Integer): string; +var + ASNType: Integer; + ASNSize: Integer; + y, n: Integer; + x: byte; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ELSE} + s: string; + {$ENDIF} + neg: Boolean; + l: Integer; + z: Int64; + +begin + Result := ''; {Do not Localize} + ValueType := ASN1_NULL; + l := Length(Buffer); + if l < (Start + 1) then begin + Exit; + end; + ASNType := Ord(Buffer[Start]); + ValueType := ASNType; + Inc(Start); + ASNSize := ASNDecLen(Start, Buffer); + if (Start + ASNSize - 1) > l then begin + Exit; + end; + if (ASNType and $20) > 0 then begin + Result := '$' + IntToHex(ASNType, 2) {Do not Localize} + end else + begin + case ASNType of + ASN1_INT: + begin + y := 0; + neg := False; + for n := 1 to ASNSize do + begin + x := Ord(Buffer[Start]); + if (n = 1) and (x > $7F) then begin + neg := True; + end; + if neg then begin + x := not x; + end; + y := y * 256 + x; + Inc(Start); + end; + if neg then begin + y := -(y + 1); + end; + Result := IntToStr(y); + end; + ASN1_COUNTER, ASN1_GAUGE, ASN1_TIMETICKS: //Typically a 32-bit _unsigned_ number + begin + z := 0; + for n := 1 to ASNSize do begin + x := Ord(Buffer[Start]); //get the byte + y := x; //promote to an integer + z := (z * 256) + y; //now accumulate value + Inc(Start); + end; + Result := IntToStr(z); + end; + ASN1_OCTSTR, ASN1_OPAQUE: + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(ASNSize); + {$ELSE} + SetLength(s, ASNSize); + {$ENDIF} + for n := 1 to ASNSize do + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char(Buffer[Start])); + {$ELSE} + s[n] := Char(Buffer[Start]); + {$ENDIF} + Inc(Start); + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ELSE} + Result := s; + {$ENDIF} + end; + ASN1_OBJID: + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(ASNSize); + {$ELSE} + SetLength(s, ASNSize); + {$ENDIF} + for n := 1 to ASNSize do + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char(Buffer[Start])); + {$ELSE} + s[n] := Char(Buffer[Start]); + {$ENDIF} + Inc(Start); + end; + Result := IdToMib( + {$IFDEF STRING_IS_IMMUTABLE} + LSB.ToString + {$ELSE} + s + {$ENDIF} + ); + end; + ASN1_IPADDR: + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(15); + {$ELSE} + s := ''; {Do not Localize} + {$ENDIF} + for n := 1 to ASNSize do + begin + if (n <> 1) then begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append('.'); {Do not Localize} + {$ELSE} + s := s + '.'; {Do not Localize} + {$ENDIF} + end; + y := Ord(Buffer[Start]); + Inc(Start); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(y); + {$ELSE} + s := s + IntToStr(y); + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ELSE} + Result := s; + {$ENDIF} + end; + ASN1_NULL: + begin + Result := ''; + Inc(Start, ASNSize); + end; + else // unknown + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(ASNSize); + {$ELSE} + SetLength(s, ASNSize); + {$ENDIF} + for n := 1 to ASNSize do + begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char(Buffer[Start])); + {$ELSE} + s[n] := Char(Buffer[Start]); + {$ENDIF} + Inc(Start); + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ELSE} + Result := s; + {$ENDIF} + end; + end; + end; +end; + +{==============================================================================} +function MibToId(Mib: string): string; +var + x: Integer; + + function WalkInt(var s: string): Integer; + var + lx: Integer; + t: string; + begin + lx := Pos('.', s); {Do not Localize} + if lx < 1 then + begin + t := s; + s := ''; {Do not Localize} + end + else + begin + t := Copy(s, 1, lx - 1); + s := Copy(s, lx + 1, MaxInt); + end; + Result := IndyStrToInt(t, 0); + end; + +begin + Result := ''; {Do not Localize} + x := WalkInt(Mib); + x := x * 40 + WalkInt(Mib); + Result := ASNEncOIDItem(x); + while Mib <> '' do {Do not Localize} + begin + x := WalkInt(Mib); + Result := Result + ASNEncOIDItem(x); + end; +end; + +{==============================================================================} +function IdToMib(const Id: string): string; +var + x, y, n: Integer; +begin + Result := ''; {Do not Localize} + n := 1; + while Length(Id) + 1 > n do + begin + x := ASNDecOIDItem(n, Id); + if (n - 1) = 1 then + begin + y := x div 40; + x := x mod 40; + Result := IntToStr(y); + end; + Result := Result + '.' + IntToStr(x); {Do not Localize} + end; +end; + +{==============================================================================} +function IntMibToStr(const Value: string): string; +var + n, y: Integer; +begin + y := 0; + for n := 1 to Length(Value) - 1 do begin + y := y * 256 + Ord(Value[n]); + end; + Result := IntToStr(y); +end; + +{==============================================================================} + +end. diff --git a/indy/Protocols/IdAllAuthentications.pas b/indy/Protocols/IdAllAuthentications.pas new file mode 100644 index 0000000..dea109e --- /dev/null +++ b/indy/Protocols/IdAllAuthentications.pas @@ -0,0 +1,40 @@ +unit IdAllAuthentications; + +interface + +{ +Note that this unit is simply for listing ALL Authentications in Indy. +The user could then add this unit to a uses clause in their program and +have all Authentications linked into their program. + +ABSOLUTELY NO CODE is permitted in this unit. + +} + +{$I IdCompilerDefines.inc} + +// RLebeau 2/14/09: this forces C++Builder to link to this unit so +// the units can register themselves correctly at program startup... + +{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} +{$ELSE} + {$HPPEMIT '#pragma link "IdAllAuthentications"'} +{$ENDIF} + +implementation + +uses + {$IFNDEF DOTNET} + {$IFDEF USE_OPENSSL} + IdAuthenticationNTLM, + {$ENDIF} + {$IFDEF USE_SSPI} + IdAuthenticationSSPI, + {$ENDIF} + {$ENDIF} + IdAuthenticationDigest; + +{dee-duh-de-duh, that's all folks.} + +end. diff --git a/indy/Protocols/IdAllFTPListParsers.pas b/indy/Protocols/IdAllFTPListParsers.pas new file mode 100644 index 0000000..7130e04 --- /dev/null +++ b/indy/Protocols/IdAllFTPListParsers.pas @@ -0,0 +1,69 @@ +unit IdAllFTPListParsers; + +interface + +{$I IdCompilerDefines.inc} + +{ +Note that is unit is simply for listing ALL FTP List parsers in Indy. +The user could then add this unit to a uses clause in their program and +have all FTP list parsers linked into their program. + +ABSOLELY NO CODE is permitted in this unit. + +} + +// RLebeau 4/17/10: this forces C++Builder to link to this unit so +// the units can register themselves correctly at program startup... + +{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} +{$ELSE} + {$HPPEMIT '#pragma link "IdAllFTPListParsers"'} +{$ENDIF} + +implementation + +uses + IdFTPListParseAS400, + IdFTPListParseBullGCOS7, + IdFTPListParseBullGCOS8, + IdFTPListParseChameleonNewt, + IdFTPListParseCiscoIOS, + IdFTPListParseDistinctTCPIP, + IdFTPListParseEPLF, + IdFTPListParseHellSoft, + IdFTPListParseIEFTPGateway, + IdFTPListParseKA9Q, + IdFTPListParseMicrowareOS9, + IdFTPListParseMPEiX, + IdFTPListParseMusic, + IdFTPListParseMVS, + IdFTPListParseNCSAForDOS, + IdFTPListParseNCSAForMACOS, + IdFTPListParseNovellNetware, + IdFTPListParseNovellNetwarePSU, + IdFTPListParseOS2, + IdFTPListParsePCNFSD, + IdFTPListParsePCTCP, + IdFTPListParseStercomOS390Exp, + IdFTPListParseStercomUnixEnt, + IdFTPListParseStratusVOS, + IdFTPListParseSuperTCP, + IdFTPListParseTandemGuardian, + IdFTPListParseTOPS20, + IdFTPListParseTSXPlus, + IdFTPListParseUnisysClearPath, + IdFTPListParseUnix, + IdFTPListParseVM, + IdFTPListParseVMS, + IdFTPListParseVSE, + IdFTPListParseVxWorks, + IdFTPListParseWfFTP, + IdFTPListParseWindowsNT, + IdFTPListParseWinQVTNET, + IdFTPListParseXecomMicroRTOS; + +{dee-duh-de-duh, that's all folks.} + +end. diff --git a/indy/Protocols/IdAllHeaderCoders.pas b/indy/Protocols/IdAllHeaderCoders.pas new file mode 100644 index 0000000..257d39d --- /dev/null +++ b/indy/Protocols/IdAllHeaderCoders.pas @@ -0,0 +1,34 @@ +unit IdAllHeaderCoders; + +interface + +{$i IdCompilerDefines.inc} + +{ +Note that this unit is simply for listing ALL Header coders in Indy. +The user could then add this unit to a uses clause in their program and +have all Header coders linked into their program. + +ABSOLUTELY NO CODE is permitted in this unit. + +} + +// RLebeau 2/14/09: this forces C++Builder to link to this unit so +// the units can register themselves correctly at program startup... + +{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} +{$ELSE} + {$HPPEMIT '#pragma link "IdAllHeaderCoders"'} +{$ENDIF} + +implementation + +uses + IdHeaderCoderPlain, + IdHeaderCoder2022JP, + IdHeaderCoderIndy; + +{dee-duh-de-duh, that's all folks.} + +end. diff --git a/indy/Protocols/IdAttachment.pas b/indy/Protocols/IdAttachment.pas new file mode 100644 index 0000000..863a8ed --- /dev/null +++ b/indy/Protocols/IdAttachment.pas @@ -0,0 +1,156 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 2/8/05 6:02:10 PM RLebeau + Try that again... + + Rev 1.7 2/8/05 6:00:02 PM RLebeau + Updated SaveToFile() to call SaveToStream() + + Rev 1.6 6/16/2004 2:10:48 PM EHill + Added SaveToStream method for TIdAttachment + + Rev 1.5 2004.03.03 10:30:46 AM czhower + Removed warning. + + Rev 1.4 2/24/04 1:23:58 PM RLebeau + Bug fix for SaveToFile() using the wrong Size + + Rev 1.3 2004.02.03 5:44:50 PM czhower + Name changes + + Rev 1.2 10/17/03 12:07:28 PM RLebeau + Updated Assign() to copy all available header values rather than select ones. + + Rev 1.1 10/16/2003 10:55:24 PM DSiders + Added localization comments. + + Rev 1.0 11/14/2002 02:12:36 PM JPMugaas +} + +unit IdAttachment; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdMessageParts, + IdBaseComponent; + +type + TIdAttachment = class(TIdMessagePart) + public + // here the methods you have to override... + + // for open handling + // works like this: + // 1) you create an attachment - and do whatever it takes to put data in it + // 2) you send the message + // 3) this will be called - first OpenLoadStream, to get a stream + // 4) when the message is fully encoded, CloseLoadStream is called + // to close the stream. The Attachment implementation decides what to do + function OpenLoadStream: TStream; virtual; abstract; + procedure CloseLoadStream; virtual; abstract; + + // for save handling + // works like this: + // 1) new attachment is created + // 2) PrepareTempStream is called + // 3) stuff is loaded + // 4) FinishTempStream is called of the newly created attachment + function PrepareTempStream: TStream; virtual; abstract; + procedure FinishTempStream; virtual; abstract; + + procedure LoadFromFile(const FileName: String); virtual; + procedure LoadFromStream(AStream: TStream); virtual; + procedure SaveToFile(const FileName: String); virtual; + procedure SaveToStream(AStream: TStream); virtual; + + class function PartType: TIdMessagePartType; override; + end; + + TIdAttachmentClass = class of TIdAttachment; + +implementation + +uses + IdGlobal, IdGlobalProtocols, IdCoderHeader, + SysUtils; + +{ TIdAttachment } + +class function TIdAttachment.PartType: TIdMessagePartType; +begin + Result := mptAttachment; +end; + +procedure TIdAttachment.LoadFromFile(const FileName: String); +var + LStrm: TIdReadFileExclusiveStream; +begin + LStrm := TIdReadFileExclusiveStream.Create(FileName); try + LoadFromStream(LStrm); + finally + FreeAndNil(LStrm); + end; +end; + +procedure TIdAttachment.LoadFromStream(AStream: TStream); +var + LStrm: TStream; +begin + LStrm := PrepareTempStream; + try + // TODO: use AStream.Size-AStream.Position instead of 0, and don't call + // CopyFrom() if (AStream.Size-AStream.Position) is <= 0. Passing 0 to + // CopyFrom() tells it to seek AStream to Position=0 and then copy the + // entire stream, which is fine for the stream provided by LoadFromFile(), + // but may not always be desirable for user-provided streams... + LStrm.CopyFrom(AStream, 0); + finally + FinishTempStream; + end; +end; + +procedure TIdAttachment.SaveToFile(const FileName: String); +var + LStrm: TIdFileCreateStream; +begin + LStrm := TIdFileCreateStream.Create(FileName); try + SaveToStream(LStrm); + finally + FreeAndNil(LStrm); + end; +end; + +procedure TIdAttachment.SaveToStream(AStream: TStream); +var + LStrm: TStream; +begin + LStrm := OpenLoadStream; + try + AStream.CopyFrom(LStrm, 0); + finally + CloseLoadStream; + end; +end; + +end. + diff --git a/indy/Protocols/IdAttachmentFile.pas b/indy/Protocols/IdAttachmentFile.pas new file mode 100644 index 0000000..062219f --- /dev/null +++ b/indy/Protocols/IdAttachmentFile.pas @@ -0,0 +1,164 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 28.09.2004 21:04:44 Andreas Hausladen + Delphi 5 does not have a Owner property in TCollection + + Rev 1.3 24.08.2004 18:01:42 Andreas Hausladen + Added AttachmentBlocked property to TIdAttachmentFile. + + Rev 1.2 2004.02.03 5:44:50 PM czhower + Name changes + + Rev 1.1 5/9/2003 10:27:20 AM BGooijen + Attachment is now opened in fmShareDenyWrite mode + + Rev 1.0 11/14/2002 02:12:42 PM JPMugaas +} + +unit IdAttachmentFile; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAttachment, + IdMessageParts; + +type + TIdAttachmentFile = class(TIdAttachment) + protected + FTempFileStream: TFileStream; + FStoredPathName: String; + FFileIsTempFile: Boolean; + FAttachmentBlocked: Boolean; + public + constructor Create(Collection: TIdMessageParts; const AFileName: String = ''); reintroduce; + destructor Destroy; override; + + function OpenLoadStream: TStream; override; + procedure CloseLoadStream; override; + function PrepareTempStream: TStream; override; + procedure FinishTempStream; override; + + procedure SaveToFile(const FileName: String); override; + + property FileIsTempFile: Boolean read FFileIsTempFile write FFileIsTempFile; + property StoredPathName: String read FStoredPathName write FStoredPathName; + property AttachmentBlocked: Boolean read FAttachmentBlocked; + end; + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + Posix.Unistd, + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + Libc, + {$ENDIF} + //facilitate inlining only. + {$IFDEF USE_INLINE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$IFDEF DOTNET} + System.IO, + {$ENDIF} + {$ENDIF} + IdGlobal, IdGlobalProtocols, IdException, IdResourceStringsProtocols, + IdMessage, SysUtils; + +{ TIdAttachmentFile } + +procedure TIdAttachmentFile.CloseLoadStream; +begin + FreeAndNil(FTempFileStream); +end; + +constructor TIdAttachmentFile.Create(Collection: TIdMessageParts; const AFileName: String = ''); +begin + inherited Create(Collection); + FFilename := ExtractFileName(AFilename); + FTempFileStream := nil; + FStoredPathName := AFileName; + FFileIsTempFile := False; + if FFilename <> '' then begin + ContentType := GetMimeTypeFromFile(FFilename); + end; +end; + +destructor TIdAttachmentFile.Destroy; +begin + if FileIsTempFile then begin + SysUtils.DeleteFile(StoredPathName); + end; + inherited Destroy; +end; + +procedure TIdAttachmentFile.FinishTempStream; +var + LMsg: TIdMessage; +begin + FreeAndNil(FTempFileStream); + // An on access virus scanner meight delete/block the temporary file. + FAttachmentBlocked := not FileExists(StoredPathName); + if FAttachmentBlocked then begin + LMsg := TIdMessage(OwnerMessage); + if Assigned(LMsg) and (not LMsg.ExceptionOnBlockedAttachments) then begin + Exit; + end; + raise EIdMessageCannotLoad.CreateFmt(RSTIdMessageErrorAttachmentBlocked, [StoredPathName]); + end; +end; + +function TIdAttachmentFile.OpenLoadStream: TStream; +begin + FTempFileStream := TIdReadFileExclusiveStream.Create(StoredPathName); + Result := FTempFileStream; +end; + +function TIdAttachmentFile.PrepareTempStream: TStream; +var + LMsg: TIdMessage; +begin + LMsg := TIdMessage(OwnerMessage); + if Assigned(LMsg) then begin + FStoredPathName := MakeTempFilename(LMsg.AttachmentTempDirectory); + end else begin + FStoredPathName := MakeTempFilename; + end; + FTempFileStream := TIdFileCreateStream.Create(FStoredPathName); + FFileIsTempFile := True; + Result := FTempFileStream; +end; + +procedure TIdAttachmentFile.SaveToFile(const FileName: String); +begin + if not CopyFileTo(StoredPathname, FileName) then begin + raise EIdException.Create(RSTIdMessageErrorSavingAttachment); + end; +end; + +initialization +// MtW: Shouldn't be neccessary?? +// RegisterClass(TIdAttachmentFile); + +end. diff --git a/indy/Protocols/IdAttachmentMemory.pas b/indy/Protocols/IdAttachmentMemory.pas new file mode 100644 index 0000000..28a4687 --- /dev/null +++ b/indy/Protocols/IdAttachmentMemory.pas @@ -0,0 +1,177 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.6 6/29/04 12:27:14 PM RLebeau + Updated to remove DotNet conditionals + + Updated constructor to call SetDataString() + + + Rev 1.5 2004.02.03 5:44:52 PM czhower + Name changes + + + Rev 1.4 2004.02.03 2:12:04 PM czhower + $I path change + + + Rev 1.3 24/01/2004 19:07:18 CCostelloe + Cleaned up warnings + + + Rev 1.2 14/12/2003 18:07:16 CCostelloe + Changed GetDataString to avoiud error 'String element cannot be passed to var + parameter' + + + Rev 1.1 13/05/2003 20:28:04 CCostelloe + Bug fix: remove default values in Create to avoid ambiguities with + Create(TCollection) + + + Rev 1.0 11/14/2002 02:12:46 PM JPMugaas +} +unit IdAttachmentMemory; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, IdAttachment, IdMessageParts, IdGlobal; + +type + TIdAttachmentMemory = class(TIdAttachment) + protected + FDataStream: TStream; + FDataStreamBeforeLoadPosition: TIdStreamSize; + + function GetDataString: string; + procedure SetDataStream(const Value: TStream); + procedure SetDataString(const Value: string); + public + {CC: Bug fix, remove default values to resolve ambiguities with Create(TCollection).} + constructor Create(Collection: TCollection); overload; override; + constructor Create(Collection: TIdMessageParts; const CopyFrom: TStream); reintroduce; overload; + constructor Create(Collection: TIdMessageParts; const CopyFrom: String); reintroduce; overload; + destructor Destroy; override; + + property DataStream: TStream read FDataStream write SetDataStream; + property DataString: string read GetDataString write SetDataString; + function OpenLoadStream: TStream; override; + procedure CloseLoadStream; override; + procedure FinishTempStream; override; + function PrepareTempStream: TStream; override; + end; + +implementation + +uses + SysUtils; + +{ TIdAttachmentMemory } + +constructor TIdAttachmentMemory.Create(Collection: TCollection); +begin + inherited Create(Collection); + FDataStream := TMemoryStream.Create; +end; + +constructor TIdAttachmentMemory.Create(Collection: TIdMessageParts; + const CopyFrom: TStream); +var + LSize: TIdStreamSize; +begin + inherited Create(Collection); + FDataStream := TMemoryStream.Create; + if Assigned(CopyFrom) then begin + LSize := IndyLength(CopyFrom); + if LSize > 0 then begin + FDataStream.CopyFrom(CopyFrom, LSize); + end; + end; +end; + +constructor TIdAttachmentMemory.Create(Collection: TIdMessageParts; + const CopyFrom: String); +begin + inherited Create(Collection); + FDataStream := TMemoryStream.Create; + SetDataString(CopyFrom); +end; + +destructor TIdAttachmentMemory.Destroy; +begin + FreeAndNil(FDataStream); + inherited Destroy; +end; + +procedure TIdAttachmentMemory.CloseLoadStream; +begin + DataStream.Position := FDataStreamBeforeLoadPosition; +end; + +function TIdAttachmentMemory.GetDataString: string; +var + Pos: TIdStreamSize; +begin + Pos := FDataStream.Position; + try + FDataStream.Position := 0; + Result := ReadStringFromStream(FDataStream, FDataStream.Size); + finally + FDataStream.Position := Pos; + end; +end; + +function TIdAttachmentMemory.OpenLoadStream: TStream; +begin + FDataStreamBeforeLoadPosition := DataStream.Position; + DataStream.Position := 0; + Result := DataStream; +end; + +procedure TIdAttachmentMemory.SetDataStream(const Value: TStream); +var + LSize: TIdStreamSize; +begin + FDataStream.Size := 0; + LSize := IndyLength(Value); + if LSize > 0 then begin + FDataStream.CopyFrom(Value, LSize); + end; +end; + +procedure TIdAttachmentMemory.SetDataString(const Value: string); +begin + FDataStream.Size := 0; + WriteStringToStream(FDataStream, Value); +end; + +procedure TIdAttachmentMemory.FinishTempStream; +begin + DataStream.Position := 0; +end; + +function TIdAttachmentMemory.PrepareTempStream: TStream; +begin + DataStream.Size := 0; + Result := DataStream; +end; + +end. diff --git a/indy/Protocols/IdAuthentication.pas b/indy/Protocols/IdAuthentication.pas new file mode 100644 index 0000000..072d68f --- /dev/null +++ b/indy/Protocols/IdAuthentication.pas @@ -0,0 +1,337 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 10:59:30 PM JPMugaas + Updated ref. + + Rev 1.4 2004.02.03 5:44:52 PM czhower + Name changes + + Rev 1.3 10/5/2003 5:01:34 PM GGrieve + fix to compile Under DotNet + + Rev 1.2 10/4/2003 9:09:28 PM GGrieve + DotNet fixes + + Rev 1.1 10/3/2003 11:40:38 PM GGrieve + move InfyGetHostName here + + Rev 1.0 11/14/2002 02:12:52 PM JPMugaas + + 2001-Sep-11 : DSiders + Corrected spelling for EIdAlreadyRegisteredAuthenticationMethod +} + +unit IdAuthentication; + +{ + Implementation of the Basic authentication as specified in RFC 2616 + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. + Author: Doychin Bondzhev (doychin@dsoft-bg.com) +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdHeaderList, + IdGlobal, + IdException; + +type + TIdAuthenticationSchemes = (asBasic, asDigest, asNTLM, asUnknown); + TIdAuthSchemeSet = set of TIdAuthenticationSchemes; + + TIdAuthWhatsNext = (wnAskTheProgram, wnDoRequest, wnFail); + + TIdAuthentication = class(TPersistent) + protected + FCurrentStep: Integer; + FParams: TIdHeaderList; + FAuthParams: TIdHeaderList; + + function ReadAuthInfo(AuthName: String): String; + function DoNext: TIdAuthWhatsNext; virtual; abstract; + procedure SetAuthParams(AValue: TIdHeaderList); + function GetPassword: String; + function GetUserName: String; + function GetSteps: Integer; virtual; + procedure SetPassword(const Value: String); virtual; + procedure SetUserName(const Value: String); virtual; + public + constructor Create; virtual; + destructor Destroy; override; + + procedure Reset; virtual; + procedure SetRequest(const AMethod, AUri: String); virtual; + + function Authentication: String; virtual; abstract; + function KeepAlive: Boolean; virtual; + function Next: TIdAuthWhatsNext; + + property AuthParams: TIdHeaderList read FAuthParams write SetAuthParams; + property Params: TIdHeaderList read FParams; + property Username: String read GetUserName write SetUserName; + property Password: String read GetPassword write SetPassword; + property Steps: Integer read GetSteps; + property CurrentStep: Integer read FCurrentStep; + end; + + TIdAuthenticationClass = class of TIdAuthentication; + + TIdBasicAuthentication = class(TIdAuthentication) + protected + FRealm: String; + function DoNext: TIdAuthWhatsNext; override; + function GetSteps: Integer; override; // this function determines the number of steps that this + // Authtentication needs take to suceed; + public + function Authentication: String; override; + + property Realm: String read FRealm write FRealm; + end; + + EIdAlreadyRegisteredAuthenticationMethod = class(EIdException); + + { Support functions } + procedure RegisterAuthenticationMethod(const MethodName: String; const AuthClass: TIdAuthenticationClass); + procedure UnregisterAuthenticationMethod(const MethodName: String); + function FindAuthClass(const AuthName: String): TIdAuthenticationClass; + +implementation + +uses + IdCoderMIME, IdGlobalProtocols, IdResourceStringsProtocols, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Defaults} + System.Generics.Defaults, + {$ENDIF} + SysUtils; + +var + AuthList: {$IFDEF HAS_GENERICS_TDictionary}TDictionary{$ELSE}TStringList{$ENDIF} = nil; + +procedure RegisterAuthenticationMethod(const MethodName: String; const AuthClass: TIdAuthenticationClass); +{$IFNDEF HAS_GENERICS_TDictionary} +var + I: Integer; +{$ENDIF} +begin + if not Assigned(AuthList) then begin + {$IFDEF HAS_GENERICS_TDictionary} + AuthList := TDictionary.Create(TIStringComparer.Ordinal); + {$ELSE} + AuthList := TStringList.Create; + {$ENDIF} + end; + {$IFDEF HAS_GENERICS_TDictionary} + if not AuthList.ContainsKey(MethodName) then begin + AuthList.Add(MethodName, AuthClass); + end else begin + //raise EIdAlreadyRegisteredAuthenticationMethod.CreateFmt(RSHTTPAuthAlreadyRegistered, [AuthClass.ClassName]); + AuthList.Items[MethodName] := AuthClass; + end; + {$ELSE} + I := AuthList.IndexOf(MethodName); + if I < 0 then begin + AuthList.AddObject(MethodName, TObject(AuthClass)); + end else begin + //raise EIdAlreadyRegisteredAuthenticationMethod.CreateFmt(RSHTTPAuthAlreadyRegistered, [AuthClass.ClassName]); + AuthList.Objects[I] := TObject(AuthClass); + end; + {$ENDIF} +end; + +procedure UnregisterAuthenticationMethod(const MethodName: String); +{$IFNDEF HAS_GENERICS_TDictionary} +var + I: Integer; +{$ENDIF} +begin + if Assigned(AuthList) then begin + {$IFDEF HAS_GENERICS_TDictionary} + if AuthList.ContainsKey(MethodName) then begin + AuthList.Remove(MethodName); + end; + {$ELSE} + I := AuthList.IndexOf(MethodName); + if I > 0 then begin + AuthList.Delete(I); + end; + {$ENDIF} + end; +end; + +function FindAuthClass(const AuthName: String): TIdAuthenticationClass; +{$IFNDEF HAS_GENERICS_TDictionary} +var + I: Integer; +{$ENDIF} +begin + Result := nil; + {$IFDEF HAS_GENERICS_TDictionary} + if AuthList.ContainsKey(AuthName) then begin + Result := AuthList.Items[AuthName]; + end; + {$ELSE} + I := AuthList.IndexOf(AuthName); + if I > -1 then begin + Result := TIdAuthenticationClass(AuthList.Objects[I]); + end; + {$ENDIF} +end; + +{ TIdAuthentication } + +constructor TIdAuthentication.Create; +begin + inherited Create; + FAuthParams := TIdHeaderList.Create(QuoteHTTP); + FParams := TIdHeaderList.Create(QuoteHTTP); + FCurrentStep := 0; +end; + +destructor TIdAuthentication.Destroy; +begin + FreeAndNil(FAuthParams); + FreeAndNil(FParams); + inherited Destroy; +end; + +procedure TIdAuthentication.SetAuthParams(AValue: TIdHeaderList); +begin + FAuthParams.Assign(AValue); +end; + +function TIdAuthentication.ReadAuthInfo(AuthName: String): String; +Var + i: Integer; +begin + for i := 0 to FAuthParams.Count - 1 do begin + if TextStartsWith(FAuthParams[i], AuthName) then begin + Result := FAuthParams[i]; + Exit; + end; + end; + Result := ''; {Do not Localize} +end; + +function TIdAuthentication.KeepAlive: Boolean; +begin + Result := False; +end; + +function TIdAuthentication.Next: TIdAuthWhatsNext; +begin + Result := DoNext; +end; + +procedure TIdAuthentication.Reset; +begin + FCurrentStep := 0; +end; + +procedure TIdAuthentication.SetRequest(const AMethod, AUri: String); +begin + // empty here, descendants can override as needed... +end; + +function TIdAuthentication.GetPassword: String; +begin + Result := Params.Values['Password']; {Do not Localize} +end; + +function TIdAuthentication.GetUserName: String; +begin + Result := Params.Values['Username']; {Do not Localize} +end; + +procedure TIdAuthentication.SetPassword(const Value: String); +begin + Params.Values['Password'] := Value; {Do not Localize} +end; + +procedure TIdAuthentication.SetUserName(const Value: String); +begin + Params.Values['Username'] := Value; {Do not Localize} +end; + +function TIdAuthentication.GetSteps: Integer; +begin + Result := 0; +end; + +{ TIdBasicAuthentication } + +function TIdBasicAuthentication.Authentication: String; +var + LEncoder: TIdEncoderMIME; +begin + LEncoder := TIdEncoderMIME.Create; + try + Result := 'Basic ' + LEncoder.Encode(Username + ':' + Password); {do not localize} + finally + LEncoder.Free; + end; +end; + +function TIdBasicAuthentication.DoNext: TIdAuthWhatsNext; +var + S: String; +begin + S := ReadAuthInfo('Basic'); {Do not Localize} + Fetch(S); + + while Length(S) > 0 do begin + // realm have 'realm="SomeRealmValue"' format {Do not Localize} + // FRealm never assigned without StringReplace + Params.Add(ReplaceOnlyFirst(Fetch(S, ', '), '=', Params.NameValueSeparator)); {do not localize} + end; + + FRealm := Copy(Params.Values['realm'], 2, Length(Params.Values['realm']) - 2); {Do not Localize} + + if FCurrentStep = 0 then + begin + if Length(Username) > 0 then begin + Result := wnDoRequest; + end else begin + Result := wnAskTheProgram; + end; + end else begin + Result := wnFail; + end; +end; + +function TIdBasicAuthentication.GetSteps: Integer; +begin + Result := 1; +end; + +initialization + RegisterAuthenticationMethod('Basic', TIdBasicAuthentication); {Do not Localize} +finalization + // UnregisterAuthenticationMethod('Basic') does not need to be called + // in this case because AuthList is freed. + FreeAndNil(AuthList); + +end. + diff --git a/indy/Protocols/IdAuthenticationDigest.pas b/indy/Protocols/IdAuthenticationDigest.pas new file mode 100644 index 0000000..ca979c0 --- /dev/null +++ b/indy/Protocols/IdAuthenticationDigest.pas @@ -0,0 +1,313 @@ +{ + $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$ +} +{ + 2005-04-22 BTaylor + Fixed AV from incorrect object being freed + Fixed memory leak + Improved parsing + + Rev 1.6 1/3/05 4:48:24 PM RLebeau + Removed reference to StrUtils unit, not being used. + + Rev 1.5 12/1/2004 1:57:50 PM JPMugaas + Updated with some code posted by: + + Interpulse Systeemontwikkeling + Interpulse Automatisering B.V. + http://www.interpulse.nl + + Rev 1.1 2004.11.25 06:17:00 PM EDMeester + + Rev 1.0 2002.11.12 10:30:44 PM czhower +} + +unit IdAuthenticationDigest; + +{ + Implementation of the digest authentication as specified in RFC2617 + rev 1.1: Edwin Meester (systeemontwikkeling@interpulse.nl) + Author: Doychin Bondzhev (doychin@dsoft-bg.com) + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAuthentication, + IdException, + IdGlobal, + IdHashMessageDigest, + IdHeaderList; + +type + EIdInvalidAlgorithm = class(EIdException); + + TIdDigestAuthentication = class(TIdAuthentication) + protected + FRealm: String; + FStale: Boolean; + FOpaque: String; + FDomain: TStringList; + FNonce: String; + FNonceCount: integer; + FAlgorithm: String; + FMethod, FUri: string; //needed for digest + FEntityBody: String; //needed for auth-int, Somebody make this nice :D + FQopOptions: TStringList; + FOther: TStringList; + function DoNext: TIdAuthWhatsNext; override; + function GetSteps: Integer; override; + public + constructor Create; override; + destructor Destroy; override; + function Authentication: String; override; + procedure SetRequest(const AMethod, AUri: String); override; + property Method: String read FMethod write FMethod; + property Uri: String read FUri write FUri; + property EntityBody: String read FEntityBody write FEntityBody; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterAuthenticationMethod can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdAuthenticationDigest"'} + {$ENDIF} + +implementation + +uses + IdGlobalProtocols, IdFIPS, IdHash, IdResourceStrings, IdResourceStringsProtocols, + SysUtils; + +{ TIdDigestAuthentication } + +constructor TIdDigestAuthentication.Create; +begin + inherited Create; + CheckMD5Permitted; +end; + +destructor TIdDigestAuthentication.Destroy; +begin + FreeAndNil(FDomain); + FreeAndNil(FQopOptions); + inherited Destroy; +end; + +procedure TIdDigestAuthentication.SetRequest(const AMethod, AUri: String); +begin + FMethod := AMethod; + FUri := AUri; +end; + +function TIdDigestAuthentication.Authentication: String; + + function Hash(const S: String): String; + var + LMD5: TIdHashMessageDigest5; + begin + LMD5 := TIdHashMessageDigest5.Create; + try + Result := LowerCase(LMD5.HashStringAsHex(S)); + finally + LMD5.Free; + end; + end; + +var + LA1, LA2, LCNonce, LResponse, LQop: string; +begin + Result := ''; {do not localize} + + case FCurrentStep of + 0: + begin + //Just be save with this one + Result := 'Digest'; {do not localize} + end; + 1: + begin + //Build request + + LCNonce := Hash(DateTimeToStr(Now)); + + LA1 := Username + ':' + FRealm + ':' + Password; {do not localize} + if TextIsSame(FAlgorithm, 'MD5-sess') then begin {do not localize} + LA1 := Hash(LA1) + ':' + FNonce + ':' + LCNonce; {do not localize} + end; + + LA2 := FMethod + ':' + FUri; {do not localize} + //Qop header present + if FQopOptions.IndexOf('auth-int') > -1 then begin {do not localize} + LQop := 'auth-int'; {do not localize} + LA2 := LA2 + ':' + Hash(FEntityBody); {do not localize} + end + else if FQopOptions.IndexOf('auth') > -1 then begin {do not localize} + LQop := 'auth'; {do not localize} + end; + + if LQop <> '' then begin + LResponse := IntToHex(FNonceCount, 8) + ':' + LCNonce + ':' + LQop + ':'; {do not localize} + end; + LResponse := Hash( Hash(LA1) + ':' + FNonce + ':' + LResponse + Hash(LA2) ); {do not localize} + + Result := 'Digest ' + {do not localize} + 'username="' + Username + '", ' + {do not localize} + 'realm="' + FRealm + '", ' + {do not localize} + 'nonce="' + FNonce + '", ' + {do not localize} + 'algorithm="' + FAlgorithm + '", ' + {do not localize} + 'uri="' + FUri + '", '; + + //Qop header present + if LQop <> '' then begin {do not localize} + Result := Result + + 'qop="' + LQop + '", ' + {do not localize} + 'nc=' + IntToHex(FNonceCount, 8) + ', ' + {do not localize} + 'cnonce="' + LCNonce + '", '; {do not localize} + end; + + Result := Result + 'response="' + LResponse + '"'; {do not localize} + + if FOpaque <> '' then begin + Result := Result + ', opaque="' + FOpaque + '"'; {do not localize} + end; + + Inc(FNonceCount); + FCurrentStep := 0; + end; + end; +end; + +function Unquote(var S: String): String; +var + I, Len: Integer; +begin + Len := Length(S); + I := 2; // skip first quote + while I <= Len do + begin + if S[I] = '"' then begin + Break; + end; + if S[I] = '\' then begin + Inc(I); + end; + Inc(I); + end; + Result := Copy(S, 2, I-2); + S := Copy(S, I+1, MaxInt); +end; + +function TIdDigestAuthentication.DoNext: TIdAuthWhatsNext; +var + S, LName, LValue, LTempNonce: String; + LParams: TStringList; +begin + Result := wnDoRequest; + + case FCurrentStep of + 0: + begin + //gather info + if not Assigned(FDomain) then begin + FDomain := TStringList.Create; + end else begin + FDomain.Clear; + end; + + if not Assigned(FQopOptions) then begin + FQopOptions := TStringList.Create; + end else begin + FQopOptions.Clear; + end; + + S := ReadAuthInfo('Digest'); {do not localize} + Fetch(S); + + LParams := TStringList.Create; + try + while Length(S) > 0 do begin + // RLebeau: Apache sends a space after each comma, but IIS does not! + LName := Trim(Fetch(S, '=')); {do not localize} + S := TrimLeft(S); + if TextStartsWith(S, '"') then begin {do not localize} + LValue := Unquote(S); {do not localize} + Fetch(S, ','); {do not localize} + end else begin + LValue := Trim(Fetch(S, ',')); + end; + LParams.Add(LName + '=' + LValue); + S := TrimLeft(S); + end; + + FRealm := LParams.Values['realm']; {do not localize} + LTempNonce := LParams.Values['nonce']; {do not localize} + if FNonce <> LTempNonce then + begin + FNonceCount := 1; + FNonce := LTempNonce; + end; + + S := LParams.Values['domain']; {do not localize} + while Length(S) > 0 do begin + FDomain.Add(Fetch(S)); + end; + + FOpaque := LParams.Values['opaque']; {do not localize} + FStale := TextIsSame(LParams.Values['stale'], 'True'); {do not localize} + FAlgorithm := LParams.Values['algorithm']; {do not localize} + FQopOptions.CommaText := LParams.Values['qop']; {do not localize} + + if FAlgorithm = '' then begin + FAlgorithm := 'MD5'; {do not localize} + end + else if PosInStrArray(FAlgorithm, ['MD5', 'MD5-sess'], False) = -1 then begin {do not localize} + raise EIdInvalidAlgorithm.Create(RSHTTPAuthInvalidHash); + end; + + finally + FreeAndNil(LParams); + end; + + if Length(Username) > 0 then begin + FCurrentStep := 1; + Result := wnDoRequest; + end else begin + Result := wnAskTheProgram; + end; + end; + end; +end; + +function TIdDigestAuthentication.GetSteps: Integer; +begin + Result := 1; +end; + +initialization + RegisterAuthenticationMethod('Digest', TIdDigestAuthentication); {do not localize} +finalization + UnregisterAuthenticationMethod('Digest'); {do not localize} +end. + diff --git a/indy/Protocols/IdAuthenticationManager.pas b/indy/Protocols/IdAuthenticationManager.pas new file mode 100644 index 0000000..c1fd2ab --- /dev/null +++ b/indy/Protocols/IdAuthenticationManager.pas @@ -0,0 +1,175 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 10/26/2004 10:59:30 PM JPMugaas + Updated ref. + + Rev 1.3 5/29/2004 10:02:20 AM DSiders + Corrected case in Create parameter. + + Rev 1.2 2004.02.03 5:44:54 PM czhower + Name changes + + Rev 1.1 2004.01.21 1:04:52 PM czhower + InitComponenet + + Rev 1.0 11/14/2002 02:13:40 PM JPMugaas +} + +unit IdAuthenticationManager; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAuthentication, + IdBaseComponent, + IdURI; + +type + TIdAuthenticationItem = class(TCollectionItem) + protected + FURI: TIdURI; + FParams: TStrings; + procedure SetParams(const Value: TStrings); + procedure SetURI(const Value: TIdURI); + public + constructor Create(ACollection: TCollection); override; + destructor Destroy; override; + procedure Assign(Source: TPersistent); override; + + property URL: TIdURI read FURI write SetURI; + property Params: TStrings read FParams write SetParams; + end; + + TIdAuthenticationCollection = class(TOwnedCollection) + protected + function GetAuthItem(AIndex: Integer): TIdAuthenticationItem; + procedure SetAuthItem(AIndex: Integer; const Value: TIdAuthenticationItem); + public + function Add: TIdAuthenticationItem; + constructor Create(AOwner: TPersistent); + // + property Items[AIndex: Integer]: TIdAuthenticationItem read GetAuthItem write SetAuthItem; + end; + + TIdAuthenticationManager = class(TIdBaseComponent) + protected + FAuthentications: TIdAuthenticationCollection; + // + procedure InitComponent; override; + public + destructor Destroy; override; + procedure AddAuthentication(AAuthentication: TIdAuthentication; AURL: TIdURI); + property Authentications: TIdAuthenticationCollection read FAuthentications; + end; + +implementation + +uses + IdGlobal, SysUtils; + +{ TIdAuthenticationManager } + +function TIdAuthenticationCollection.Add: TIdAuthenticationItem; +begin + Result := TIdAuthenticationItem(inherited Add); +end; + +constructor TIdAuthenticationCollection.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdAuthenticationItem); +end; + +function TIdAuthenticationCollection.GetAuthItem(AIndex: Integer): TIdAuthenticationItem; +begin + Result := TIdAuthenticationItem(inherited GetItem(AIndex)); +end; + +procedure TIdAuthenticationCollection.SetAuthItem(AIndex: Integer; + const Value: TIdAuthenticationItem); +begin + inherited SetItem(AIndex, Value); +end; + +{ TIdAuthenticationManager } + +procedure TIdAuthenticationManager.AddAuthentication( + AAuthentication: TIdAuthentication; AURL: TIdURI); +var + LItem: TIdAuthenticationItem; +begin + LItem := Authentications.Add; + LItem.URL.URI := AURL.URI; + LItem.Params.Assign(AAuthentication.Params); +end; + +destructor TIdAuthenticationManager.Destroy; +begin + FreeAndNil(FAuthentications); + inherited Destroy; +end; + +procedure TIdAuthenticationManager.InitComponent; +begin + inherited InitComponent; + FAuthentications := TIdAuthenticationCollection.Create(Self); +end; + +{ TIdAuthenticationItem } + +constructor TIdAuthenticationItem.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + FURI := TIdURI.Create; + FParams := TStringList.Create; +end; + +destructor TIdAuthenticationItem.Destroy; +begin + FreeAndNil(FURI); + FreeAndNil(FParams); + inherited Destroy; +end; + +procedure TIdAuthenticationItem.Assign(Source: TPersistent); +var + LSource: TIdAuthenticationItem; +begin + if Source is TIdAuthenticationItem then begin + LSource := TIdAuthenticationItem(Source); + URL.URI := LSource.URL.URI; + Params.Assign(LSource.Params); + end else begin + inherited Assign(Source); + end; +end; + +procedure TIdAuthenticationItem.SetParams(const Value: TStrings); +begin + FParams.Assign(Value); +end; + +procedure TIdAuthenticationItem.SetURI(const Value: TIdURI); +begin + FURI.URI := Value.URI; +end; + +end. diff --git a/indy/Protocols/IdAuthenticationNTLM.pas b/indy/Protocols/IdAuthenticationNTLM.pas new file mode 100644 index 0000000..6ac8f0a --- /dev/null +++ b/indy/Protocols/IdAuthenticationNTLM.pas @@ -0,0 +1,198 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 2004.02.03 5:44:54 PM czhower + Name changes + + Rev 1.2 2/1/2004 3:33:48 AM JPMugaas + Reenabled. Should work in DotNET. + + Rev 1.1 2003.10.12 3:36:26 PM czhower + todo item + + Rev 1.0 11/14/2002 02:13:44 PM JPMugaas +} +{ + + Implementation of the NTLM authentication as specified in + http://www.innovation.ch/java/ntlm.html with some fixes + + Author: Doychin Bondzhev (doychin@dsoft-bg.com) + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. + + S.G. 12/7/2002: Moved the user query one step up: the domain name is required + to properly format the Type 1 message. +} + + +unit IdAuthenticationNTLM; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAuthentication; + +Type + TIdNTLMAuthentication = class(TIdAuthentication) + protected + FNTLMInfo: String; + FHost, FDomain, FUser: String; + function DoNext: TIdAuthWhatsNext; override; + function GetSteps: Integer; override; + procedure SetUserName(const Value: String); override; + public + constructor Create; override; + function Authentication: String; override; + function KeepAlive: Boolean; override; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterAuthenticationMethod can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdAuthenticationNTLM"'} + {$ENDIF} + +implementation + +uses + IdGlobal, + IdGlobalProtocols, + IdException, + IdCoderMIME, + IdResourceStringsOpenSSL, + IdSSLOpenSSLHeaders, + IdSSLOpenSSL, + IdNTLM, + SysUtils; + +{ TIdNTLMAuthentication } + +constructor TIdNTLMAuthentication.Create; +begin + inherited Create; + if not LoadOpenSSLLibrary then begin + raise EIdOSSLCouldNotLoadSSLLibrary.Create(RSOSSLCouldNotLoadSSLLibrary); + end; +end; + +function TIdNTLMAuthentication.DoNext: TIdAuthWhatsNext; +begin + Result := wnDoRequest; + case FCurrentStep of + 0: + begin + if Length(UserName) > 0 then begin + FCurrentStep := 1; + Result := wnDoRequest; + end else begin + Result := wnAskTheProgram; + end; + end; + 1: + begin + FCurrentStep := 2; + Result := wnDoRequest; + end; + 2: + begin + FCurrentStep := 3; + Result := wnDoRequest; + end; + 3: + begin + Reset; + Result := wnFail; + end; + end; +end; + +function TIdNTLMAuthentication.Authentication: String; +var + buf: TIdBytes; + Type2: type_2_message_header; + LDecoder: TIdDecoderMIME; +begin + Result := ''; {do not localize} + SetLength(buf, 0); + + case FCurrentStep of + 1: + begin + FHost := IndyComputerName; + Result := 'NTLM ' + BuildType1Message(FDomain, FHost); {do not localize} + end; + 2: + begin + if Length(FNTLMInfo) = 0 then + begin + FNTLMInfo := ReadAuthInfo('NTLM'); {do not localize} + Fetch(FNTLMInfo); + end; + + if Length(FNTLMInfo) = 0 then + begin + Reset; + Abort; + end; + + LDecoder := TIdDecoderMIME.Create; + try + buf := LDecoder.DecodeBytes(FNTLMInfo); + finally + LDecoder.Free; + end; + BytesToRaw(buf, Type2, SizeOf(Type2)); + + buf := RawToBytes(Type2.Nonce, SizeOf(Type2.Nonce)); + Result := 'NTLM ' + BuildType3Message(FDomain, FHost, FUser, Password, buf); {do not localize} + + FCurrentStep := 2; + end; + end; +end; + +function TIdNTLMAuthentication.KeepAlive: Boolean; +begin + Result := True; +end; + +function TIdNTLMAuthentication.GetSteps: Integer; +begin + Result := 3; +end; + +procedure TIdNTLMAuthentication.SetUserName(const Value: String); +begin + if Value <> Username then + begin + inherited SetUserName(Value); + GetDomain(Username, FUser, FDomain); + end; +end; + +initialization + RegisterAuthenticationMethod('NTLM', TIdNTLMAuthentication); {do not localize} +finalization + UnregisterAuthenticationMethod('NTLM'); {do not localize} +end. diff --git a/indy/Protocols/IdAuthenticationSSPI.pas b/indy/Protocols/IdAuthenticationSSPI.pas new file mode 100644 index 0000000..ea0a44e --- /dev/null +++ b/indy/Protocols/IdAuthenticationSSPI.pas @@ -0,0 +1,1332 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 6/11/2004 9:33:58 AM DSiders + Added "Do not Localize" comments. + + Rev 1.2 13.1.2004 . 17:26:06 DBondzhev + Added Domain property + + Rev 1.1 4/12/2003 10:24:04 PM GGrieve + Fix to Compile + + Rev 1.0 11/14/2002 02:13:50 PM JPMugaas +} + +unit IdAuthenticationSSPI; + +{ + Implementation of the NTLM authentication with SSPI + Author: Alex Brainman + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. +} + +{$DEFINE SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdAuthentication, + IdCoder, + Windows, + SysUtils, + IdSSPI; + +const + SEC_E_OK = 0; + {$EXTERNALSYM SEC_E_OK} + SEC_E_INSUFFICIENT_MEMORY = HRESULT($80090300); + {$EXTERNALSYM SEC_E_INSUFFICIENT_MEMORY} + SEC_E_INVALID_HANDLE = HRESULT($80090301); + {$EXTERNALSYM SEC_E_INVALID_HANDLE} + SEC_E_UNSUPPORTED_FUNCTION = HRESULT($80090302); + {$EXTERNALSYM SEC_E_UNSUPPORTED_FUNCTION} + SEC_E_TARGET_UNKNOWN = HRESULT($80090303); + {$EXTERNALSYM SEC_E_TARGET_UNKNOWN} + SEC_E_INTERNAL_ERROR = HRESULT($80090304); + {$EXTERNALSYM SEC_E_INTERNAL_ERROR} + SEC_E_SECPKG_NOT_FOUND = HRESULT($80090305); + {$EXTERNALSYM SEC_E_SECPKG_NOT_FOUND} + SEC_E_NOT_OWNER = HRESULT($80090306); + {$EXTERNALSYM SEC_E_NOT_OWNER} + SEC_E_CANNOT_INSTALL = HRESULT($80090307); + {$EXTERNALSYM SEC_E_CANNOT_INSTALL} + SEC_E_INVALID_TOKEN = HRESULT($80090308); + {$EXTERNALSYM SEC_E_INVALID_TOKEN} + SEC_E_CANNOT_PACK = HRESULT($80090309); + {$EXTERNALSYM SEC_E_CANNOT_PACK} + SEC_E_QOP_NOT_SUPPORTED = HRESULT($8009030A); + {$EXTERNALSYM SEC_E_QOP_NOT_SUPPORTED} + SEC_E_NO_IMPERSONATION = HRESULT($8009030B); + {$EXTERNALSYM SEC_E_NO_IMPERSONATION} + SEC_E_LOGON_DENIED = HRESULT($8009030C); + {$EXTERNALSYM SEC_E_LOGON_DENIED} + SEC_E_UNKNOWN_CREDENTIALS = HRESULT($8009030D); + {$EXTERNALSYM SEC_E_UNKNOWN_CREDENTIALS} + SEC_E_NO_CREDENTIALS = HRESULT($8009030E); + {$EXTERNALSYM SEC_E_NO_CREDENTIALS} + SEC_E_MESSAGE_ALTERED = HRESULT($8009030F); + {$EXTERNALSYM SEC_E_MESSAGE_ALTERED} + SEC_E_OUT_OF_SEQUENCE = HRESULT($80090310); + {$EXTERNALSYM SEC_E_OUT_OF_SEQUENCE} + SEC_E_NO_AUTHENTICATING_AUTHORITY = HRESULT($80090311); + {$EXTERNALSYM SEC_E_NO_AUTHENTICATING_AUTHORITY} + SEC_I_CONTINUE_NEEDED = HRESULT($00090312); + {$EXTERNALSYM SEC_I_CONTINUE_NEEDED} + SEC_I_COMPLETE_NEEDED = HRESULT($00090313); + {$EXTERNALSYM SEC_I_COMPLETE_NEEDED} + SEC_I_COMPLETE_AND_CONTINUE = HRESULT($00090314); + {$EXTERNALSYM SEC_I_COMPLETE_AND_CONTINUE} + SEC_I_LOCAL_LOGON = HRESULT($00090315); + {$EXTERNALSYM SEC_I_LOCAL_LOGON} + SEC_E_BAD_PKGID = HRESULT($80090316); + {$EXTERNALSYM SEC_E_BAD_PKGID} + SEC_E_CONTEXT_EXPIRED = HRESULT($80090317); + {$EXTERNALSYM SEC_E_CONTEXT_EXPIRED} + SEC_E_INCOMPLETE_MESSAGE = HRESULT($80090318); + {$EXTERNALSYM SEC_E_INCOMPLETE_MESSAGE} + SEC_E_INCOMPLETE_CREDENTIALS = HRESULT($80090320); + {$EXTERNALSYM SEC_E_INCOMPLETE_CREDENTIALS} + SEC_E_BUFFER_TOO_SMALL = HRESULT($80090321); + {$EXTERNALSYM SEC_E_BUFFER_TOO_SMALL} + SEC_I_INCOMPLETE_CREDENTIALS = HRESULT($00090320); + {$EXTERNALSYM SEC_I_INCOMPLETE_CREDENTIALS} + SEC_I_RENEGOTIATE = HRESULT($00090321); + {$EXTERNALSYM SEC_I_RENEGOTIATE} + SEC_E_WRONG_PRINCIPAL = HRESULT($80090322); + {$EXTERNALSYM SEC_E_WRONG_PRINCIPAL} + SEC_I_NO_LSA_CONTEXT = HRESULT($00090323); + {$EXTERNALSYM SEC_I_NO_LSA_CONTEXT} + SEC_E_TIME_SKEW = HRESULT($80090324); + {$EXTERNALSYM SEC_E_TIME_SKEW} + SEC_E_UNTRUSTED_ROOT = HRESULT($80090325); + {$EXTERNALSYM SEC_E_UNTRUSTED_ROOT} + SEC_E_ILLEGAL_MESSAGE = HRESULT($80090326); + {$EXTERNALSYM SEC_E_ILLEGAL_MESSAGE} + SEC_E_CERT_UNKNOWN = HRESULT($80090327); + {$EXTERNALSYM SEC_E_CERT_UNKNOWN} + SEC_E_CERT_EXPIRED = HRESULT($80090328); + {$EXTERNALSYM SEC_E_CERT_EXPIRED} + SEC_E_ENCRYPT_FAILURE = HRESULT($80090329); + {$EXTERNALSYM SEC_E_ENCRYPT_FAILURE} + SEC_E_DECRYPT_FAILURE = HRESULT($80090330); + {$EXTERNALSYM SEC_E_DECRYPT_FAILURE} + SEC_E_ALGORITHM_MISMATCH = HRESULT($80090331); + {$EXTERNALSYM SEC_E_ALGORITHM_MISMATCH} + SEC_E_SECURITY_QOS_FAILED = HRESULT($80090332); + {$EXTERNALSYM SEC_E_SECURITY_QOS_FAILED} + + SEC_E_UNFINISHED_CONTEXT_DELETED = HRESULT($80090333); + {$EXTERNALSYM SEC_E_UNFINISHED_CONTEXT_DELETED} + SEC_E_NO_TGT_REPLY = HRESULT($80090334); + {$EXTERNALSYM SEC_E_NO_TGT_REPLY} + SEC_E_NO_IP_ADDRESSES = HRESULT($80090335); + {$EXTERNALSYM SEC_E_NO_IP_ADDRESSES} + SEC_E_WRONG_CREDENTIAL_HANDLE = HRESULT($80090336); + {$EXTERNALSYM SEC_E_WRONG_CREDENTIAL_HANDLE} + SEC_E_CRYPTO_SYSTEM_INVALID = HRESULT($80090337); + {$EXTERNALSYM SEC_E_CRYPTO_SYSTEM_INVALID} + SEC_E_MAX_REFERRALS_EXCEEDED = HRESULT($80090338); + {$EXTERNALSYM SEC_E_MAX_REFERRALS_EXCEEDED} + SEC_E_MUST_BE_KDC = HRESULT($80090339); + {$EXTERNALSYM SEC_E_MUST_BE_KDC} + SEC_E_STRONG_CRYPTO_NOT_SUPPORTED = HRESULT($8009033A); + {$EXTERNALSYM SEC_E_STRONG_CRYPTO_NOT_SUPPORTED} + SEC_E_TOO_MANY_PRINCIPALS = HRESULT($8009033B); + {$EXTERNALSYM SEC_E_TOO_MANY_PRINCIPALS} + SEC_E_NO_PA_DATA = HRESULT($8009033C); + {$EXTERNALSYM SEC_E_NO_PA_DATA} + SEC_E_PKINIT_NAME_MISMATCH = HRESULT($8009033D); + {$EXTERNALSYM SEC_E_PKINIT_NAME_MISMATCH} + SEC_E_SMARTCARD_LOGON_REQUIRED = HRESULT($8009033E); + {$EXTERNALSYM SEC_E_SMARTCARD_LOGON_REQUIRED} + SEC_E_SHUTDOWN_IN_PROGRESS = HRESULT($8009033F); + {$EXTERNALSYM SEC_E_SHUTDOWN_IN_PROGRESS} + SEC_E_KDC_INVALID_REQUEST = HRESULT($80090340); + {$EXTERNALSYM SEC_E_KDC_INVALID_REQUEST} + SEC_E_KDC_UNABLE_TO_REFER = HRESULT($80090341); + {$EXTERNALSYM SEC_E_KDC_UNABLE_TO_REFER} + SEC_E_KDC_UNKNOWN_ETYPE = HRESULT($80090342); + {$EXTERNALSYM SEC_E_KDC_UNKNOWN_ETYPE} + SEC_E_UNSUPPORTED_PREAUTH = HRESULT($80090343); + {$EXTERNALSYM SEC_E_UNSUPPORTED_PREAUTH} + SEC_E_DELEGATION_REQUIRED = HRESULT($80090345); + {$EXTERNALSYM SEC_E_DELEGATION_REQUIRED} + SEC_E_BAD_BINDINGS = HRESULT($80090346); + {$EXTERNALSYM SEC_E_BAD_BINDINGS} + SEC_E_MULTIPLE_ACCOUNTS = HRESULT($80090347); + {$EXTERNALSYM SEC_E_MULTIPLE_ACCOUNTS} + SEC_E_NO_KERB_KEY = HRESULT($80090348); + {$EXTERNALSYM SEC_E_NO_KERB_KEY} + SEC_E_CERT_WRONG_USAGE = HRESULT($80090349); + {$EXTERNALSYM SEC_E_CERT_WRONG_USAGE} + SEC_E_DOWNGRADE_DETECTED = HRESULT($80090350); + {$EXTERNALSYM SEC_E_DOWNGRADE_DETECTED} + SEC_E_SMARTCARD_CERT_REVOKED = HRESULT($80090351); + {$EXTERNALSYM SEC_E_SMARTCARD_CERT_REVOKED} + SEC_E_ISSUING_CA_UNTRUSTED = HRESULT($80090352); + {$EXTERNALSYM SEC_E_ISSUING_CA_UNTRUSTED} + SEC_E_REVOCATION_OFFLINE_C = HRESULT($80090353); + {$EXTERNALSYM SEC_E_REVOCATION_OFFLINE_C} + SEC_E_PKINIT_CLIENT_FAILURE = HRESULT($80090354); + {$EXTERNALSYM SEC_E_PKINIT_CLIENT_FAILURE} + SEC_E_SMARTCARD_CERT_EXPIRED = HRESULT($80090355); + {$EXTERNALSYM SEC_E_SMARTCARD_CERT_EXPIRED} + SEC_E_NO_S4U_PROT_SUPPORT = HRESULT($80090356); + {$EXTERNALSYM SEC_E_NO_S4U_PROT_SUPPORT} + SEC_E_CROSSREALM_DELEGATION_FAILURE = HRESULT($80090357); + {$EXTERNALSYM SEC_E_CROSSREALM_DELEGATION_FAILURE} + SEC_E_REVOCATION_OFFLINE_KDC = HRESULT($80090358); + {$EXTERNALSYM SEC_E_REVOCATION_OFFLINE_KDC} + SEC_E_ISSUING_CA_UNTRUSTED_KDC = HRESULT($80090359); + {$EXTERNALSYM SEC_E_ISSUING_CA_UNTRUSTED_KDC} + SEC_E_KDC_CERT_EXPIRED = HRESULT($8009035A); + {$EXTERNALSYM SEC_E_KDC_CERT_EXPIRED} + SEC_E_KDC_CERT_REVOKED = HRESULT($8009035B); + {$EXTERNALSYM SEC_E_KDC_CERT_REVOKED} + SEC_I_SIGNATURE_NEEDED = HRESULT($0009035C); + {$EXTERNALSYM SEC_I_SIGNATURE_NEEDED} + SEC_E_INVALID_PARAMETER = HRESULT($8009035D); + {$EXTERNALSYM SEC_E_INVALID_PARAMETER} + SEC_E_DELEGATION_POLICY = HRESULT($8009035E); + {$EXTERNALSYM SEC_E_DELEGATION_POLICY} + SEC_E_POLICY_NLTM_ONLY = HRESULT($8009035F); + {$EXTERNALSYM SEC_E_POLICY_NLTM_ONLY} + SEC_I_NO_RENEGOTIATION = HRESULT($00090360); + {$EXTERNALSYM SEC_I_NO_RENEGOTIATION} + SEC_E_NO_CONTEXT = HRESULT($80090361); + {$EXTERNALSYM SEC_E_NO_CONTEXT} + SEC_E_PKU2U_CERT_FAILURE = HRESULT($80090362); + {$EXTERNALSYM SEC_E_PKU2U_CERT_FAILURE} + SEC_E_MUTUAL_AUTH_FAILED = HRESULT($80090363); + {$EXTERNALSYM SEC_E_MUTUAL_AUTH_FAILED} + +type + ESSPIException = class(Exception) + public + // Params must be in this order to avoid conflict with CreateHelp + // constructor in CBuilder as CB does not differentiate constructors + // by name as Delphi does + constructor CreateError(const AErrorNo: Integer; const AFailedFuncName: string); + // + class function GetErrorMessageByNo(AErrorNo: UInt32): string; + end; + + ESSPIInterfaceInitFailed = class(ESSPIException); + + { TSSPIInterface } + + TSSPIInterface = class(TObject) + private + fLoadPending, fIsAvailable: Boolean; + fPFunctionTable: PSecurityFunctionTable; + fDLLHandle: THandle; + procedure ReleaseFunctionTable; + procedure CheckAvailable; + function GetFunctionTable: SecurityFunctionTable; + public + class procedure RaiseIfError(aStatus: SECURITY_STATUS; const aFunctionName: string); + function IsAvailable: Boolean; + property FunctionTable: SecurityFunctionTable read GetFunctionTable; + public + constructor Create; + destructor Destroy; override; + end; + + { TSSPIPackages } + + TSSPIPackage = class(TObject) + private + fPSecPkginfo: PSecPkgInfo; + function GetPSecPkgInfo: PSecPkgInfo; + function GetMaxToken: ULONG; + function GetName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}; + public + property MaxToken: ULONG read GetMaxToken; + property Name: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF} read GetName; + public + constructor Create(aPSecPkginfo: PSecPkgInfo); + end; + + TCustomSSPIPackage = class(TSSPIPackage) + private + fInfo: PSecPkgInfo; + public + constructor Create(const aPkgName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}); + destructor Destroy; override; + end; + + TSSPINTLMPackage = class(TCustomSSPIPackage) + public + constructor Create; + end; + + { TSSPICredentials } + + TSSPICredentialsUse = (scuInBound, scuOutBound, scuBoth); + + TSSPICredentials = class(TObject) + private + fPackage: TSSPIPackage; + fHandle: CredHandle; + fUse: TSSPICredentialsUse; + fAcquired: Boolean; + fExpiry: TimeStamp; + function GetHandle: PCredHandle; + procedure SetUse(aValue: TSSPICredentialsUse); + protected + procedure CheckAcquired; + procedure CheckNotAcquired; + procedure DoAcquire(pszPrincipal: {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}; pvLogonId, pAuthData: PVOID); + procedure DoRelease; virtual; + public + procedure Release; + property Package: TSSPIPackage read fPackage; + property Handle: PCredHandle read GetHandle; + property Use: TSSPICredentialsUse read fUse write SetUse; + property Acquired: Boolean read fAcquired; + public + constructor Create(aPackage: TSSPIPackage); + destructor Destroy; override; + end; + + { TSSPIWinNTCredentials } + + TSSPIWinNTCredentials = class(TSSPICredentials) + protected + public + procedure Acquire(aUse: TSSPICredentialsUse); overload; + procedure Acquire(aUse: TSSPICredentialsUse; + const aDomain, aUserName, aPassword: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}); overload; + end; + + { TSSPIContext } + + TSSPIContext = class(TObject) + private + fCredentials: TSSPICredentials; + fHandle: CtxtHandle; + fHasHandle: Boolean; + fExpiry: TimeStamp; + function GetHandle: PCtxtHandle; + function GetExpiry: TimeStamp; + procedure UpdateHasContextAndCheckForError( + const aFuncResult: SECURITY_STATUS; const aFuncName: string; + const aErrorsToIgnore: array of SECURITY_STATUS); + protected + procedure CheckHasHandle; + procedure CheckCredentials; + function DoInitialize(const aTokenSourceName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}; + var aIn, aOut: SecBufferDesc; + const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS; + procedure DoRelease; virtual; + function GetRequestedFlags: ULONG; virtual; abstract; + procedure SetEstablishedFlags(aFlags: ULONG); virtual; abstract; + function GetAuthenticated: Boolean; virtual; abstract; + property HasHandle: Boolean read fHasHandle; + public + procedure Release; + property Credentials: TSSPICredentials read fCredentials; + property Handle: PCtxtHandle read GetHandle; + property Authenticated: Boolean read GetAuthenticated; + property Expiry: TimeStamp read GetExpiry; + public + constructor Create(aCredentials: TSSPICredentials); + destructor Destroy; override; + end; + + { TSSPIConnectionContext } + + TCustomSSPIConnectionContext = class(TSSPIContext) + private + fStatus: SECURITY_STATUS; + fOutBuffDesc, fInBuffDesc: SecBufferDesc; + fInBuff: SecBuffer; + protected + procedure DoRelease; override; + function GetAuthenticated: Boolean; override; + function DoUpdateAndGenerateReply(var aIn, aOut: SecBufferDesc; + const aErrorsToIgnore: array of SECURITY_STATUS + ): SECURITY_STATUS; virtual; abstract; + public + constructor Create(ACredentials: TSSPICredentials); + function UpdateAndGenerateReply( + const aFromPeerToken: TIdBytes; var aToPeerToken: TIdBytes): Boolean; + end; + + TSSPIClientConnectionContext = class(TCustomSSPIConnectionContext) + private + fTargetName: string; + fReqReguested, fReqEstablished: ULONG; + protected + function GetRequestedFlags: ULONG; override; + procedure SetEstablishedFlags(aFlags: ULONG); override; + function DoUpdateAndGenerateReply(var aIn, aOut: SecBufferDesc; + const aErrorsToIgnore: array of SECURITY_STATUS + ): SECURITY_STATUS; override; + public + function GenerateInitialChallenge(const aTargetName: string; + var aToPeerToken: TIdBytes): Boolean; + public + constructor Create(aCredentials: TSSPICredentials); + end; + + TIndySSPINTLMClient = class(TObject) + protected + fNTLMPackage: TSSPINTLMPackage; + fCredentials: TSSPIWinNTCredentials; + fContext: TSSPIClientConnectionContext; + public + procedure SetCredentials(const aDomain, aUserName, aPassword: string); + procedure SetCredentialsAsCurrentUser; + function InitAndBuildType1Message: TIdBytes; + function UpdateAndBuildType3Message(const aServerType2Message: TIdBytes): TIdBytes; + public + constructor Create; + destructor Destroy; override; + end; + + TIdSSPINTLMAuthentication = class(TIdAuthentication) + protected + FNTLMInfo: string; + FSSPIClient: TIndySSPINTLMClient; + procedure SetDomain(const Value: String); + function GetDomain: String; + procedure SetUserName(const Value: String); override; + function GetSteps: Integer; override; + function DoNext: TIdAuthWhatsNext; override; + public + constructor Create; override; + destructor Destroy; override; + function Authentication: string; override; + function KeepAlive: Boolean; override; + property Domain: String read GetDomain write SetDomain; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterAuthenticationMethod can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdAuthenticationSSPI"'} + {$ENDIF} + +implementation + +uses + IdGlobalCore, + IdGlobalProtocols, + IdException, + IdCoderMIME, + IdResourceStringsSSPI, + IdHeaderList; + +var + gSSPIInterface: TSSPIInterface = nil; + gAuthRegistered: Boolean = False; + +{ ESSPIException } + +class function ESSPIException.GetErrorMessageByNo(aErrorNo: UInt32): string; +begin + case HRESULT(aErrorNo) of + SEC_E_OK: + Result := RSHTTPSSPISuccess; + SEC_E_INSUFFICIENT_MEMORY: + Result := RSHTTPSSPINotEnoughMem; + SEC_E_INVALID_HANDLE: + Result := RSHTTPSSPIInvalidHandle; + SEC_E_UNSUPPORTED_FUNCTION: + Result := RSHTTPSSPIFuncNotSupported; + SEC_E_TARGET_UNKNOWN: + Result := RSHTTPSSPIUnknownTarget; + SEC_E_INTERNAL_ERROR: + Result := RSHTTPSSPIInternalError; + SEC_E_SECPKG_NOT_FOUND: + Result := RSHTTPSSPISecPackageNotFound; + SEC_E_NOT_OWNER: + Result := RSHTTPSSPINotOwner; + SEC_E_CANNOT_INSTALL: + Result := RSHTTPSSPIPackageCannotBeInstalled; + SEC_E_INVALID_TOKEN: + Result := RSHTTPSSPIInvalidToken; + SEC_E_CANNOT_PACK: + Result := RSHTTPSSPICannotPack; + SEC_E_QOP_NOT_SUPPORTED: + Result := RSHTTPSSPIQOPNotSupported; + SEC_E_NO_IMPERSONATION: + Result := RSHTTPSSPINoImpersonation; + SEC_E_LOGON_DENIED: + Result := RSHTTPSSPILoginDenied; + SEC_E_UNKNOWN_CREDENTIALS: + Result := RSHTTPSSPIUnknownCredentials; + SEC_E_NO_CREDENTIALS: + Result := RSHTTPSSPINoCredentials; + SEC_E_MESSAGE_ALTERED: + Result := RSHTTPSSPIMessageAltered; + SEC_E_OUT_OF_SEQUENCE: + Result := RSHTTPSSPIOutOfSequence; + SEC_E_NO_AUTHENTICATING_AUTHORITY: + Result := RSHTTPSSPINoAuthAuthority; + SEC_I_CONTINUE_NEEDED: + Result := RSHTTPSSPIContinueNeeded; + SEC_I_COMPLETE_NEEDED: + Result := RSHTTPSSPICompleteNeeded; + SEC_I_COMPLETE_AND_CONTINUE: + Result :=RSHTTPSSPICompleteContinueNeeded; + SEC_I_LOCAL_LOGON: + Result := RSHTTPSSPILocalLogin; + SEC_E_BAD_PKGID: + Result := RSHTTPSSPIBadPackageID; + SEC_E_CONTEXT_EXPIRED: + Result := RSHTTPSSPIContextExpired; + SEC_E_INCOMPLETE_MESSAGE: + Result := RSHTTPSSPIIncompleteMessage; + SEC_E_INCOMPLETE_CREDENTIALS: + Result := RSHTTPSSPIIncompleteCredentialNotInit; + SEC_E_BUFFER_TOO_SMALL: + Result := RSHTTPSSPIBufferTooSmall; + SEC_I_INCOMPLETE_CREDENTIALS: + Result := RSHTTPSSPIIncompleteCredentialsInit; + SEC_I_RENEGOTIATE: + Result := RSHTTPSSPIRengotiate; + SEC_E_WRONG_PRINCIPAL: + Result := RSHTTPSSPIWrongPrincipal; + SEC_I_NO_LSA_CONTEXT: + Result := RSHTTPSSPINoLSACode; + SEC_E_TIME_SKEW: + Result := RSHTTPSSPITimeScew; + SEC_E_UNTRUSTED_ROOT: + Result := RSHTTPSSPIUntrustedRoot; + SEC_E_ILLEGAL_MESSAGE: + Result := RSHTTPSSPIIllegalMessage; + SEC_E_CERT_UNKNOWN: + Result := RSHTTPSSPICertUnknown; + SEC_E_CERT_EXPIRED: + Result := RSHTTPSSPICertExpired; + SEC_E_ENCRYPT_FAILURE: + Result := RSHTTPSSPIEncryptionFailure; + SEC_E_DECRYPT_FAILURE: + Result := RSHTTPSSPIDecryptionFailure; + SEC_E_ALGORITHM_MISMATCH: + Result := RSHTTPSSPIAlgorithmMismatch; + SEC_E_SECURITY_QOS_FAILED: + Result := RSHTTPSSPISecurityQOSFailure; + SEC_E_UNFINISHED_CONTEXT_DELETED : + Result := RSHTTPSSPISecCtxWasDelBeforeUpdated; + SEC_E_NO_TGT_REPLY : + Result := RSHTTPSSPIClientNoTGTReply; + SEC_E_NO_IP_ADDRESSES : + Result := RSHTTPSSPILocalNoIPAddr; + SEC_E_WRONG_CREDENTIAL_HANDLE : + Result := RSHTTPSSPIWrongCredHandle; + SEC_E_CRYPTO_SYSTEM_INVALID : + Result := RSHTTPSSPICryptoSysInvalid; + SEC_E_MAX_REFERRALS_EXCEEDED : + Result := RSHTTPSSPIMaxTicketRef; + SEC_E_MUST_BE_KDC : + Result := RSHTTPSSPIMustBeKDC; + SEC_E_STRONG_CRYPTO_NOT_SUPPORTED : + Result := RSHTTPSSPIStrongCryptoNotSupported; + SEC_E_TOO_MANY_PRINCIPALS : + Result := RSHTTPSSPIKDCReplyTooManyPrincipals; + SEC_E_NO_PA_DATA : + Result := RSHTTPSSPINoPAData; + SEC_E_PKINIT_NAME_MISMATCH : + Result := RSHTTPSSPIPKInitNameMismatch; + SEC_E_SMARTCARD_LOGON_REQUIRED : + Result := RSHTTPSSPISmartcardLogonReq; + SEC_E_SHUTDOWN_IN_PROGRESS : + Result := RSHTTPSSPISysShutdownInProg; + SEC_E_KDC_INVALID_REQUEST : + Result := RSHTTPSSPIKDCInvalidRequest; + SEC_E_KDC_UNABLE_TO_REFER : + Result := RSHTTPSSPIKDCUnableToRefer; + SEC_E_KDC_UNKNOWN_ETYPE : + Result := RSHTTPSSPIKDCETypeUnknown; + SEC_E_UNSUPPORTED_PREAUTH : + Result := RSHTTPSSPIUnsupPreauth; + SEC_E_DELEGATION_REQUIRED : + Result := RSHTTPSSPIDeligationReq; + SEC_E_BAD_BINDINGS : + Result := RSHTTPSSPIBadBindings; + SEC_E_MULTIPLE_ACCOUNTS : + Result := RSHTTPSSPIMultipleAccounts; + SEC_E_NO_KERB_KEY : + Result := RSHTTPSSPINoKerbKey; + SEC_E_CERT_WRONG_USAGE : + Result := RSHTTPSSPICertWrongUsage; + SEC_E_DOWNGRADE_DETECTED : + Result := RSHTTPSSPIDowngradeDetected; + SEC_E_SMARTCARD_CERT_REVOKED : + Result := RSHTTPSSPISmartcardCertRevoked; + SEC_E_ISSUING_CA_UNTRUSTED : + Result := RSHTTPSSPIIssuingCAUntrusted; + SEC_E_REVOCATION_OFFLINE_C : + Result := RSHTTPSSPIRevocationOffline; + SEC_E_PKINIT_CLIENT_FAILURE : + Result := RSHTTPSSPIPKInitClientFailure; + SEC_E_SMARTCARD_CERT_EXPIRED : + Result := RSHTTPSSPISmartcardExpired; + SEC_E_NO_S4U_PROT_SUPPORT : + Result := RSHTTPSSPINoS4UProtSupport; + SEC_E_CROSSREALM_DELEGATION_FAILURE : + Result := RSHTTPSSPICrossRealmDeligationFailure; + SEC_E_REVOCATION_OFFLINE_KDC : + Result := RSHTTPSSPIRevocationOfflineKDC; + SEC_E_ISSUING_CA_UNTRUSTED_KDC : + Result := RSHTTPSSPICAUntrustedKDC; + SEC_E_KDC_CERT_EXPIRED : + Result := RSHTTPSSPIKDCCertExpired; + SEC_E_KDC_CERT_REVOKED : + Result := RSHTTPSSPIKDCCertRevoked; + SEC_I_SIGNATURE_NEEDED : + Result := RSHTTPSSPISignatureNeeded; + SEC_E_INVALID_PARAMETER : + Result := RSHTTPSSPIInvalidParameter; + SEC_E_DELEGATION_POLICY : + Result := RSHTTPSSPIDeligationPolicy; + SEC_E_POLICY_NLTM_ONLY : + Result := RSHTTPSSPIPolicyNTLMOnly; + SEC_I_NO_RENEGOTIATION : + Result := RSHTTPSSPINoRenegotiation; + SEC_E_NO_CONTEXT : + Result := RSHTTPSSPINoContext; + SEC_E_PKU2U_CERT_FAILURE : + Result := RSHTTPSSPIPKU2UCertFailure; + SEC_E_MUTUAL_AUTH_FAILED : + Result := RSHTTPSSPIMutualAuthFailed; + else + Result := RSHTTPSSPIUnknwonError; + end; +end; + +constructor ESSPIException.CreateError(const AErrorNo: Integer; const AFailedFuncName: string); +begin + if AErrorNo = SEC_E_OK then begin + inherited Create(AFailedFuncName); + end else begin + inherited CreateFmt(RSHTTPSSPIErrorMsg, + [AFailedFuncName, AErrorNo, AErrorNo, GetErrorMessageByNo(AErrorNo)]); + end; +end; + +{ TSSPIInterface } + +procedure TSSPIInterface.ReleaseFunctionTable; +begin + if fPFunctionTable <> nil then begin + fPFunctionTable := nil; + end; +end; + +procedure TSSPIInterface.CheckAvailable; +begin + if not IsAvailable then begin + raise ESSPIInterfaceInitFailed.Create(RSHTTPSSPIInterfaceInitFailed); + end; +end; + +function TSSPIInterface.GetFunctionTable: SecurityFunctionTable; +begin + CheckAvailable; + Result := fPFunctionTable^; +end; + +class procedure TSSPIInterface.RaiseIfError(aStatus: SECURITY_STATUS; + const aFunctionName: string); +begin + if not SEC_SUCCESS(aStatus) then begin + raise ESSPIException.CreateError(aStatus, aFunctionName); + end; +end; + +function TSSPIInterface.IsAvailable: Boolean; + + procedure LoadDLL; + const + SECURITY_DLL_NT = 'security.dll'; {Do not translate} + SECURITY_DLL_95 = 'secur32.dll'; {Do not translate} + ENCRYPT_MESSAGE = 'EncryptMessage'; {Do not translate} + DECRYPT_MESSAGE = 'DecryptMessage'; {Do not translate} + var + dllName: string; + entrypoint: INIT_SECURITY_INTERFACE; + begin + fIsAvailable := False; + if IndyWindowsPlatform = VER_PLATFORM_WIN32_WINDOWS then + { Windows95 SSPI dll } + dllName := SECURITY_DLL_95 + else + { WindowsNT & Windows2000 SSPI dll } + dllName := SECURITY_DLL_NT; + { load SSPI dll } + //In Windows, you should use SafeLoadLibrary instead of the LoadLibrary API + //call because LoadLibrary messes with the FPU control word. + fDLLHandle := SafeLoadLibrary(dllName); + if fDLLHandle > 0 then begin + { get InitSecurityInterface entry point + and call it to fetch SPPI function table} + entrypoint := GetProcAddress(fDLLHandle, SECURITY_ENTRYPOINT); + fPFunctionTable := entrypoint(); + { let's see what SSPI functions are available + and if we can continue on with the set } + fIsAvailable := + Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.QuerySecurityPackageInfoW{$ELSE}fPFunctionTable^.QuerySecurityPackageInfoA{$ENDIF}) and + Assigned(fPFunctionTable^.FreeContextBuffer) and + Assigned(fPFunctionTable^.DeleteSecurityContext) and + Assigned(fPFunctionTable^.FreeCredentialsHandle) and + Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.AcquireCredentialsHandleW{$ELSE}fPFunctionTable^.AcquireCredentialsHandleA{$ENDIF}) and + Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.InitializeSecurityContextW{$ELSE}fPFunctionTable^.InitializeSecurityContextA{$ENDIF}) and + Assigned(fPFunctionTable^.AcceptSecurityContext) and + Assigned(fPFunctionTable^.ImpersonateSecurityContext) and + Assigned(fPFunctionTable^.RevertSecurityContext) and + Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.QueryContextAttributesW{$ELSE}fPFunctionTable^.QueryContextAttributesA{$ENDIF}) and + Assigned(fPFunctionTable^.MakeSignature) and + Assigned(fPFunctionTable^.VerifySignature); + {$IFDEF SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE} + { fudge for Encrypt/DecryptMessage } + if not Assigned(fPFunctionTable^.EncryptMessage) then begin + fPFunctionTable^.EncryptMessage := GetProcAddress(fDLLHandle, ENCRYPT_MESSAGE); + end; + if not Assigned(fPFunctionTable^.DecryptMessage) then begin + fPFunctionTable^.DecryptMessage := GetProcAddress(fDLLHandle, DECRYPT_MESSAGE); + end; + {$ENDIF} + end; + end; + +begin + if not fIsAvailable then begin + if fLoadPending then begin + ReleaseFunctionTable; + LoadDLL; + fLoadPending := False; + end; + end; + Result := fIsAvailable; +end; + +constructor TSSPIInterface.Create; +begin + inherited Create; + fLoadPending := True; + fIsAvailable := False; + fPFunctionTable := nil; +end; + +destructor TSSPIInterface.Destroy; +begin + ReleaseFunctionTable; + FreeLibrary(fDLLHandle); + inherited Destroy; +end; + +{ TSSPIPackage } + +constructor TSSPIPackage.Create(aPSecPkginfo: PSecPkgInfo); +begin + inherited Create; + fPSecPkginfo := aPSecPkginfo; +end; + +function TSSPIPackage.GetPSecPkgInfo: PSecPkgInfo; +begin + if not Assigned(fPSecPkginfo) then begin + raise ESSPIException.Create(RSHTTPSSPINoPkgInfoSpecified); + end; + Result := fPSecPkginfo; +end; + +function TSSPIPackage.GetMaxToken: ULONG; +begin + Result := GetPSecPkgInfo^.cbMaxToken; +end; + +function TSSPIPackage.GetName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}; +begin + Result := GetPSecPkgInfo^.Name; +end; + +{ TCustomSSPIPackage } + +constructor TCustomSSPIPackage.Create(const aPkgName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}); +begin + gSSPIInterface.RaiseIfError( + {$IFDEF SSPI_UNICODE} + gSSPIInterface.FunctionTable.QuerySecurityPackageInfoW(PWideChar(aPkgName), @fInfo), + 'QuerySecurityPackageInfoW' {Do not translate} + {$ELSE} + gSSPIInterface.FunctionTable.QuerySecurityPackageInfoA(PAnsiChar(aPkgName), @fInfo), + 'QuerySecurityPackageInfoA' {Do not translate} + {$ENDIF} + ); + inherited Create(fInfo); +end; + +destructor TCustomSSPIPackage.Destroy; +begin + if fInfo <> nil then begin + gSSPIInterface.RaiseIfError( + gSSPIInterface.FunctionTable.FreeContextBuffer(fInfo), 'FreeContextBuffer'); {Do not localize} + end; + inherited Destroy; +end; + +{ TSSPINTLMPackage } + +constructor TSSPINTLMPackage.Create; +begin + inherited Create(NTLMSP_NAME); +end; + +{ TSSPICredentials } + +constructor TSSPICredentials.Create(aPackage: TSSPIPackage); +begin + inherited Create; + fPackage := aPackage; + fUse := scuOutBound; + fAcquired := False; +end; + +procedure TSSPICredentials.CheckAcquired; +begin + if not fAcquired then begin + raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle); + end; +end; + +procedure TSSPICredentials.CheckNotAcquired; +begin + if fAcquired then begin + raise ESSPIException.Create(RSHTTPSSPICanNotChangeCredentials); + end; +end; + +procedure TSSPICredentials.DoAcquire + (pszPrincipal: {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}; pvLogonId, pAuthData: PVOID); +var + cu: ULONG; +begin + Release; + case Use of + scuInBound: + cu := SECPKG_CRED_INBOUND; + scuOutBound: + cu := SECPKG_CRED_OUTBOUND; + scuBoth: + cu := SECPKG_CRED_BOTH; + else + raise ESSPIException.Create(RSHTTPSSPIUnknwonCredentialUse); + end; + gSSPIInterface.RaiseIfError( + gSSPIInterface.FunctionTable.{$IFDEF SSPI_UNICODE}AcquireCredentialsHandleW{$ELSE}AcquireCredentialsHandleA{$ENDIF}( + pszPrincipal, {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}(Package.Name), cu, pvLogonId, pAuthData, nil, nil, + @fHandle, @fExpiry), + {$IFDEF SSPI_UNICODE} + 'AcquireCredentialsHandleW' {Do not translater} + {$ELSE} + 'AcquireCredentialsHandleA' {Do not translater} + {$ENDIF} + ); + fAcquired := True; +end; + +procedure TSSPICredentials.DoRelease; +begin + gSSPIInterface.RaiseIfError( + gSSPIInterface.FunctionTable.FreeCredentialsHandle(@fHandle), + 'FreeCredentialsHandle'); {Do not translate} + SecInvalidateHandle(fHandle); +end; + +procedure TSSPICredentials.Release; +begin + if fAcquired then begin + DoRelease; + fAcquired := False; + end; +end; + +function TSSPICredentials.GetHandle: PCredHandle; +begin + CheckAcquired; + Result := @fHandle; +end; + +procedure TSSPICredentials.SetUse(aValue: TSSPICredentialsUse); +begin + if fUse <> aValue then begin + CheckNotAcquired; + fUse := aValue; + end; +end; + +destructor TSSPICredentials.Destroy; +begin + Release; + inherited Destroy; +end; + +{ TSSPIWinNTCredentials } + +procedure TSSPIWinNTCredentials.Acquire(aUse: TSSPICredentialsUse); +begin + Acquire(aUse, '', '', ''); {Do not translate} +end; + +procedure TSSPIWinNTCredentials.Acquire(aUse: TSSPICredentialsUse; + const aDomain, aUserName, aPassword: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}); +var + ai: SEC_WINNT_AUTH_IDENTITY; + pai: PVOID; +begin + Use := aUse; + if (Length(aDomain) > 0) and (Length(aUserName) > 0) then begin + {$IFDEF SSPI_UNICODE} + ai.User := PUSHORT(PWideChar(aUserName)); + ai.UserLength := Length(aUserName); + ai.Domain := PUSHORT(PWideChar(aDomain)); + ai.DomainLength := Length(aDomain); + ai.Password := PUSHORT(PWideChar(aPassword)); + ai.PasswordLength := Length(aPassword); + ai.Flags := SEC_WINNT_AUTH_IDENTITY_UNICODE; + {$ELSE} + ai.User := PUCHAR(PAnsiChar(aUserName)); + ai.UserLength := Length(aUserName); + ai.Domain := PUCHAR(PAnsiChar(aDomain)); + ai.DomainLength := Length(aDomain); + ai.Password := PUCHAR(PAnsiChar(aPassword)); + ai.PasswordLength := Length(aPassword); + ai.Flags := SEC_WINNT_AUTH_IDENTITY_ANSI; + {$ENDIF} + pai := @ai; + end else + begin + pai := nil; + end; + DoAcquire(nil, nil, pai); +end; + +{ TSSPIContext } + +constructor TSSPIContext.Create(aCredentials: TSSPICredentials); +begin + inherited Create; + fCredentials := aCredentials; + fHasHandle := False; +end; + +destructor TSSPIContext.Destroy; +begin + Release; + inherited Destroy; +end; + +procedure TSSPIContext.UpdateHasContextAndCheckForError( + const aFuncResult: SECURITY_STATUS; const aFuncName: string; + const aErrorsToIgnore: array of SECURITY_STATUS); +var + doRaise: Boolean; + i: Integer; +begin + doRaise := not SEC_SUCCESS(aFuncResult); + if doRaise then begin + for i := Low(aErrorsToIgnore) to High(aErrorsToIgnore) do begin + if aFuncResult = aErrorsToIgnore[i] then begin + doRaise := False; + Break; + end; + end; + end; + if doRaise then begin + raise ESSPIException.CreateError(aFuncResult, aFuncName); + end; + fHasHandle := True; +end; + +function TSSPIContext.DoInitialize(const aTokenSourceName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}; + var aIn, aOut: SecBufferDesc; + const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS; +var + tmp: PCtxtHandle; + tmp2: PSecBufferDesc; + r: ULONG; +begin + if fHasHandle then begin + tmp := @fHandle; + tmp2 := @aIn; + end else begin + tmp := nil; + tmp2 := nil; + end; + Result := + gSSPIInterface.FunctionTable.{$IFDEF SSPI_UNICODE}InitializeSecurityContextW{$ELSE}InitializeSecurityContextA{$ENDIF}( + Credentials.Handle, tmp, + {$IFDEF SSPI_UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}(aTokenSourceName), + GetRequestedFlags, 0, SECURITY_NATIVE_DREP, tmp2, 0, + @fHandle, @aOut, @r, @fExpiry + ); + UpdateHasContextAndCheckForError(Result, + {$IFDEF SSPI_UNICODE}'InitializeSecurityContextW'{$ELSE}'InitializeSecurityContextA'{$ENDIF}, {Do not translate} + errorsToIgnore); + SetEstablishedFlags(r); +end; + +procedure TSSPIContext.DoRelease; +begin + gSSPIInterface.RaiseIfError( + gSSPIInterface.FunctionTable.DeleteSecurityContext(@fHandle), 'DeleteSecurityContext'); {Do not translate} +end; + +procedure TSSPIContext.Release; +begin + if HasHandle then begin + DoRelease; + fHasHandle := False; + end; +end; + +procedure TSSPIContext.CheckHasHandle; +begin + if not HasHandle then begin + raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle); + end; +end; + +procedure TSSPIContext.CheckCredentials; +begin + if (not Assigned(Credentials)) or (not Credentials.Acquired) then begin + raise ESSPIException.Create(RSHTTPSSPIDoAuquireCredentialHandle); + end; +end; + +function TSSPIContext.GetExpiry: TimeStamp; +begin + CheckHasHandle; + Result := fExpiry; +end; + +function TSSPIContext.GetHandle: PCtxtHandle; +begin + CheckHasHandle; + Result := @fHandle; +end; + +{ TCustomSSPIConnectionContext } + +procedure TCustomSSPIConnectionContext.DoRelease; +begin + inherited DoRelease; + fStatus := SEC_E_INVALID_HANDLE; // just to put something other then SEC_E_OK +end; + +function TCustomSSPIConnectionContext.GetAuthenticated: Boolean; +begin + CheckHasHandle; + Result := fStatus = SEC_E_OK; +end; + +function TCustomSSPIConnectionContext.UpdateAndGenerateReply + (const aFromPeerToken: TIdBytes; var aToPeerToken: TIdBytes): Boolean; +var + fOutBuff: SecBuffer; +begin + Result := False; + + { check credentials } + CheckCredentials; + { prepare input buffer } + + fInBuff.cbBuffer := Length(aFromPeerToken); + + //Assert(Length(aFromPeerToken)>0); + if fInBuff.cbBuffer > 0 then begin + fInBuff.pvBuffer := @aFromPeerToken[0]; + end; + + { prepare output buffer } + fOutBuff.BufferType := SECBUFFER_TOKEN; + fOutBuff.cbBuffer := Credentials.Package.MaxToken; + fOutBuff.pvBuffer := AllocMem(fOutBuff.cbBuffer); + + fOutBuffDesc.ulVersion := SECBUFFER_VERSION; + fOutBuffDesc.cBuffers := 1; + fOutBuffDesc.pBuffers := @fOutBuff; + + try + { do processing } + fStatus := DoUpdateAndGenerateReply(fInBuffDesc, fOutBuffDesc, []); + { complete token if applicable } + case fStatus of + SEC_I_COMPLETE_NEEDED, + SEC_I_COMPLETE_AND_CONTINUE: + begin + if not Assigned(gSSPIInterface.FunctionTable.CompleteAuthToken) then begin + raise ESSPIException.Create(RSHTTPSSPICompleteTokenNotSupported); + end; + fStatus := gSSPIInterface.FunctionTable.CompleteAuthToken(Handle, @fOutBuffDesc); + gSSPIInterface.RaiseIfError(fStatus, 'CompleteAuthToken'); {Do not translate} + end; + end; + Result := + (fStatus = SEC_I_CONTINUE_NEEDED) or + (fStatus = SEC_I_COMPLETE_AND_CONTINUE) or + (fOutBuff.cbBuffer > 0); + if Result then begin + aToPeerToken := RawToBytes(fOutBuff.pvBuffer^, fOutBuff.cbBuffer); + end; + finally + FreeMem(fOutBuff.pvBuffer); + end; +end; + +constructor TCustomSSPIConnectionContext.Create(aCredentials: TSSPICredentials); +begin + inherited Create(aCredentials); + + fInBuff.BufferType := SECBUFFER_TOKEN; + + fInBuffDesc.ulVersion := SECBUFFER_VERSION; + fInBuffDesc.cBuffers := 1; + fInBuffDesc.pBuffers := @fInBuff; + + fOutBuffDesc.ulVersion := SECBUFFER_VERSION; + fOutBuffDesc.cBuffers := 1; +end; + +{ TSSPIClientConnectionContext } + +constructor TSSPIClientConnectionContext.Create(aCredentials: TSSPICredentials); +begin + inherited Create(aCredentials); + fTargetName := ''; {Do not translate} +end; + +function TSSPIClientConnectionContext.GetRequestedFlags: ULONG; +begin + Result := fReqReguested; +end; + +procedure TSSPIClientConnectionContext.SetEstablishedFlags(aFlags: ULONG); +begin + fReqEstablished := aFlags; +end; + +function TSSPIClientConnectionContext.DoUpdateAndGenerateReply + (var aIn, aOut: SecBufferDesc; + const aErrorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS; +begin + Result := DoInitialize(fTargetName, aIn, aOut, []); +end; + +function TSSPIClientConnectionContext.GenerateInitialChallenge + (const aTargetName: string; var aToPeerToken: TIdBytes): Boolean; +begin + Release; + fTargetName := aTargetName; + Result := UpdateAndGenerateReply(nil, aToPeerToken); {Do not translate} +end; + +{ TIndySSPINTLMClient } + +constructor TIndySSPINTLMClient.Create; +begin + inherited Create; + fNTLMPackage := TSSPINTLMPackage.Create; + fCredentials := TSSPIWinNTCredentials.Create(fNTLMPackage); + fContext := TSSPIClientConnectionContext.Create(fCredentials); +end; + +destructor TIndySSPINTLMClient.Destroy; +begin + FreeAndNil(fContext); + FreeAndNil(fCredentials); + FreeAndNil(fNTLMPackage); + inherited Destroy; +end; + +procedure TIndySSPINTLMClient.SetCredentials(const aDomain, aUserName, aPassword: string); +begin + fCredentials.Acquire(scuOutBound, aDomain, aUserName, aPassword); +end; + +procedure TIndySSPINTLMClient.SetCredentialsAsCurrentUser; +begin + fCredentials.Acquire(scuOutBound); +end; + +function TIndySSPINTLMClient.InitAndBuildType1Message: TIdBytes; +begin + fContext.GenerateInitialChallenge('', Result); +end; + +function TIndySSPINTLMClient.UpdateAndBuildType3Message(const aServerType2Message: TIdBytes): TIdBytes; +begin + fContext.UpdateAndGenerateReply(aServerType2Message, Result); +end; + +{ TIdSSPINTLMAuthentication } + +constructor TIdSSPINTLMAuthentication.Create; +begin + inherited Create; + FSSPIClient := TIndySSPINTLMClient.Create; + Domain := IndyComputerName; +end; + +function TIdSSPINTLMAuthentication.DoNext: TIdAuthWhatsNext; +begin + Result := wnDoRequest; + case FCurrentStep of + 0: + begin + {if (Length(Username) > 0) and (Length(Password) > 0) then + begin} + Result := wnDoRequest; + FCurrentStep := 1; + {end + else begin + result := wnAskTheProgram; + end;} + end; + 1: + begin + FCurrentStep := 2; + Result := wnDoRequest; + end; + //Authentication does the 2>3 progression + 3: + begin + FCurrentStep := 4; + Result := wnDoRequest; + end; + 4: + begin + FCurrentStep := 0; + if Username = '' then begin + Result := wnAskTheProgram; + end else begin + Result := wnFail; + Username := ''; + Password := ''; + Domain := IndyComputerName; + end; + end; + end; +end; + +function TIdSSPINTLMAuthentication.Authentication: string; +var + buf: TIdBytes; +begin + Result := ''; + buf := nil; + case FCurrentStep of + 1: + begin + if Length(Username) = 0 then begin + FSSPIClient.SetCredentialsAsCurrentUser; + end else begin + FSSPIClient.SetCredentials(Domain, Username, Password); + end; + Result := 'NTLM ' + TIdEncoderMIME.EncodeBytes(FSSPIClient.InitAndBuildType1Message); {Do not translate} + FNTLMInfo := ''; {Do not translate} + end; + 2: + begin + if Length(FNTLMInfo) = 0 then begin + FNTLMInfo := ReadAuthInfo('NTLM'); {Do not translate} + Fetch(FNTLMInfo); + end; + + if Length(FNTLMInfo) = 0 then begin + Reset; + Abort; + end; + + buf := TIdDecoderMIME.DecodeBytes(FNTLMInfo); + Result := 'NTLM ' + TIdEncoderMIME.EncodeBytes(FSSPIClient.UpdateAndBuildType3Message(buf)); {Do not translate} + + FCurrentStep := 3; + end; + 3: begin + FCurrentStep := 4; + end; + end; +end; + +function TIdSSPINTLMAuthentication.KeepAlive: Boolean; +begin + Result := FCurrentStep >= 1; +end; + +function TIdSSPINTLMAuthentication.GetSteps: Integer; +begin + Result := 3; +end; + +procedure TIdSSPINTLMAuthentication.SetDomain(const Value: String); +begin + Params.Values['Domain'] := Value; {do not localize} +end; + +function TIdSSPINTLMAuthentication.GetDomain: String; +begin + Result := Params.Values['Domain']; {do not localize} +end; + +procedure TIdSSPINTLMAuthentication.SetUserName(const Value: String); +Var + S: String; +begin + S := Value; + if IndyPos('\', S) > 0 then begin + Domain := Copy(S, 1, IndyPos('\', S) - 1); + Delete(S, 1, Length(Domain) + 1); + end; + inherited SetUserName(S); +end; + +destructor TIdSSPINTLMAuthentication.Destroy; +begin + FreeAndNil(FSSPIClient); + inherited; +end; + +initialization + gSSPIInterface := TSSPIInterface.Create; + if gSSPIInterface.IsAvailable then begin + RegisterAuthenticationMethod('NTLM', TIdSSPINTLMAuthentication); {do not localize} + RegisterAuthenticationMethod('Negotiate', TIdSSPINTLMAuthentication); {do not localize} + gAuthRegistered := True; + end; +finalization + if gAuthRegistered then begin + UnregisterAuthenticationMethod('NTLM'); {do not localize} + UnregisterAuthenticationMethod('Negotiate'); {do not localize} + end; + FreeAndNil(gSSPIInterface); + +end. + diff --git a/indy/Protocols/IdBlockCipherIntercept.pas b/indy/Protocols/IdBlockCipherIntercept.pas new file mode 100644 index 0000000..bffe178 --- /dev/null +++ b/indy/Protocols/IdBlockCipherIntercept.pas @@ -0,0 +1,272 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 5/12/2003 12:30:58 AM GGrieve + Get compiling again with DotNet Changes + + Rev 1.3 10/12/2003 1:49:26 PM BGooijen + Changed comment of last checkin + + Rev 1.2 10/12/2003 1:43:24 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/14/2002 02:13:56 PM JPMugaas +} + +unit IdBlockCipherIntercept; + +{ + UnitName: IdBlockCipherIntercept + Author: Andrew P.Rybin [magicode@mail.ru] + Creation: 27.02.2002 + Version: 0.9.0b + Purpose: Secure communications +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdException, + IdResourceStringsProtocols, + IdIntercept; + +const + IdBlockCipherBlockSizeDefault = 16; + + IdBlockCipherBlockSizeMax = 256; + // why 256? not any block ciphers that can - or should - be used beyond this + // length. You can extend this if you like. But the longer it is, the + // more network traffic is wasted + + //256, as currently the last byte of the block is used to store the block size + +type + TIdBlockCipherIntercept = class; + + // OnSend and OnRecieve Events will always be called with a blockSize Data + TIdBlockCipherIntercept = class(TIdConnectionIntercept) + protected + FBlockSize: Integer; + FIncoming : TIdBytes; + procedure Decrypt (var VData : TIdBytes); virtual; + procedure Encrypt (var VData : TIdBytes); virtual; + procedure SetBlockSize(const Value: Integer); + procedure InitComponent; override; + public + procedure Receive(var VBuffer: TIdBytes); override; //Decrypt + procedure Send(var VBuffer: TIdBytes); override; //Encrypt + procedure CopySettingsFrom (ASrcBlockCipherIntercept: TIdBlockCipherIntercept); // warning: copies Data too + published + property BlockSize: Integer read FBlockSize write SetBlockSize default IdBlockCipherBlockSizeDefault; + end; + + TIdServerBlockCipherIntercept = class(TIdServerIntercept) + protected + FBlockSize: Integer; + procedure InitComponent; override; + public + procedure Init; override; + function Accept(AConnection: TComponent): TIdConnectionIntercept; override; + published + property BlockSize: Integer read FBlockSize write FBlockSize default IdBlockCipherBlockSizeDefault; + end; + + EIdBlockCipherInterceptException = EIdException; {block length} + +implementation + +uses + IdResourceStrings, + SysUtils; + +{ TIdBlockCipherIntercept } + +//const +// bitLongTail = $80; //future: for IdBlockCipherBlockSizeMax>256 + +procedure TIdBlockCipherIntercept.Encrypt(var VData : TIdBytes); +begin + if Assigned(FOnSend) then begin + FOnSend(Self, VData); + end;//ex: EncryptAES(LTempIn, ExpandedKey, LTempOut); +end; + +procedure TIdBlockCipherIntercept.Decrypt(var VData : TIdBytes); +Begin + if Assigned(FOnReceive) then begin + FOnReceive(Self, VData); + end;//ex: DecryptAES(LTempIn, ExpandedKey, LTempOut); +end; + +procedure TIdBlockCipherIntercept.Send(var VBuffer: TIdBytes); +var + LSrc, LBlock : TIdBytes; + LSize, LCount, LMaxDataSize: Integer; + LCompleteBlocks, LRemaining: Integer; +begin + LSrc := nil; // keep the compiler happy + + LSize := Length(VBuffer); + if LSize > 0 then begin + LSrc := VBuffer; + + LMaxDataSize := FBlockSize - 1; + SetLength(VBuffer, ((LSize + LMaxDataSize - 1) div LMaxDataSize) * FBlockSize); + SetLength(LBlock, FBlockSize); + + LCompleteBlocks := LSize div LMaxDataSize; + LRemaining := LSize mod LMaxDataSize; + + //process all complete blocks + for LCount := 0 to LCompleteBlocks-1 do + begin + CopyTIdBytes(LSrc, LCount * LMaxDataSize, LBlock, 0, LMaxDataSize); + LBlock[LMaxDataSize] := LMaxDataSize; + Encrypt(LBlock); + CopyTIdBytes(LBlock, 0, VBuffer, LCount * FBlockSize, FBlockSize); + end; + + //process the possible remaining bytes, ie less than a full block + if LRemaining > 0 then + begin + CopyTIdBytes(LSrc, LSize - LRemaining, LBlock, 0, LRemaining); + LBlock[LMaxDataSize] := LRemaining; + Encrypt(LBlock); + CopyTIdBytes(LBlock, 0, VBuffer, Length(VBuffer) - FBlockSize, FBlockSize); + end; + end; + + // let the next Intercept in the chain encode its data next + + // RLebeau: DO NOT call inherited! It will trigger the OnSend event + // again with the entire altered buffer as input, which can cause user + // code to re-encrypt the already-encrypted data. We do not want that + // here! Just call the next Intercept directly... + + //inherited Send(VBuffer); + if Intercept <> nil then begin + Intercept.Send(VBuffer); + end; +end; + +procedure TIdBlockCipherIntercept.Receive(var VBuffer: TIdBytes); +var + LBlock : TIdBytes; + LSize, LCount, LPos, LMaxDataSize, LCompleteBlocks: Integer; + LRemaining: Integer; +begin + // let the next Intercept in the chain decode its data first + + // RLebeau: DO NOT call inherited! It will trigger the OnReceive event + // with the entire decoded buffer as input, which can cause user + // code to decrypt data prematurely/incorrectly. We do not want that + // here! Just call the next Intercept directly... + + //inherited Receive(VBuffer); + if Intercept <> nil then begin + Intercept.Receive(VBuffer); + end; + + LPos := 0; + AppendBytes(FIncoming, VBuffer); + + LSize := Length(FIncoming); + if LSize >= FBlockSize then + begin + // the length of ABuffer when we have finished is currently unknown, but must be less than + // the length of FIncoming. We will reserve this much, then reallocate at the end + SetLength(VBuffer, LSize); + SetLength(LBlock, FBlockSize); + + LMaxDataSize := FBlockSize - 1; + LCompleteBlocks := LSize div FBlockSize; + LRemaining := LSize mod FBlockSize; + + for LCount := 0 to LCompleteBlocks-1 do + begin + CopyTIdBytes(FIncoming, LCount * FBlockSize, LBlock, 0, FBlockSize); + Decrypt(LBlock); + if (LBlock[LMaxDataSize] = 0) or (LBlock[LMaxDataSize] >= FBlockSize) then begin + raise EIdBlockCipherInterceptException.CreateFmt(RSBlockIncorrectLength, [LBlock[LMaxDataSize]]); + end; + CopyTIdBytes(LBlock, 0, VBuffer, LPos, LBlock[LMaxDataSize]); + Inc(LPos, LBlock[LMaxDataSize]); + end; + + if LRemaining > 0 then begin + CopyTIdBytes(FIncoming, LSize - LRemaining, FIncoming, 0, LRemaining); + end; + + SetLength(FIncoming, LRemaining); + end; + + SetLength(VBuffer, LPos); +end; + +procedure TIdBlockCipherIntercept.CopySettingsFrom(ASrcBlockCipherIntercept: TIdBlockCipherIntercept); +Begin + FBlockSize := ASrcBlockCipherIntercept.FBlockSize; + {$IFDEF USE_OBJECT_ARC} + FDataObject := ASrcBlockCipherIntercept.FDataObject; + FDataValue := ASrcBlockCipherIntercept.FDataValue; + {$ELSE} + FData := ASrcBlockCipherIntercept.FData; // not sure that this is actually safe + {$ENDIF} + FOnConnect := ASrcBlockCipherIntercept.FOnConnect; + FOnDisconnect:= ASrcBlockCipherIntercept.FOnDisconnect; + FOnReceive := ASrcBlockCipherIntercept.FOnReceive; + FOnSend := ASrcBlockCipherIntercept.FOnSend; +end; + +procedure TIdBlockCipherIntercept.SetBlockSize(const Value: Integer); +Begin + if (Value > 0) and (Value <= IdBlockCipherBlockSizeMax) then begin + FBlockSize := Value; + end; +end; + +procedure TIdBlockCipherIntercept.InitComponent; +begin + inherited InitComponent; + FBlockSize := IdBlockCipherBlockSizeDefault; + SetLength(FIncoming, 0); +end; + +{ TIdServerBlockCipherIntercept } + +procedure TIdServerBlockCipherIntercept.InitComponent; +begin + inherited InitComponent; + FBlockSize := IdBlockCipherBlockSizeDefault; +end; + +procedure TIdServerBlockCipherIntercept.Init; +begin +end; + +function TIdServerBlockCipherIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept; +begin + Result := TIdBlockCipherIntercept.Create(nil); + TIdBlockCipherIntercept(Result).BlockSize := BlockSize; +end; + +end. diff --git a/indy/Protocols/IdChargenServer.pas b/indy/Protocols/IdChargenServer.pas new file mode 100644 index 0000000..41a290e --- /dev/null +++ b/indy/Protocols/IdChargenServer.pas @@ -0,0 +1,104 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 12/2/2004 4:23:48 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.3 1/21/2004 1:49:34 PM JPMugaas + InitComponent + + Rev 1.2 1/17/2003 05:35:28 PM JPMugaas + Now compiles with new design. + + Rev 1.1 1-1-2003 20:12:40 BGooijen + Changed to support the new TIdContext class + + Rev 1.0 11/14/2002 02:14:02 PM JPMugaas + + 2000-Apr-17 Kudzu + Converted to Indy + Improved efficiency +} + +unit IdChargenServer; + +{ + Original Author: Ozz Nixon +} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Type + TIdChargenServer = class(TIdCustomTCPServer) + protected + function DoExecute(AContext:TIdContext): boolean; override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_CHARGEN; + end; + +implementation + +uses + IdIOHandler; + +{ TIdChargenServer } + +procedure TIdChargenServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_CHARGEN; +end; + +function TIdChargenServer.DoExecute(AContext:TIdContext): boolean; +var + Counter, Width, Base: integer; + LIOHandler: TIdIOHandler; +begin + Result := true; + Base := 0; + Counter := 1; + Width := 1; + LIOHandler := AContext.Connection.IOHandler; + while LIOHandler.Connected do begin + LIOHandler.Write(Chr(Counter + 31)); + Inc(Counter); + Inc(Width); + if Width = 72 then begin + LIOHandler.WriteLn; {Do not Localize} + Width := 1; + Inc(Base); + if Base = 95 then begin + Base := 1; + end; + Counter := Base; + end; + if Counter = 95 then begin + Counter := 1; + end; + end; +end; + +end. diff --git a/indy/Protocols/IdChargenUDPServer.pas b/indy/Protocols/IdChargenUDPServer.pas new file mode 100644 index 0000000..b2b4d91 --- /dev/null +++ b/indy/Protocols/IdChargenUDPServer.pas @@ -0,0 +1,102 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 2004.02.03 5:44:56 PM czhower + Name changes + + Rev 1.3 1/21/2004 1:49:36 PM JPMugaas + InitComponent + + Rev 1.2 10/24/2003 02:54:50 PM JPMugaas + These should now work with the new code. + + Rev 1.1 2003.10.24 10:38:24 AM czhower + UDP Server todos + + Rev 1.0 11/14/2002 02:14:08 PM JPMugaas + + 2001 - Sep 17 + J. Peter Mugaas + Started this with code from Rune Moburg's UDP Chargen Server +} + +unit IdChargenUDPServer; + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, IdGlobal, IdSocketHandle, IdUDPBase, IdUDPServer; + +type + TIdChargenUDPServer = class(TIdUDPServer) + protected + procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_CHARGEN; + end; + +implementation + +{ TIdChargenUDPServer } + +procedure TIdChargenUDPServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_CHARGEN; +end; + +procedure TIdChargenUDPServer.DoUDPRead(AThread: TIdUDPListenerThread; + const AData: TIdBytes; ABinding: TIdSocketHandle); +const + rowlength = 75; +var + s: string; + i, row, ln : integer; + c: Char; +begin + inherited DoUDPRead(AThread, AData, ABinding); + i := 1; + c := '0'; {Do not Localize} + s := ''; {Do not Localize} + ln := Random(512); + Row := 0; + while i <= ln do + begin + if c > #95 then + begin + c := '0'; {Do not Localize} + end; + if i mod (rowlength + 1) = 0 then + begin + s := s + #13; + c := chr(ord('0') + row mod (95 - ord('0'))); {Do not Localize} + inc(row); + end + else + begin + s := s + c; + end; + inc(i); + inc(c); + end; + ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, ToBytes(s), ABinding.IPVersion); +end; + +end. diff --git a/indy/Protocols/IdCharsets.pas b/indy/Protocols/IdCharsets.pas new file mode 100644 index 0000000..211095f --- /dev/null +++ b/indy/Protocols/IdCharsets.pas @@ -0,0 +1,5165 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 10/16/2003 10:49:18 PM DSiders + Added localization comments. + + Rev 1.2 10/8/2003 9:49:02 PM GGrieve + merge all TIdCharset to here + + Rev 1.1 10/3/2003 5:39:26 PM GGrieve + dotnet work + + Rev 1.0 11/14/2002 02:14:14 PM JPMugaas +} + +unit IdCharsets; + +{ + This file is automatically created from + http://www.iana.org/assignments/character-sets + + All character set constants are prefixed with "idcs", this could lead + to having a constant named idcscs... because some IANA names are actually + cs... + All constants have been renamed to fit Delphi's naming scheme, + '-', '.', ':' and '+' are converted to '_' + If a collision occurs, a '_' is appended to the name. + Care is taken to + a) put the preferred charset first in a list of identical ones + b) not append a '_' to the preferred charset + + Two functions can be found here: + 1) + * function FindPreferredCharset(const Charset: TIdCharSet): TIdCharSet; + is provided to find the preferred identical charset from an arbitrary + charset given. + + 2) + * function FindCharset(const s: string): TIdCharset; + can be used to find a charset from a given string + (if not found idcs_INVALID is returned) + + For references and people see the end of the file (copied from above location) + + Johannes Berg - 2002-08-22 + + -- header of the original file follows -- + + =================================================================== + CHARACTER SETS + + (last updated 2007-05-14) + + These are the official names for character sets that may be used in + the Internet and may be referred to in Internet documentation. These + names are expressed in ANSI_X3.4-1968 which is commonly called + US-ASCII or simply ASCII. The character set most commonly use in the + Internet and used especially in protocol standards is US-ASCII, this + is strongly encouraged. The use of the name US-ASCII is also + encouraged. + + The character set names may be up to 40 characters taken from the + printable characters of US-ASCII. However, no distinction is made + between use of upper and lower case letters. + + The MIBenum value is a unique value for use in MIBs to identify coded + character sets. + + The value space for MIBenum values has been divided into three + regions. The first region (3-999) consists of coded character sets + that have been standardized by some standard setting organization. + This region is intended for standards that do not have subset + implementations. The second region (1000-1999) is for the Unicode and + ISO/IEC 10646 coded character sets together with a specification of a + (set of) sub-repertoires that may occur. The third region (>1999) is + intended for vendor specific coded character sets. + + Assigned MIB enum Numbers + ------------------------- + 0-2 Reserved + 3-999 Set By Standards Organizations + 1000-1999 Unicode / 10646 + 2000-2999 Vendor + + The aliases that start with "cs" have been added for use with the + IANA-CHARSET-MIB as originally defined in RFC3808, and as currently + maintained by IANA at http://www.iana.org/assignments/ianacharset-mib. + Note that the ianacharset-mib needs to be kept in sync with this + registry. These aliases that start with "cs" contain the standard + numbers along with suggestive names in order to facilitate applications + that want to display the names in user interfaces. The "cs" stands + for character set and is provided for applications that need a lower + case first letter but want to use mixed case thereafter that cannot + contain any special characters, such as underbar ("_") and dash ("-"). + + If the character set is from an ISO standard, its cs alias is the ISO + standard number or name. If the character set is not from an ISO + standard, but is registered with ISO (IPSJ/ITSCJ is the current ISO + Registration Authority), the ISO Registry number is specified as + ISOnnn followed by letters suggestive of the name or standards number + of the code set. When a national or international standard is + revised, the year of revision is added to the cs alias of the new + character set entry in the IANA Registry in order to distinguish the + revised character set from the original character set. +} + +interface + +{$i IdCompilerDefines.inc} + +// once upon a time Indy had 3 different declarations of TIdCharSet +// now all use this one. For reference, one of the more widely used +// enums and the equivalents in the full enum is listed here: +// +// csGB2312 idcsGB2312 * +// csBig5 idcsBig5 * +// csIso2022jp idcsISO_2022_JP * +// csEucKR idcsEUC_KR * +// csIso88591 idcsISO_8859_1 * +// csWindows1251 idcswindows_1251 * +// csKOI8r idcsKOI8_R * +// csKOI8u idcsKOI8_U * +// csUnicode idcsUNICODE_1_1 +// +// +// Classic UTF-8 is idcsUTF_8 + +type + TIdCharSet = ( + idcs_INVALID, { signifies an invalid character was found when searching for a charset by name } + + { US-ASCII } + { MIB: 3 } + idcs_US_ASCII, // Codepage: 20127 + idcs_ANSI_X3_4_1968, + idcs_iso_ir_6, + idcs_ANSI_X3_4_1986, + idcs_ISO_646_irv_1991, + idcs_ASCII, + idcs_ISO646_US, + idcs_us, + idcs_IBM367, + idcs_cp367, + idcs_csASCII, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 27 } + idcs_ISO_10646_UTF_1, // Codepage: ? + idcs_csISO10646UTF1, + { Source: + Universal Transfer Format (1), this is the multibyte + encoding, that subsets ASCII-7. It does not have byte + ordering issues. } + + { MIB: 28 } + idcs_ISO_646_basic_1983, // Codepage: ? + idcs_ref, + idcs_csISO646basic1983, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 29 } + idcs_INVARIANT, // Codepage: ? + idcs_csINVARIANT, + { References: RFC1345,KXS2 } + + { MIB: 30 } + idcs_ISO_646_irv_1983, // Codepage: ? + idcs_iso_ir_2, + idcs_irv, + idcs_csISO2IntlRefVersion, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 20 } + idcs_BS_4730, // Codepage: ? + idcs_iso_ir_4, + idcs_ISO646_GB, + idcs_gb, + idcs_uk, + idcs_csISO4UnitedKingdom, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 31 } + idcs_NATS_SEFI, // Codepage: ? + idcs_iso_ir_8_1, + idcs_csNATSSEFI, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 32 } + idcs_NATS_SEFI_ADD, // Codepage: ? + idcs_iso_ir_8_2, + idcs_csNATSSEFIADD, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 33 } + idcs_NATS_DANO, // Codepage: ? + idcs_iso_ir_9_1, + idcs_csNATSDANO, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 34 } + idcs_NATS_DANO_ADD, // Codepage: ? + idcs_iso_ir_9_2, + idcs_csNATSDANOADD, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 35 } + idcs_SEN_850200_B, // Codepage: ? + idcs_iso_ir_10, + idcs_FI, + idcs_ISO646_FI, + idcs_ISO646_SE, + idcs_se, + idcs_csISO10Swedish, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 21 } + idcs_SEN_850200_C, // Codepage: ? + idcs_iso_ir_11, + idcs_ISO646_SE2, + idcs_se2, + idcs_csISO11SwedishForNames, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Korean } + { MIB: 36 } + idcs_KS_C_5601_1987, // Codepage: 949 + idcs_iso_ir_149, + idcs_KS_C_5601_1989, + idcs_KSC_5601, + idcs_korean, + idcs_csKSC56011987, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Korean (ISO) } + { MIB: 37 } + idcs_ISO_2022_KR, // Codepage: 50225 + idcs_csISO2022KR, + { References: RFC1557,Choi } + { Source: + RFC-1557 (see also KS_C_5601-1987) } + + { Korean (EUC) } + { MIB: 38 } + idcs_EUC_KR, // Codepage: 51949 + idcs_csEUCKR, + { References: RFC1557,Choi } + { Source: + RFC-1557 (see also KS_C_5861-1992) } + + { Japanese (JIS-Allow 1 byte Kana - SO/SI) } + { MIB: 39 } + idcs_ISO_2022_JP, // Codepage: 50220 [need to verify] + idcs_csISO2022JP, // Codepage: 50221 + { References: RFC1468,Murai } + { Source: + RFC-1468 (see also RFC-2237) } + + { MIB: 40 } + idcs_ISO_2022_JP_2, // Codepage: ? + idcs_csISO2022JP2, + { References: RFC1554,Ohta } + { Source: + RFC-1554 } + + { MIB: 104 } + idcs_ISO_2022_CN, // Codepage: ? + { References: RFC1922 } + { Source: + RFC-1922 } + + { MIB: 105 } + idcs_ISO_2022_CN_EXT, // Codepage: ? + { References: RFC1922 } + { Source: + RFC-1922 } + + { MIB: 41 } + idcs_JIS_C6220_1969_jp, // Codepage: ? + idcs_JIS_C6220_1969, + idcs_iso_ir_13, + idcs_katakana, + idcs_x0201_7, + idcs_csISO13JISC6220jp, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 42 } + idcs_JIS_C6220_1969_ro, // Codepage: ? + idcs_iso_ir_14, + idcs_jp, + idcs_ISO646_JP, + idcs_csISO14JISC6220ro, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 22 } + idcs_IT, // Codepage: ? + idcs_iso_ir_15, + idcs_ISO646_IT, + idcs_csISO15Italian, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 43 } + idcs_PT, // Codepage: ? + idcs_iso_ir_16, + idcs_ISO646_PT, + idcs_csISO16Portuguese, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 23 } + idcs_ES, // Codepage: ? + idcs_iso_ir_17, + idcs_ISO646_ES, + idcs_csISO17Spanish, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 44 } + idcs_greek7_old, // Codepage: ? + idcs_iso_ir_18, + idcs_csISO18Greek7Old, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 45 } + idcs_latin_greek, // Codepage: ? + idcs_iso_ir_19, + idcs_csISO19LatinGreek, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 24 } + idcs_DIN_66003, // Codepage: ? + idcs_iso_ir_21, + idcs_de, + idcs_ISO646_DE, + idcs_csISO21German, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 46 } + idcs_NF_Z_62_010_1973, // Codepage: ? + idcs_iso_ir_25, + idcs_ISO646_FR1, + idcs_csISO25French, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 47 } + idcs_Latin_greek_1, // Codepage: ? + idcs_iso_ir_27, + idcs_csISO27LatinGreek1, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 48 } + idcs_ISO_5427, // Codepage: ? + idcs_iso_ir_37, + idcs_csISO5427Cyrillic, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 49 } + idcs_JIS_C6226_1978, // Codepage: ? + idcs_iso_ir_42, + idcs_csISO42JISC62261978, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 50 } + idcs_BS_viewdata, // Codepage: ? + idcs_iso_ir_47, + idcs_csISO47BSViewdata, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 51 } + idcs_INIS, // Codepage: ? + idcs_iso_ir_49, + idcs_csISO49INIS, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 52 } + idcs_INIS_8, // Codepage: ? + idcs_iso_ir_50, + idcs_csISO50INIS8, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 53 } + idcs_INIS_cyrillic, // Codepage: ? + idcs_iso_ir_51, + idcs_csISO51INISCyrillic, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 54 } + idcs_ISO_5427_1981, // Codepage: ? + idcs_iso_ir_54, + idcs_ISO5427Cyrillic1981, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 55 } + idcs_ISO_5428_1980, // Codepage: ? + idcs_iso_ir_55, + idcs_csISO5428Greek, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 56 } + idcs_GB_1988_80, // Codepage: ? + idcs_iso_ir_57, + idcs_cn, + idcs_ISO646_CN, + idcs_csISO57GB1988, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 57 } + idcs_GB_2312_80, // Codepage: ? + idcs_iso_ir_58, + idcs_chinese, + idcs_csISO58GB231280, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 25 } + idcs_NS_4551_1, // Codepage: ? + idcs_iso_ir_60, + idcs_ISO646_NO, + idcs_no, + idcs_csISO60DanishNorwegian, + idcs_csISO60Norwegian1, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 58 } + idcs_NS_4551_2, // Codepage: ? + idcs_ISO646_NO2, + idcs_iso_ir_61, + idcs_no2, + idcs_csISO61Norwegian2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 26 } + idcs_NF_Z_62_010, // Codepage: ? + idcs_iso_ir_69, + idcs_ISO646_FR, + idcs_fr, + idcs_csISO69French, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 59 } + idcs_videotex_suppl, // Codepage: ? + idcs_iso_ir_70, + idcs_csISO70VideotexSupp1, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 60 } + idcs_PT2, // Codepage: ? + idcs_iso_ir_84, + idcs_ISO646_PT2, + idcs_csISO84Portuguese2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 61 } + idcs_ES2, // Codepage: ? + idcs_iso_ir_85, + idcs_ISO646_ES2, + idcs_csISO85Spanish2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 62 } + idcs_MSZ_7795_3, // Codepage: ? + idcs_iso_ir_86, + idcs_ISO646_HU, + idcs_hu, + idcs_csISO86Hungarian, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 63 } + idcs_JIS_C6226_1983, // Codepage: ? + idcs_iso_ir_87, + idcs_x0208, + idcs_JIS_X0208_1983, + idcs_csISO87JISX0208, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 64 } + idcs_greek7, // Codepage: ? + idcs_iso_ir_88, + idcs_csISO88Greek7, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 65 } + idcs_ASMO_449, // Codepage: ? + idcs_ISO_9036, + idcs_arabic7, + idcs_iso_ir_89, + idcs_csISO89ASMO449, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 66 } + idcs_iso_ir_90, // Codepage: ? + idcs_csISO90, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 67 } + idcs_JIS_C6229_1984_a, // Codepage: ? + idcs_iso_ir_91, + idcs_jp_ocr_a, + idcs_csISO91JISC62291984a, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 68 } + idcs_JIS_C6229_1984_b, // Codepage: ? + idcs_iso_ir_92, + idcs_ISO646_JP_OCR_B, + idcs_jp_ocr_b, + idcs_csISO92JISC62991984b, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 69 } + idcs_JIS_C6229_1984_b_add, // Codepage: ? + idcs_iso_ir_93, + idcs_jp_ocr_b_add, + idcs_csISO93JIS62291984badd, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 70 } + idcs_JIS_C6229_1984_hand, // Codepage: ? + idcs_iso_ir_94, + idcs_jp_ocr_hand, + idcs_csISO94JIS62291984hand, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 71 } + idcs_JIS_C6229_1984_hand_add, // Codepage: ? + idcs_iso_ir_95, + idcs_jp_ocr_hand_add, + idcs_csISO95JIS62291984handadd, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 72 } + idcs_JIS_C6229_1984_kana, // Codepage: ? + idcs_iso_ir_96, + idcs_csISO96JISC62291984kana, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 73 } + idcs_ISO_2033_1983, // Codepage: ? + idcs_iso_ir_98, + idcs_e13b, + idcs_csISO2033, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 74 } + idcs_ANSI_X3_110_1983, // Codepage: ? + idcs_iso_ir_99, + idcs_CSA_T500_1983, + idcs_NAPLPS, + idcs_csISO99NAPLPS, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Western European (ISO) } + { MIB: 4 } + idcs_ISO_8859_1, // Codepage: 28591 + idcs_ISO_8859_1_1987, + idcs_iso_ir_100, + idcs_ISO_8859_1_, + idcs_latin1, + idcs_l1, + idcs_IBM819, + idcs_CP819, + idcs_csISOLatin1, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Central European (ISO) } + { MIB: 5 } + idcs_ISO_8859_2, // Codepage: 28592 + idcs_ISO_8859_2_1987, + idcs_iso_ir_101, + idcs_ISO_8859_2_, + idcs_latin2, + idcs_l2, + idcs_csISOLatin2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 75 } + idcs_T_61_7bit, // Codepage: ? + idcs_iso_ir_102, + idcs_csISO102T617bit, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 76 } + idcs_T_61_8bit, // Codepage: ? + idcs_T_61, + idcs_iso_ir_103, + idcs_csISO103T618bit, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Latin 3 (ISO) } + { MIB: 6 } + idcs_ISO_8859_3, // Codepage: 28593 + idcs_ISO_8859_3_1988, + idcs_iso_ir_109, + idcs_ISO_8859_3_, + idcs_latin3, + idcs_l3, + idcs_csISOLatin3, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Baltic (ISO) } + { MIB: 7 } + idcs_ISO_8859_4, // Codepage: 28594 + idcs_ISO_8859_4_1988, + idcs_iso_ir_110, + idcs_ISO_8859_4_, + idcs_latin4, + idcs_l4, + idcs_csISOLatin4, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 77 } + idcs_ECMA_cyrillic, // Codepage: ? + idcs_iso_ir_111, + idcs_KOI8_E, + idcs_csISO111ECMACyrillic, + { Source: + ISO registry (formerly ECMA registry) + http://www.itscj.ipsj.jp/ISO-IR/111.pdf } + + { MIB: 78 } + idcs_CSA_Z243_4_1985_1, // Codepage: ? + idcs_iso_ir_121, + idcs_ISO646_CA, + idcs_csa7_1, + idcs_ca, + idcs_csISO121Canadian1, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 79 } + idcs_CSA_Z243_4_1985_2, // Codepage: ? + idcs_iso_ir_122, + idcs_ISO646_CA2, + idcs_csa7_2, + idcs_csISO122Canadian2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 80 } + idcs_CSA_Z243_4_1985_gr, // Codepage: ? + idcs_iso_ir_123, + idcs_csISO123CSAZ24341985gr, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Arabic (ISO) } + { MIB: 9 } + idcs_ISO_8859_6, // Codepage: 28596 + idcs_ISO_8859_6_1987, + idcs_iso_ir_127, + idcs_ISO_8859_6_, + idcs_ECMA_114, + idcs_ASMO_708, + idcs_arabic, + idcs_csISOLatinArabic, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 81 } + idcs_ISO_8859_6_E, // Codepage: ? + idcs_ISO_8859_6_E_, + idcs_csISO88596E, + { References: RFC1556,IANA } + { Source: + RFC1556 } + + { MIB: 82 } + idcs_ISO_8859_6_I, // Codepage: ? + idcs_ISO_8859_6_I_, + idcs_csISO88596I, + { References: RFC1556,IANA } + { Source: + RFC1556 } + + { Greek (ISO) } + { MIB: 10 } + idcs_ISO_8859_7, // Codepage: 28597 + idcs_ISO_8859_7_1987, + idcs_iso_ir_126, + idcs_ISO_8859_7_, + idcs_ELOT_928, + idcs_ECMA_118, + idcs_greek, + idcs_greek8, + idcs_csISOLatinGreek, + { References: RFC1947,RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 83 } + idcs_T_101_G2, // Codepage: ? + idcs_iso_ir_128, + idcs_csISO128T101G2, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Hebrew (ISO-Visual) } + { MIB: 11 } + idcs_ISO_8859_8, // Codepage: 28598 + idcs_ISO_8859_8_1988, + idcs_iso_ir_138, + idcs_ISO_8859_8_, + idcs_hebrew, + idcs_csISOLatinHebrew, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 84 } + idcs_ISO_8859_8_E, // Codepage: ? + idcs_ISO_8859_8_E_, + idcs_csISO88598E, + { References: RFC1556,Nussbacher } + { Source: + RFC1556 } + + { Hebrew (ISO-Logical) } + { MIB: 85 } + idcs_ISO_8859_8_I, // Codepage: 38598 + idcs_ISO_8859_8_I_, + idcs_csISO88598I, + { References: RFC1556,Nussbacher } + { Source: + RFC1556 } + + { MIB: 86 } + idcs_CSN_369103, // Codepage: ? + idcs_iso_ir_139, + idcs_csISO139CSN369103, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 87 } + idcs_JUS_I_B1_002, // Codepage: ? + idcs_iso_ir_141, + idcs_ISO646_YU, + idcs_js, + idcs_yu, + idcs_csISO141JUSIB1002, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 14 } + idcs_ISO_6937_2_add, // Codepage: ? + idcs_iso_ir_142, + idcs_csISOTextComm, + { References: RFC1345,KXS2 } + { Source: + ECMA registry and ISO 6937-2:1983 } + + { MIB: 88 } + idcs_IEC_P27_1, // Codepage: ? + idcs_iso_ir_143, + idcs_csISO143IECP271, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Cyrillic (ISO) } + { MIB: 8 } + idcs_ISO_8859_5, // Codepage: 28595 + idcs_ISO_8859_5_1988, + idcs_iso_ir_144, + idcs_ISO_8859_5_, + idcs_cyrillic, + idcs_csISOLatinCyrillic, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 89 } + idcs_JUS_I_B1_003_serb, // Codepage: ? + idcs_iso_ir_146, + idcs_serbian, + idcs_csISO146Serbian, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 90 } + idcs_JUS_I_B1_003_mac, // Codepage: ? + idcs_macedonian, + idcs_iso_ir_147, + idcs_csISO147Macedonian, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { Turkish (ISO) } + { MIB: 12 } + idcs_ISO_8859_9, // Codepage: 28599 + idcs_ISO_8859_9_1989, + idcs_iso_ir_148, + idcs_ISO_8859_9_, + idcs_latin5, + idcs_l5, + idcs_csISOLatin5, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 91 } + idcs_greek_ccitt, // Codepage: ? + idcs_iso_ir_150, + idcs_csISO150, + idcs_csISO150GreekCCITT, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 92 } + idcs_NC_NC00_10_81, // Codepage: ? + idcs_cuba, + idcs_iso_ir_151, + idcs_ISO646_CU, + idcs_csISO151Cuba, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 93 } + idcs_ISO_6937_2_25, // Codepage: ? + idcs_iso_ir_152, + idcs_csISO6937Add, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 94 } + idcs_GOST_19768_74, // Codepage: ? + idcs_ST_SEV_358_88, + idcs_iso_ir_153, + idcs_csISO153GOST1976874, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 95 } + idcs_ISO_8859_supp, // Codepage: ? + idcs_iso_ir_154, + idcs_latin1_2_5, + idcs_csISO8859Supp, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 96 } + idcs_ISO_10367_box, // Codepage: ? + idcs_iso_ir_155, + idcs_csISO10367Box, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 13 } + idcs_ISO_8859_10, // Codepage: ? + idcs_iso_ir_157, + idcs_l6, + idcs_ISO_8859_10_1992, + idcs_csISOLatin6, + idcs_latin6, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 97 } + idcs_latin_lap, // Codepage: ? + idcs_lap, + idcs_iso_ir_158, + idcs_csISO158Lap, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 98 } + idcs_JIS_X0212_1990, // Codepage: ? + idcs_x0212, + idcs_iso_ir_159, + idcs_csISO159JISX02121990, + { References: RFC1345,KXS2 } + { Source: + ECMA registry } + + { MIB: 99 } + idcs_DS_2089, // Codepage: ? + idcs_DS2089, + idcs_ISO646_DK, + idcs_dk, + idcs_csISO646Danish, + { References: RFC1345,KXS2 } + { Source: + Danish Standard, DS 2089, February 1974 } + + { MIB: 100 } + idcs_us_dk, // Codepage: ? + idcs_csUSDK, + { References: RFC1345,KXS2 } + + { MIB: 101 } + idcs_dk_us, // Codepage: ? + idcs_csDKUS, + { References: RFC1345,KXS2 } + + { MIB: 15 } + idcs_JIS_X0201, // Codepage: ? + idcs_X0201, + idcs_csHalfWidthKatakana, + { References: RFC1345,KXS2 } + { Source: + JIS X 0201-1976. One byte only, this is equivalent to + JIS/Roman (similar to ASCII) plus eight-bit half-width + Katakana } + + { MIB: 102 } + idcs_KSC5636, // Codepage: ? + idcs_ISO646_KR, + idcs_csKSC5636, + { References: RFC1345,KXS2 } + + { MIB: 2008 } + idcs_DEC_MCS, // Codepage: ? + idcs_dec, + idcs_csDECMCS, + { References: RFC1345,KXS2 } + { Source: + VAX/VMS User's Manual, + Order Number: AI-Y517A-TE, April 1986. } + + { MIB: 2004 } + idcs_hp_roman8, // Codepage: ? + idcs_roman8, + idcs_r8, + idcs_csHPRoman8, + { References: HP-PCL5,RFC1345,KXS2 } + { Source: + LaserJet IIP Printer User's Manual, + HP part no 33471-90901, Hewlet-Packard, June 1989. } + + { Western European (Mac) } + { MIB: 2027 } + idcs_macintosh, // Codepage: 10000 + idcs_mac, + idcs_csMacintosh, + { References: RFC1345,KXS2 } + { Source: + The Unicode Standard ver1.0, ISBN 0-201-56788-1, Oct 1991 } + + { IBM EBCDIC (US-Canada) } + { MIB: 2028 } + idcs_IBM037, // Codepage: 37 + idcs_cp037, + idcs_ebcdic_cp_us, + idcs_ebcdic_cp_ca, + idcs_ebcdic_cp_wt, + idcs_ebcdic_cp_nl, + idcs_csIBM037, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2029 } + idcs_IBM038, // Codepage: ? + idcs_EBCDIC_INT, + idcs_cp038, + idcs_csIBM038, + { References: RFC1345,KXS2 } + { Source: + IBM 3174 Character Set Ref, GA27-3831-02, March 1990 } + + { IBM EBCDIC (Germany) } + { MIB: 2030 } + idcs_IBM273, // Codepage: 20273 + idcs_CP273, + idcs_csIBM273, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2031 } + idcs_IBM274, // Codepage: ? + idcs_EBCDIC_BE, + idcs_CP274, + idcs_csIBM274, + { References: RFC1345,KXS2 } + { Source: + IBM 3174 Character Set Ref, GA27-3831-02, March 1990 } + + { MIB: 2032 } + idcs_IBM275, // Codepage: ? + idcs_EBCDIC_BR, + idcs_cp275, + idcs_csIBM275, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Denmark-Norway) } + { MIB: 2033 } + idcs_IBM277, // Codepage: 20277 + idcs_EBCDIC_CP_DK, + idcs_EBCDIC_CP_NO, + idcs_csIBM277, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Finland-Sweden) } + { MIB: 2034 } + idcs_IBM278, // Codepage: 20278 + idcs_CP278, + idcs_ebcdic_cp_fi, + idcs_ebcdic_cp_se, + idcs_csIBM278, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Italy) } + { MIB: 2035 } + idcs_IBM280, // Codepage: 20280 + idcs_CP280, + idcs_ebcdic_cp_it, + idcs_csIBM280, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2036 } + idcs_IBM281, // Codepage: ? + idcs_EBCDIC_JP_E, + idcs_cp281, + idcs_csIBM281, + { References: RFC1345,KXS2 } + { Source: + IBM 3174 Character Set Ref, GA27-3831-02, March 1990 } + + { IBM EBCDIC (Spain) } + { MIB: 2037 } + idcs_IBM284, // Codepage: 20284 + idcs_CP284, + idcs_ebcdic_cp_es, + idcs_csIBM284, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (UK) } + { MIB: 2038 } + idcs_IBM285, // Codepage: 20285 + idcs_CP285, + idcs_ebcdic_cp_gb, + idcs_csIBM285, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Japanese katakana) } + { MIB: 2039 } + idcs_IBM290, // Codepage: 20290 + idcs_cp290, + idcs_EBCDIC_JP_kana, + idcs_csIBM290, + { References: RFC1345,KXS2 } + { Source: + IBM 3174 Character Set Ref, GA27-3831-02, March 1990 } + + { IBM EBCDIC (France) } + { MIB: 2040 } + idcs_IBM297, // Codepage: 20297 + idcs_cp297, + idcs_ebcdic_cp_fr, + idcs_csIBM297, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Arabic) } + { MIB: 2041 } + idcs_IBM420, // Codepage: 20420 + idcs_cp420, + idcs_ebcdic_cp_ar1, + idcs_csIBM420, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990, + IBM NLS RM p 11-11 } + + { IBM EBCDIC (Greek) } + { MIB: 2042 } + idcs_IBM423, // Codepage: 20423 + idcs_cp423, + idcs_ebcdic_cp_gr, + idcs_csIBM423, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Hebrew) } + { MIB: 2043 } + idcs_IBM424, // Codepage: 20424 + idcs_cp424, + idcs_ebcdic_cp_he, + idcs_csIBM424, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { OEM United States } + { MIB: 2011 } + idcs_IBM437, // Codepage: 437 + idcs_cp437, + idcs_437, + idcs_csPC8CodePage437, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (International) } + { MIB: 2044 } + idcs_IBM500, // Codepage: 500 + idcs_CP500, + idcs_ebcdic_cp_be, + idcs_ebcdic_cp_ch, + idcs_csIBM500, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Baltic (DOS) } + { MIB: 2087 } + idcs_IBM775, // Codepage: 775 + idcs_cp775, + idcs_csPC775Baltic, + { References: HP-PCL5 } + { Source: + HP PCL 5 Comparison Guide (P/N 5021-0329) pp B-13, 1996 } + + { Western European (DOS) } + { MIB: 2009 } + idcs_IBM850, // Codepage: 850 + idcs_cp850, + idcs_850, + idcs_csPC850Multilingual, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2045 } + idcs_IBM851, // Codepage: ? + idcs_cp851, + idcs_851, + idcs_csIBM851, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Central European (DOS) } + { MIB: 2010 } + idcs_IBM852, // Codepage: 852 + idcs_cp852, + idcs_852, + idcs_csPCp852, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { OEM Cyrillic } + { MIB: 2046 } + idcs_IBM855, // Codepage: 855 + idcs_cp855, + idcs_855, + idcs_csIBM855, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Turkish (DOS) } + { MIB: 2047 } + idcs_IBM857, // Codepage: 857 + idcs_cp857, + idcs_857, + idcs_csIBM857, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Portuguese (DOS) } + { MIB: 2048 } + idcs_IBM860, // Codepage: 860 + idcs_cp860, + idcs_860, + idcs_csIBM860, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Icelandic (DOS) } + { MIB: 2049 } + idcs_IBM861, // Codepage: 861 + idcs_cp861, + idcs_861, + idcs_cp_is, + idcs_csIBM861, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2013 } + idcs_IBM862, // Codepage: ? + idcs_cp862, + idcs_862, + idcs_csPC862LatinHebrew, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { French Canadian (DOS) } + { MIB: 2050 } + idcs_IBM863, // Codepage: 863 + idcs_cp863, + idcs_863, + idcs_csIBM863, + { References: RFC1345,KXS2 } + { Source: + IBM Keyboard layouts and code pages, PN 07G4586 June 1991 } + + { Arabic (864) } + { MIB: 2051 } + idcs_IBM864, // Codepage: 864 + idcs_cp864, + idcs_csIBM864, + { References: RFC1345,KXS2 } + { Source: + IBM Keyboard layouts and code pages, PN 07G4586 June 1991 } + + { Nordic (DOS) } + { MIB: 2052 } + idcs_IBM865, // Codepage: 865 + idcs_cp865, + idcs_865, + idcs_csIBM865, + { References: RFC1345,KXS2 } + { Source: + IBM DOS 3.3 Ref (Abridged), 94X9575 (Feb 1987) } + + { Cyrillic (DOS) } + { MIB: 2086 } + idcs_IBM866, // Codepage: 866 + idcs_cp866, + idcs_866, + idcs_csIBM866, + { References: Pond } + { Source: + IBM NLDG Volume 2 (SE09-8002-03) August 1994 } + + { MIB: 2053 } + idcs_IBM868, // Codepage: ? + idcs_CP868, + idcs_cp_ar, + idcs_csIBM868, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { Greek, Modern (DOS) } + { MIB: 2054 } + idcs_IBM869, // Codepage: 869 + idcs_cp869, + idcs_869, + idcs_cp_gr, + idcs_csIBM869, + { References: RFC1345,KXS2 } + { Source: + IBM Keyboard layouts and code pages, PN 07G4586 June 1991 } + + { IBM EBCDIC (Multilingual Latin-2) } + { MIB: 2055 } + idcs_IBM870, // Codepage: 870 + idcs_CP870, + idcs_ebcdic_cp_roece, + idcs_ebcdic_cp_yu, + idcs_csIBM870, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Icelandic) } + { MIB: 2056 } + idcs_IBM871, // Codepage: 20871 + idcs_CP871, + idcs_ebcdic_cp_is, + idcs_csIBM871, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Cyrillic Russian) } + { MIB: 2057 } + idcs_IBM880, // Codepage: 20880 + idcs_cp880, + idcs_EBCDIC_Cyrillic, + idcs_csIBM880, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2058 } + idcs_IBM891, // Codepage: ? + idcs_cp891, + idcs_csIBM891, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2059 } + idcs_IBM903, // Codepage: ? + idcs_cp903, + idcs_csIBM903, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2060 } + idcs_IBM904, // Codepage: ? + idcs_cp904, + idcs_904, + idcs_csIBBM904, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Turkish) } + { MIB: 2061 } + idcs_IBM905, // Codepage: 20905 + idcs_CP905, + idcs_ebcdic_cp_tr, + idcs_csIBM905, + { References: RFC1345,KXS2 } + { Source: + IBM 3174 Character Set Ref, GA27-3831-02, March 1990 } + + { MIB: 2062 } + idcs_IBM918, // Codepage: ? + idcs_CP918, + idcs_ebcdic_cp_ar2, + idcs_csIBM918, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { IBM EBCDIC (Turkish Latin-5) } + { MIB: 2063 } + idcs_IBM1026, // Codepage: 1026 + idcs_CP1026, + idcs_csIBM1026, + { References: RFC1345,KXS2 } + { Source: + IBM NLS RM Vol2 SE09-8002-01, March 1990 } + + { MIB: 2064 } + idcs_EBCDIC_AT_DE, // Codepage: ? + idcs_csIBMEBCDICATDE, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2065 } + idcs_EBCDIC_AT_DE_A, // Codepage: ? + idcs_csEBCDICATDEA, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2066 } + idcs_EBCDIC_CA_FR, // Codepage: ? + idcs_csEBCDICCAFR, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2067 } + idcs_EBCDIC_DK_NO, // Codepage: ? + idcs_csEBCDICDKNO, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2068 } + idcs_EBCDIC_DK_NO_A, // Codepage: ? + idcs_csEBCDICDKNOA, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2069 } + idcs_EBCDIC_FI_SE, // Codepage: ? + idcs_csEBCDICFISE, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2070 } + idcs_EBCDIC_FI_SE_A, // Codepage: ? + idcs_csEBCDICFISEA, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2071 } + idcs_EBCDIC_FR, // Codepage: ? + idcs_csEBCDICFR, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2072 } + idcs_EBCDIC_IT, // Codepage: ? + idcs_csEBCDICIT, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2073 } + idcs_EBCDIC_PT, // Codepage: ? + idcs_csEBCDICPT, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2074 } + idcs_EBCDIC_ES, // Codepage: ? + idcs_csEBCDICES, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2075 } + idcs_EBCDIC_ES_A, // Codepage: ? + idcs_csEBCDICESA, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2076 } + idcs_EBCDIC_ES_S, // Codepage: ? + idcs_csEBCDICESS, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2077 } + idcs_EBCDIC_UK, // Codepage: ? + idcs_csEBCDICUK, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2078 } + idcs_EBCDIC_US, // Codepage: ? + idcs_csEBCDICUS, + { References: RFC1345,KXS2 } + { Source: + IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 } + + { MIB: 2079 } + idcs_UNKNOWN_8BIT, // Codepage: ? + idcs_csUnknown8BiT, + { References: RFC1428 } + + { MIB: 2080 } + idcs_MNEMONIC, // Codepage: ? + idcs_csMnemonic, + { References: RFC1345,KXS2 } + { Source: + RFC 1345, also known as "mnemonic+ascii+38" } + + { MIB: 2081 } + idcs_MNEM, // Codepage: ? + idcs_csMnem, + { References: RFC1345,KXS2 } + { Source: + RFC 1345, also known as "mnemonic+ascii+8200" } + + { MIB: 2082 } + idcs_VISCII, // Codepage: ? + idcs_csVISCII, + { References: RFC1456 } + { Source: + RFC 1456 } + + { MIB: 2083 } + idcs_VIQR, // Codepage: ? + idcs_csVIQR, + { References: RFC1456 } + { Source: + RFC 1456 } + + { Cyrillic (KOI8-R) } + { MIB: 2084 } + idcs_KOI8_R, // Codepage: 20866 + idcs_csKOI8R, + { References: RFC1489 } + { Source: + RFC 1489, based on GOST-19768-74, ISO-6937/8, + INIS-Cyrillic, ISO-5427. } + + { Cyrillic (KOI8-U) } + { MIB: 2088 } + idcs_KOI8_U, // Codepage: 21866 + { References: RFC2319 } + { Source: + RFC 2319 } + + { OEM Multilingual Latin I } + { MIB: 2089 } + idcs_IBM00858, // Codepage: 858 + idcs_CCSID00858, + idcs_CP00858, + idcs_PC_Multilingual_850_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM00858) [Mahdi] } + + { IBM Latin-1 } + { MIB: 2090 } + idcs_IBM00924, // Codepage: 20924 + idcs_CCSID00924, + idcs_CP00924, + idcs_ebcdic_Latin9__euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM00924) [Mahdi] } + + { IBM EBCDIC (US-Canada-Euro) } + { MIB: 2091 } + idcs_IBM01140, // Codepage: 1140 + idcs_CCSID01140, + idcs_CP01140, + idcs_ebcdic_us_37_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01140) [Mahdi] } + + { IBM EBCDIC (Germany-Euro) } + { MIB: 2092 } + idcs_IBM01141, // Codepage: 1141 + idcs_CCSID01141, + idcs_CP01141, + idcs_ebcdic_de_273_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01141) [Mahdi] } + + { IBM EBCDIC (Denmark-Norway-Euro) } + { MIB: 2093 } + idcs_IBM01142, // Codepage: 1142 + idcs_CCSID01142, + idcs_CP01142, + idcs_ebcdic_dk_277_euro, + idcs_ebcdic_no_277_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01142) [Mahdi] } + + { IBM EBCDIC (Finland-Sweden-Euro) } + { MIB: 2094 } + idcs_IBM01143, // Codepage: 1143 + idcs_CCSID01143, + idcs_CP01143, + idcs_ebcdic_fi_278_euro, + idcs_ebcdic_se_278_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01143) [Mahdi] } + + { IBM EBCDIC (Italy-Euro) } + { MIB: 2095 } + idcs_IBM01144, // Codepage: 1144 + idcs_CCSID01144, + idcs_CP01144, + idcs_ebcdic_it_280_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01144) [Mahdi] } + + { IBM EBCDIC (Spain-Euro) } + { MIB: 2096 } + idcs_IBM01145, // Codepage: 1145 + idcs_CCSID01145, + idcs_CP01145, + idcs_ebcdic_es_284_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01145) [Mahdi] } + + { IBM EBCDIC (UK-Euro) } + { MIB: 2097 } + idcs_IBM01146, // Codepage: 1146 + idcs_CCSID01146, + idcs_CP01146, + idcs_ebcdic_gb_285_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01146) [Mahdi] } + + { IBM EBCDIC (France-Euro) } + { MIB: 2098 } + idcs_IBM01147, // Codepage: 1147 + idcs_CCSID01147, + idcs_CP01147, + idcs_ebcdic_fr_297_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01147) [Mahdi] } + + { IBM EBCDIC (International-Euro) } + { MIB: 2099 } + idcs_IBM01148, // Codepage: 1148 + idcs_CCSID01148, + idcs_CP01148, + idcs_ebcdic_international_500_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01148) [Mahdi] } + + { IBM EBCDIC (Icelandic-Euro) } + { MIB: 2100 } + idcs_IBM01149, // Codepage: 1149 + idcs_CCSID01149, + idcs_CP01149, + idcs_ebcdic_is_871_euro, + { Source: + IBM See (http://www.iana.org/assignments/charset-reg/IBM01149) [Mahdi] } + + { MIB: 2101 } + idcs_Big5_HKSCS, // Codepage: ? + { References: Yick } + { Source: + See (http://www.iana.org/assignments/charset-reg/Big5-HKSCS) } + + { MIB: 1013 } + idcs_UTF_16BE, // Codepage: 1201 + { References: RFC2781 } + { Source: + RFC 2781 } + + { MIB: 1014 } + idcs_UTF_16LE, // Codepage: 1200 + { References: RFC2781 } + { Source: + RFC 2781 } + + { Unicode } + { MIB: 1015 } + idcs_UTF_16, // Codepage: 1200 + { References: RFC2781 } + { Source: + RFC 2781 } + + { MIB: 1016 } + idcs_CESU_8, // Codepage: ? + idcs_csCESU_8, + { References: Phipps } + { Source: + } + + { Unicode (UTF-32) } + { MIB: 1017 } + idcs_UTF_32, // Codepage: 12000 + { References: Davis } + { Source: + } + + { Unicode (UTF-32 Big endian) } + { MIB: 1018 } + idcs_UTF_32BE, // Codepage: 12001 + { References: Davis } + { Source: + } + + { MIB: 1019 } + idcs_UTF_32LE, // Codepage: 12000 + { References: Davis } + { Source: + } + + { MIB: 103 } + idcs_UNICODE_1_1_UTF_7, // Codepage: ? + idcs_csUnicode11UTF7, + { References: RFC1642 } + { Source: + RFC 1642 } + + { Unicode (UTF-8) } + { MIB: 106 } + idcs_UTF_8, // Codepage: 65001 + { References: RFC3629 } + { Source: + RFC 3629 } + + { Estonian (ISO) } + { MIB: 109 } + idcs_ISO_8859_13, // Codepage: 28603 + { Source: + ISO See (http://www.iana.org/assignments/charset-reg/ISO-8859-13)[Tumasonis] } + + { MIB: 110 } + idcs_ISO_8859_14, // Codepage: ? + idcs_iso_ir_199, + idcs_ISO_8859_14_1998, + idcs_ISO_8859_14_, + idcs_latin8, + idcs_iso_celtic, + idcs_l8, + { Source: + ISO See (http://www.iana.org/assignments/charset-reg/ISO-8859-14) [Simonsen] } + + { Latin 9 (ISO) } + { MIB: 111 } + idcs_ISO_8859_15, // Codepage: 28605 + idcs_ISO_8859_15_, + idcs_Latin_9, + { Source: + ISO + Please see: } + + { MIB: 112 } + idcs_ISO_8859_16, // Codepage: ? + idcs_iso_ir_226, + idcs_ISO_8859_16_2001, + idcs_ISO_8859_16_, + idcs_latin10, + idcs_l10, + { Source: + ISO } + + { MIB: 113 } + idcs_GBK, // Codepage: 936 + idcs_CP936, + idcs_MS936, + idcs_windows_936, + { Source: + Chinese IT Standardization Technical Committee + Please see: } + + { Chinese Simplified (GB18030) } + { MIB: 114 } + idcs_GB18030, // Codepage: 54936 + { Source: + Chinese IT Standardization Technical Committee + Please see: } + + { MIB: 16 } + idcs_JIS_Encoding, // Codepage: ? + idcs_csJISEncoding, + { Source: + JIS X 0202-1991. Uses ISO 2022 escape sequences to + shift code sets as documented in JIS X 0202-1991. } + + { Japanese (Shift-JIS) } + { MIB: 17 } + idcs_Shift_JIS, // Codepage: 932 + idcs_MS_Kanji, + idcs_csShiftJIS, + { Source: + This charset is an extension of csHalfWidthKatakana by + adding graphic characters in JIS X 0208. The CCS's are + JIS X0201:1997 and JIS X0208:1997. The + complete definition is shown in Appendix 1 of JIS + X0208:1997. + This charset can be used for the top-level media type "text". } + + { Japanese (EUC) } + { MIB: 18 } + idcs_EUC_JP, // Codepage: 20932 [need to verify] + idcs_Extended_UNIX_Code_Packed_Format_for_Japanese, + idcs_csEUCPkdFmtJapanese, + { Source: + Standardized by OSF, UNIX International, and UNIX Systems + Laboratories Pacific. Uses ISO 2022 rules to select + code set 0: US-ASCII (a single 7-bit byte set) + code set 1: JIS X0208-1990 (a double 8-bit byte set) + restricted to A0-FF in both bytes + code set 2: Half Width Katakana (a single 7-bit byte set) + requiring SS2 as the character prefix + code set 3: JIS X0212-1990 (a double 7-bit byte set) + restricted to A0-FF in both bytes + requiring SS3 as the character prefix } + + { MIB: 19 } + idcs_Extended_UNIX_Code_Fixed_Width_for_Japanese, // Codepage: ? + idcs_csEUCFixWidJapanese, + { Source: + Used in Japan. Each character is 2 octets. + code set 0: US-ASCII (a single 7-bit byte set) + 1st byte = 00 + 2nd byte = 20-7E + code set 1: JIS X0208-1990 (a double 7-bit byte set) + restricted to A0-FF in both bytes + code set 2: Half Width Katakana (a single 7-bit byte set) + 1st byte = 00 + 2nd byte = A0-FF + code set 3: JIS X0212-1990 (a double 7-bit byte set) + restricted to A0-FF in + the first byte + and 21-7E in the second byte } + + { Hebrew (DOS) } + { MIB: -1 } + idcs_DOS_862, // Codepage: 862 + + { Thai (Windows) } + { MIB: -1 } + idcs_windows_874, // Codepage: 874 + + { IBM EBCDIC (Greek Modern) } + { MIB: -1 } + idcs_cp875, // Codepage: 875 + + { IBM Latin-1 } + { MIB: -1 } + idcs_IBM01047, // Codepage: 1047 + + { Unicode (Big endian) } + { MIB: -1 } + idcs_unicodeFFFE, // Codepage: 1201 + + { Korean (Johab) } + { MIB: -1 } + idcs_Johab, // Codepage: 1361 + + { Japanese (Mac) } + { MIB: -1 } + idcs_x_mac_japanese, // Codepage: 10001 + + { Chinese Traditional (Mac) } + { MIB: -1 } + idcs_x_mac_chinesetrad, // Codepage: 10002 + + { Korean (Mac) } + { MIB: -1 } + idcs_x_mac_korean, // Codepage: 10003 + + { Arabic (Mac) } + { MIB: -1 } + idcs_x_mac_arabic, // Codepage: 10004 + + { Hebrew (Mac) } + { MIB: -1 } + idcs_x_mac_hebrew, // Codepage: 10005 + + { Greek (Mac) } + { MIB: -1 } + idcs_x_mac_greek, // Codepage: 10006 + + { Cyrillic (Mac) } + { MIB: -1 } + idcs_x_mac_cyrillic, // Codepage: 10007 + + { Chinese Simplified (Mac) } + { MIB: -1 } + idcs_x_mac_chinesesimp, // Codepage: 10008 + + { Romanian (Mac) } + { MIB: -1 } + idcs_x_mac_romanian, // Codepage: 10010 + + { Ukrainian (Mac) } + { MIB: -1 } + idcs_x_mac_ukrainian, // Codepage: 10017 + + { Thai (Mac) } + { MIB: -1 } + idcs_x_mac_thai, // Codepage: 10021 + + { Central European (Mac) } + { MIB: -1 } + idcs_x_mac_ce, // Codepage: 10029 + + { Icelandic (Mac) } + { MIB: -1 } + idcs_x_mac_icelandic, // Codepage: 10079 + + { Turkish (Mac) } + { MIB: -1 } + idcs_x_mac_turkish, // Codepage: 10081 + + { Croatian (Mac) } + { MIB: -1 } + idcs_x_mac_croatian, // Codepage: 10082 + + { Chinese Traditional (CNS) } + { MIB: -1 } + idcs_x_Chinese_CNS, // Codepage: 20000 + + { TCA Taiwan } + { MIB: -1 } + idcs_x_cp20001, // Codepage: 20001 + + { Chinese Traditional (Eten) } + { MIB: -1 } + idcs_x_Chinese_Eten, // Codepage: 20002 + + { IBM5550 Taiwan } + { MIB: -1 } + idcs_x_cp20003, // Codepage: 20003 + + { TeleText Taiwan } + { MIB: -1 } + idcs_x_cp20004, // Codepage: 20004 + + { Wang Taiwan } + { MIB: -1 } + idcs_x_cp20005, // Codepage: 20005 + + { Western European (IA5) } + { MIB: -1 } + idcs_x_IA5, // Codepage: 20105 + + { German (IA5) } + { MIB: -1 } + idcs_x_IA5_German, // Codepage: 20106 + + { Swedish (IA5) } + { MIB: -1 } + idcs_x_IA5_Swedish, // Codepage: 20107 + + { Norwegian (IA5) } + { MIB: -1 } + idcs_x_IA5_Norwegian, // Codepage: 20108 + + { T.61 } + { MIB: -1 } + idcs_x_cp20261, // Codepage: 20261 + + { ISO-6937 } + { MIB: -1 } + idcs_x_cp20269, // Codepage: 20269 + + { IBM EBCDIC (Korean Extended) } + { MIB: -1 } + idcs_x_EBCDIC_KoreanExtended, // Codepage: 20833 + + { Chinese Simplified (GB2312-80) } + { MIB: -1 } + idcs_x_cp20936, // Codepage: 20936 + + { Korean Wansung } + { MIB: -1 } + idcs_x_cp20949, // Codepage: 20949 + + { IBM EBCDIC (Cyrillic Serbian-Bulgarian) } + { MIB: -1 } + idcs_cp1025, // Codepage: 21025 + + { Europa } + { MIB: -1 } + idcs_x_Europa, // Codepage: 29001 + + { Chinese Simplified (ISO-2022) } + { MIB: -1 } + idcs_x_cp50227, // Codepage: 50227 + + { Chinese Simplified (EUC) } + { MIB: -1 } + idcs_EUC_CN, // Codepage: 51936 + + { ISCII Devanagari } + { MIB: -1 } + idcs_x_iscii_de, // Codepage: 57002 + + { ISCII Bengali } + { MIB: -1 } + idcs_x_iscii_be, // Codepage: 57003 + + { ISCII Tamil } + { MIB: -1 } + idcs_x_iscii_ta, // Codepage: 57004 + + { ISCII Telugu } + { MIB: -1 } + idcs_x_iscii_te, // Codepage: 57005 + + { ISCII Assamese } + { MIB: -1 } + idcs_x_iscii_as, // Codepage: 57006 + + { ISCII Oriya } + { MIB: -1 } + idcs_x_iscii_or, // Codepage: 57007 + + { ISCII Kannada } + { MIB: -1 } + idcs_x_iscii_ka, // Codepage: 57008 + + { ISCII Malayalam } + { MIB: -1 } + idcs_x_iscii_ma, // Codepage: 57009 + + { ISCII Gujarati } + { MIB: -1 } + idcs_x_iscii_gu, // Codepage: 57010 + + { ISCII Punjabi } + { MIB: -1 } + idcs_x_iscii_pa, // Codepage: 57011 + + { IBM EBCDIC (Arabic) } + { MIB: -1 } + idcs_x_EBCDIC_Arabic, // Codepage: 20420 + + { IBM EBCDIC (Cyrillic Russian) } + { MIB: -1 } + idcs_x_EBCDIC_CyrillicRussian, // Codepage: 20880 + + { IBM EBCDIC (Cyrillic Serbian-Bulgarian) } + { MIB: -1 } + idcs_x_EBCDIC_CyrillicSerbianBulgarian, // Codepage: 21025 + + { IBM EBCDIC (Denmark-Norway) } + { MIB: -1 } + idcs_x_EBCDIC_DenmarkNorway, // Codepage: 20277 + + { IBM EBCDIC (Denmark-Norway-Euro) } + { MIB: -1 } + idcs_x_ebcdic_denmarknorway_euro, // Codepage: 1142 + + { IBM EBCDIC (Finland-Sweden) } + { MIB: -1 } + idcs_x_EBCDIC_FinlandSweden, // Codepage: 20278 + + { IBM EBCDIC (Finland-Sweden-Euro) } + { MIB: -1 } + idcs_x_ebcdic_finlandsweden_euro, // Codepage: 1143 + idcs_X_EBCDIC_France, + + { IBM EBCDIC (France-Euro) } + { MIB: -1 } + idcs_x_ebcdic_france_euro, // Codepage: 1147 + + { IBM EBCDIC (Germany) } + { MIB: -1 } + idcs_x_EBCDIC_Germany, // Codepage: 20273 + + { IBM EBCDIC (Germany-Euro) } + { MIB: -1 } + idcs_x_ebcdic_germany_euro, // Codepage: 1141 + + { IBM EBCDIC (Greek Modern) } + { MIB: -1 } + idcs_x_EBCDIC_GreekModern, // Codepage: 875 + + { IBM EBCDIC (Greek) } + { MIB: -1 } + idcs_x_EBCDIC_Greek, // Codepage: 20423 + + { IBM EBCDIC (Hebrew) } + { MIB: -1 } + idcs_x_EBCDIC_Hebrew, // Codepage: 20424 + + { IBM EBCDIC (Icelandic) } + { MIB: -1 } + idcs_x_EBCDIC_Icelandic, // Codepage: 20871 + + { IBM EBCDIC (Icelandic-Euro) } + { MIB: -1 } + idcs_x_ebcdic_icelandic_euro, // Codepage: 1149 + + { IBM EBCDIC (International-Euro) } + { MIB: -1 } + idcs_x_ebcdic_international_euro, // Codepage: 1148 + + { IBM EBCDIC (Italy) } + { MIB: -1 } + idcs_x_EBCDIC_Italy, // Codepage: 20280 + + { IBM EBCDIC (Italy-Euro) } + { MIB: -1 } + idcs_x_ebcdic_italy_euro, // Codepage: 1144 + + { IBM EBCDIC (Japanese and Japanese Katakana) } + { MIB: -1 } + idcs_x_EBCDIC_JapaneseAndKana, // Codepage: 50930 + + { IBM EBCDIC (Japanese and Japanese-Latin) } + { MIB: -1 } + idcs_x_EBCDIC_JapaneseAndJapaneseLatin, // Codepage: 50939 + + { IBM EBCDIC (Japanese and US-Canada) } + { MIB: -1 } + idcs_x_EBCDIC_JapaneseAndUSCanada, // Codepage: 50931 + + { IBM EBCDIC (Japanese katakana) } + { MIB: -1 } + idcs_x_EBCDIC_JapaneseKatakana, // Codepage: 20290 + + { IBM EBCDIC (Korean and Korean Extended) } + { MIB: -1 } + idcs_x_EBCDIC_KoreanAndKoreanExtended, // Codepage: 50933 + + { IBM EBCDIC (Simplified Chinese) } + { MIB: -1 } + idcs_x_EBCDIC_SimplifiedChinese, // Codepage: 50935 + + { IBM EBCDIC (Spain) } + { MIB: -1 } + idcs_X_EBCDIC_Spain, // Codepage: 20284 + + { IBM EBCDIC (Spain-Euro) } + { MIB: -1 } + idcs_x_ebcdic_spain_euro, // Codepage: 1145 + + { IBM EBCDIC (Thai) } + { MIB: -1 } + idcs_x_EBCDIC_Thai, // Codepage: 20838 + + { IBM EBCDIC (Traditional Chinese) } + { MIB: -1 } + idcs_x_EBCDIC_TraditionalChinese, // Codepage: 50937 + + { IBM EBCDIC (Turkish) } + { MIB: -1 } + idcs_x_EBCDIC_Turkish, // Codepage: 20905 + + { IBM EBCDIC (UK) } + { MIB: -1 } + idcs_x_EBCDIC_UK, // Codepage: 20285 + + { IBM EBCDIC (UK-Euro) } + { MIB: -1 } + idcs_x_ebcdic_uk_euro, // Codepage: 1146 + + { IBM EBCDIC (US-Canada-Euro) } + { MIB: -1 } + idcs_x_ebcdic_cp_us_euro, // Codepage: 1140 + + { MIB: 115 } + idcs_OSD_EBCDIC_DF04_15, // Codepage: ? + { Source: + Fujitsu-Siemens standard mainframe EBCDIC encoding + Please see: } + + { MIB: 116 } + idcs_OSD_EBCDIC_DF03_IRV, // Codepage: ? + { Source: + Fujitsu-Siemens standard mainframe EBCDIC encoding + Please see: } + + { MIB: 117 } + idcs_OSD_EBCDIC_DF04_1, // Codepage: ? + { Source: + Fujitsu-Siemens standard mainframe EBCDIC encoding + Please see: } + + { MIB: 118 } + idcs_ISO_11548_1, // Codepage: ? + idcs_ISO_11548_1_, + idcs_ISO_TR_11548_1, + idcs_csISO115481, + { Source: + See [Thibault] } + + { MIB: 119 } + idcs_KZ_1048, // Codepage: ? + idcs_STRK1048_2002, + idcs_RK1048, + idcs_csKZ1048, + { Source: + See [Veremeev, Kikkarin] } + + { MIB: 1000 } + idcs_ISO_10646_UCS_2, // Codepage: ? + idcs_csUnicode, + { Source: + the 2-octet Basic Multilingual Plane, aka Unicode + this needs to specify network byte order: the standard + does not specify (it is a 16-bit integer space) } + + { MIB: 1001 } + idcs_ISO_10646_UCS_4, // Codepage: ? + idcs_csUCS4, + { Source: + the full code space. (same comment about byte order, + these are 31-bit numbers. } + + { MIB: 1010 } + idcs_UNICODE_1_1, // Codepage: ? + idcs_csUnicode11, + { References: RFC1641 } + { Source: + RFC 1641 } + + { MIB: 1011 } + idcs_SCSU, // Codepage: ? + { Source: + SCSU See (http://www.iana.org/assignments/charset-reg/SCSU) [Scherer] } + + { Unicode (UTF-7) } + { MIB: 1012 } + idcs_UTF_7, // Codepage: 65000 + { References: RFC2152 } + { Source: + RFC 2152 } + + { MIB: 1002 } + idcs_ISO_10646_UCS_Basic, // Codepage: ? + idcs_csUnicodeASCII, + { Source: + ASCII subset of Unicode. Basic Latin = collection 1 + See ISO 10646, Appendix A } + + { MIB: 1003 } + idcs_ISO_10646_Unicode_Latin1, // Codepage: ? + idcs_csUnicodeLatin1, + idcs_ISO_10646, + { Source: + ISO Latin-1 subset of Unicode. Basic Latin and Latin-1 + Supplement = collections 1 and 2. See ISO 10646, + Appendix A. See RFC 1815. } + + { MIB: -1 } + idcs_ISO_10646_J_1, // Codepage: ? + { Source: + ISO 10646 Japanese, see RFC 1815. } + + { MIB: 1005 } + idcs_ISO_Unicode_IBM_1261, // Codepage: ? + idcs_csUnicodeIBM1261, + { Source: + IBM Latin-2, -3, -5, Extended Presentation Set, GCSGID: 1261 } + + { MIB: 1006 } + idcs_ISO_Unicode_IBM_1268, // Codepage: ? + idcs_csUnicodeIBM1268, + { Source: + IBM Latin-4 Extended Presentation Set, GCSGID: 1268 } + + { MIB: 1007 } + idcs_ISO_Unicode_IBM_1276, // Codepage: ? + idcs_csUnicodeIBM1276, + { Source: + IBM Cyrillic Greek Extended Presentation Set, GCSGID: 1276 } + + { MIB: 1008 } + idcs_ISO_Unicode_IBM_1264, // Codepage: ? + idcs_csUnicodeIBM1264, + { Source: + IBM Arabic Presentation Set, GCSGID: 1264 } + + { MIB: 1009 } + idcs_ISO_Unicode_IBM_1265, // Codepage: ? + idcs_csUnicodeIBM1265, + { Source: + IBM Hebrew Presentation Set, GCSGID: 1265 } + + { MIB: 1020 } + idcs_BOCU_1, // Codepage: ? + idcs_csBOCU_1, + { References: Scherer } + { Source: + http://www.unicode.org/notes/tn6/ } + + { MIB: 2000 } + idcs_ISO_8859_1_Windows_3_0_Latin_1, // Codepage: ? + idcs_csWindows30Latin1, + { References: HP-PCL5 } + { Source: + Extended ISO 8859-1 Latin-1 for Windows 3.0. + PCL Symbol Set id: 9U } + + { MIB: 2001 } + idcs_ISO_8859_1_Windows_3_1_Latin_1, // Codepage: ? + idcs_csWindows31Latin1, + { References: HP-PCL5 } + { Source: + Extended ISO 8859-1 Latin-1 for Windows 3.1. + PCL Symbol Set id: 19U } + + { MIB: 2002 } + idcs_ISO_8859_2_Windows_Latin_2, // Codepage: ? + idcs_csWindows31Latin2, + { References: HP-PCL5 } + { Source: + Extended ISO 8859-2. Latin-2 for Windows 3.1. + PCL Symbol Set id: 9E } + + { MIB: 2003 } + idcs_ISO_8859_9_Windows_Latin_5, // Codepage: ? + idcs_csWindows31Latin5, + { References: HP-PCL5 } + { Source: + Extended ISO 8859-9. Latin-5 for Windows 3.1 + PCL Symbol Set id: 5T } + + { MIB: 2005 } + idcs_Adobe_Standard_Encoding, // Codepage: ? + idcs_csAdobeStandardEncoding, + { References: Adobe } + { Source: + PostScript Language Reference Manual + PCL Symbol Set id: 10J } + + { MIB: 2006 } + idcs_Ventura_US, // Codepage: ? + idcs_csVenturaUS, + { References: HP-PCL5 } + { Source: + Ventura US. ASCII plus characters typically used in + publishing, like pilcrow, copyright, registered, trade mark, + section, dagger, and double dagger in the range A0 (hex) + to FF (hex). + PCL Symbol Set id: 14J } + + { MIB: 2007 } + idcs_Ventura_International, // Codepage: ? + idcs_csVenturaInternational, + { References: HP-PCL5 } + { Source: + Ventura International. ASCII plus coded characters similar + to Roman8. + PCL Symbol Set id: 13J } + + { MIB: 2012 } + idcs_PC8_Danish_Norwegian, // Codepage: ? + idcs_csPC8DanishNorwegian, + { References: HP-PCL5 } + { Source: + PC Danish Norwegian + 8-bit PC set for Danish Norwegian + PCL Symbol Set id: 11U } + + { MIB: 2014 } + idcs_PC8_Turkish, // Codepage: ? + idcs_csPC8Turkish, + { References: HP-PCL5 } + { Source: + PC Latin Turkish. PCL Symbol Set id: 9T } + + { MIB: 2015 } + idcs_IBM_Symbols, // Codepage: ? + idcs_csIBMSymbols, + { References: IBM-CIDT } + { Source: + Presentation Set, CPGID: 259 } + + { IBM EBCDIC (Thai) } + { MIB: 2016 } + idcs_IBM_Thai, // Codepage: 20838 + idcs_csIBMThai, + { References: IBM-CIDT } + { Source: + Presentation Set, CPGID: 838 } + + { MIB: 2017 } + idcs_HP_Legal, // Codepage: ? + idcs_csHPLegal, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 1U } + + { MIB: 2018 } + idcs_HP_Pi_font, // Codepage: ? + idcs_csHPPiFont, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 15U } + + { MIB: 2019 } + idcs_HP_Math8, // Codepage: ? + idcs_csHPMath8, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 8M } + + { MIB: 2020 } + idcs_Adobe_Symbol_Encoding, // Codepage: ? + idcs_csHPPSMath, + { References: Adobe } + { Source: + PostScript Language Reference Manual + PCL Symbol Set id: 5M } + + { MIB: 2021 } + idcs_HP_DeskTop, // Codepage: ? + idcs_csHPDesktop, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 7J } + + { MIB: 2022 } + idcs_Ventura_Math, // Codepage: ? + idcs_csVenturaMath, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 6M } + + { MIB: 2023 } + idcs_Microsoft_Publishing, // Codepage: ? + idcs_csMicrosoftPublishing, + { References: HP-PCL5 } + { Source: + PCL 5 Comparison Guide, Hewlett-Packard, + HP part number 5961-0510, October 1992 + PCL Symbol Set id: 6J } + + { MIB: 2024 } + idcs_Windows_31J, // Codepage: ? + idcs_csWindows31J, + { Source: + Windows Japanese. A further extension of Shift_JIS + to include NEC special characters (Row 13), NEC + selection of IBM extensions (Rows 89 to 92), and IBM + extensions (Rows 115 to 119). The CCS's are + JIS X0201:1997, JIS X0208:1997, and these extensions. + This charset can be used for the top-level media type "text", + but it is of limited or specialized use (see RFC2278). + PCL Symbol Set id: 19K } + + { Chinese Simplified (GB2312) } + { MIB: 2025 } + idcs_GB2312, // Codepage: 936 + idcs_csGB2312, + { Source: + Chinese for People's Republic of China (PRC) mixed one byte, + two byte set: + 20-7E = one byte ASCII + A1-FE = two byte PRC Kanji + See GB 2312-80 + PCL Symbol Set Id: 18C } + + { Chinese Traditional (Big5) } + { MIB: 2026 } + idcs_Big5, // Codepage: 950 + idcs_csBig5, + { Source: + Chinese for Taiwan Multi-byte set. + PCL Symbol Set Id: 18T } + + { Chinese Simplified (HZ) } + { MIB: 2085 } + idcs_HZ_GB_2312, // Codepage: 52936 + { Source: + RFC 1842, RFC 1843 [RFC1842, RFC1843] } + + { MIB: 2102 } + idcs_IBM1047, // Codepage: ? + idcs_IBM_1047, + { References: Robrigado } + { Source: + IBM1047 (EBCDIC Latin 1/Open Systems) } + + { MIB: 2103 } + idcs_PTCP154, // Codepage: ? + idcs_csPTCP154, + idcs_PT154, + idcs_CP154, + idcs_Cyrillic_Asian, + { References: Uskov } + { Source: + See (http://www.iana.org/assignments/charset-reg/PTCP154) } + + { MIB: 2104 } + idcs_Amiga_1251, // Codepage: ? + idcs_Ami1251, + idcs_Amiga1251, + idcs_Ami_1251, + { Source: + See (http://www.amiga.ultranet.ru/Amiga-1251.html) } + + { MIB: 2105 } + idcs_KOI7_switched, // Codepage: ? + { Source: + See } + + { MIB: 2106 } + idcs_BRF, // Codepage: ? + idcs_csBRF, + { Source: + See [Thibault] } + + { MIB: 2107 } + idcs_TSCII, // Codepage: ? + idcs_csTSCII, + { Source: + See [Kalyanasundaram] } + + { Central European (Windows) } + { MIB: 2250 } + idcs_windows_1250, // Codepage: 1250 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1250) [Lazhintseva] } + + { Cyrillic (Windows) } + { MIB: 2251 } + idcs_windows_1251, // Codepage: 1251 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1251) [Lazhintseva] } + + { Western European (Windows) } + { MIB: 2252 } + idcs_windows_1252, // Codepage: 1252 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1252) [Wendt] } + + { Greek (Windows) } + { MIB: 2253 } + idcs_windows_1253, // Codepage: 1253 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1253) [Lazhintseva] } + + { Turkish (Windows) } + { MIB: 2254 } + idcs_windows_1254, // Codepage: 1254 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1254) [Lazhintseva] } + + { Hebrew (Windows) } + { MIB: 2255 } + idcs_windows_1255, // Codepage: 1255 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1255) [Lazhintseva] } + + { Arabic (Windows) } + { MIB: 2256 } + idcs_windows_1256, // Codepage: 1256 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1256) [Lazhintseva] } + + { Baltic (Windows) } + { MIB: 2257 } + idcs_windows_1257, // Codepage: 1257 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1257) [Lazhintseva] } + + { Vietnamese (Windows) } + { MIB: 2258 } + idcs_windows_1258, // Codepage: 1258 + { Source: + Microsoft (http://www.iana.org/assignments/charset-reg/windows-1258) [Lazhintseva] } + + { MIB: 2259 } + idcs_TIS_620, // Codepage: ? + { Source: + Thai Industrial Standards Institute (TISI) [Tantsetthi] } + + { Arabic (DOS) } + { MIB: -1 } + idcs_DOS_720, // Codepage: 720 + + { Greek (DOS) } + { MIB: -1 } + idcs_ibm737 // Codepage: 737 + + ); + +const + IdCharsetNames : array[Low(TIdCharSet)..High(TIdCharSet)] of string = ( + '', {invalid is empty} + 'US-ASCII', {do not localize} + 'ANSI_X3.4-1968', {do not localize} + 'iso-ir-6', {do not localize} + 'ANSI_X3.4-1986', {do not localize} + 'ISO_646.irv:1991', {do not localize} + 'ASCII', {do not localize} + 'ISO646-US', {do not localize} + 'us', {do not localize} + 'IBM367', {do not localize} + 'cp367', {do not localize} + 'csASCII', {do not localize} + 'ISO-10646-UTF-1', {do not localize} + 'csISO10646UTF1', {do not localize} + 'ISO_646.basic:1983', {do not localize} + 'ref', {do not localize} + 'csISO646basic1983', {do not localize} + 'INVARIANT', {do not localize} + 'csINVARIANT', {do not localize} + 'ISO_646.irv:1983', {do not localize} + 'iso-ir-2', {do not localize} + 'irv', {do not localize} + 'csISO2IntlRefVersion', {do not localize} + 'BS_4730', {do not localize} + 'iso-ir-4', {do not localize} + 'ISO646-GB', {do not localize} + 'gb', {do not localize} + 'uk', {do not localize} + 'csISO4UnitedKingdom', {do not localize} + 'NATS-SEFI', {do not localize} + 'iso-ir-8-1', {do not localize} + 'csNATSSEFI', {do not localize} + 'NATS-SEFI-ADD', {do not localize} + 'iso-ir-8-2', {do not localize} + 'csNATSSEFIADD', {do not localize} + 'NATS-DANO', {do not localize} + 'iso-ir-9-1', {do not localize} + 'csNATSDANO', {do not localize} + 'NATS-DANO-ADD', {do not localize} + 'iso-ir-9-2', {do not localize} + 'csNATSDANOADD', {do not localize} + 'SEN_850200_B', {do not localize} + 'iso-ir-10', {do not localize} + 'FI', {do not localize} + 'ISO646-FI', {do not localize} + 'ISO646-SE', {do not localize} + 'se', {do not localize} + 'csISO10Swedish', {do not localize} + 'SEN_850200_C', {do not localize} + 'iso-ir-11', {do not localize} + 'ISO646-SE2', {do not localize} + 'se2', {do not localize} + 'csISO11SwedishForNames', {do not localize} + 'KS_C_5601-1987', {do not localize} + 'iso-ir-149', {do not localize} + 'KS_C_5601-1989', {do not localize} + 'KSC_5601', {do not localize} + 'korean', {do not localize} + 'csKSC56011987', {do not localize} + 'ISO-2022-KR', {do not localize} + 'csISO2022KR', {do not localize} + 'EUC-KR', {do not localize} + 'csEUCKR', {do not localize} + 'ISO-2022-JP', {do not localize} + 'csISO2022JP', {do not localize} + 'ISO-2022-JP-2', {do not localize} + 'csISO2022JP2', {do not localize} + 'ISO-2022-CN', {do not localize} + 'ISO-2022-CN-EXT', {do not localize} + 'JIS_C6220-1969-jp', {do not localize} + 'JIS_C6220-1969', {do not localize} + 'iso-ir-13', {do not localize} + 'katakana', {do not localize} + 'x0201-7', {do not localize} + 'csISO13JISC6220jp', {do not localize} + 'JIS_C6220-1969-ro', {do not localize} + 'iso-ir-14', {do not localize} + 'jp', {do not localize} + 'ISO646-JP', {do not localize} + 'csISO14JISC6220ro', {do not localize} + 'IT', {do not localize} + 'iso-ir-15', {do not localize} + 'ISO646-IT', {do not localize} + 'csISO15Italian', {do not localize} + 'PT', {do not localize} + 'iso-ir-16', {do not localize} + 'ISO646-PT', {do not localize} + 'csISO16Portuguese', {do not localize} + 'ES', {do not localize} + 'iso-ir-17', {do not localize} + 'ISO646-ES', {do not localize} + 'csISO17Spanish', {do not localize} + 'greek7-old', {do not localize} + 'iso-ir-18', {do not localize} + 'csISO18Greek7Old', {do not localize} + 'latin-greek', {do not localize} + 'iso-ir-19', {do not localize} + 'csISO19LatinGreek', {do not localize} + 'DIN_66003', {do not localize} + 'iso-ir-21', {do not localize} + 'de', {do not localize} + 'ISO646-DE', {do not localize} + 'csISO21German', {do not localize} + 'NF_Z_62-010_(1973)', {do not localize} + 'iso-ir-25', {do not localize} + 'ISO646-FR1', {do not localize} + 'csISO25French', {do not localize} + 'Latin-greek-1', {do not localize} + 'iso-ir-27', {do not localize} + 'csISO27LatinGreek1', {do not localize} + 'ISO_5427', {do not localize} + 'iso-ir-37', {do not localize} + 'csISO5427Cyrillic', {do not localize} + 'JIS_C6226-1978', {do not localize} + 'iso-ir-42', {do not localize} + 'csISO42JISC62261978', {do not localize} + 'BS_viewdata', {do not localize} + 'iso-ir-47', {do not localize} + 'csISO47BSViewdata', {do not localize} + 'INIS', {do not localize} + 'iso-ir-49', {do not localize} + 'csISO49INIS', {do not localize} + 'INIS-8', {do not localize} + 'iso-ir-50', {do not localize} + 'csISO50INIS8', {do not localize} + 'INIS-cyrillic', {do not localize} + 'iso-ir-51', {do not localize} + 'csISO51INISCyrillic', {do not localize} + 'ISO_5427:1981', {do not localize} + 'iso-ir-54', {do not localize} + 'ISO5427Cyrillic1981', {do not localize} + 'ISO_5428:1980', {do not localize} + 'iso-ir-55', {do not localize} + 'csISO5428Greek', {do not localize} + 'GB_1988-80', {do not localize} + 'iso-ir-57', {do not localize} + 'cn', {do not localize} + 'ISO646-CN', {do not localize} + 'csISO57GB1988', {do not localize} + 'GB_2312-80', {do not localize} + 'iso-ir-58', {do not localize} + 'chinese', {do not localize} + 'csISO58GB231280', {do not localize} + 'NS_4551-1', {do not localize} + 'iso-ir-60', {do not localize} + 'ISO646-NO', {do not localize} + 'no', {do not localize} + 'csISO60DanishNorwegian', {do not localize} + 'csISO60Norwegian1', {do not localize} + 'NS_4551-2', {do not localize} + 'ISO646-NO2', {do not localize} + 'iso-ir-61', {do not localize} + 'no2', {do not localize} + 'csISO61Norwegian2', {do not localize} + 'NF_Z_62-010', {do not localize} + 'iso-ir-69', {do not localize} + 'ISO646-FR', {do not localize} + 'fr', {do not localize} + 'csISO69French', {do not localize} + 'videotex-suppl', {do not localize} + 'iso-ir-70', {do not localize} + 'csISO70VideotexSupp1', {do not localize} + 'PT2', {do not localize} + 'iso-ir-84', {do not localize} + 'ISO646-PT2', {do not localize} + 'csISO84Portuguese2', {do not localize} + 'ES2', {do not localize} + 'iso-ir-85', {do not localize} + 'ISO646-ES2', {do not localize} + 'csISO85Spanish2', {do not localize} + 'MSZ_7795.3', {do not localize} + 'iso-ir-86', {do not localize} + 'ISO646-HU', {do not localize} + 'hu', {do not localize} + 'csISO86Hungarian', {do not localize} + 'JIS_C6226-1983', {do not localize} + 'iso-ir-87', {do not localize} + 'x0208', {do not localize} + 'JIS_X0208-1983', {do not localize} + 'csISO87JISX0208', {do not localize} + 'greek7', {do not localize} + 'iso-ir-88', {do not localize} + 'csISO88Greek7', {do not localize} + 'ASMO_449', {do not localize} + 'ISO_9036', {do not localize} + 'arabic7', {do not localize} + 'iso-ir-89', {do not localize} + 'csISO89ASMO449', {do not localize} + 'iso-ir-90', {do not localize} + 'csISO90', {do not localize} + 'JIS_C6229-1984-a', {do not localize} + 'iso-ir-91', {do not localize} + 'jp-ocr-a', {do not localize} + 'csISO91JISC62291984a', {do not localize} + 'JIS_C6229-1984-b', {do not localize} + 'iso-ir-92', {do not localize} + 'ISO646-JP-OCR-B', {do not localize} + 'jp-ocr-b', {do not localize} + 'csISO92JISC62991984b', {do not localize} + 'JIS_C6229-1984-b-add', {do not localize} + 'iso-ir-93', {do not localize} + 'jp-ocr-b-add', {do not localize} + 'csISO93JIS62291984badd', {do not localize} + 'JIS_C6229-1984-hand', {do not localize} + 'iso-ir-94', {do not localize} + 'jp-ocr-hand', {do not localize} + 'csISO94JIS62291984hand', {do not localize} + 'JIS_C6229-1984-hand-add', {do not localize} + 'iso-ir-95', {do not localize} + 'jp-ocr-hand-add', {do not localize} + 'csISO95JIS62291984handadd', {do not localize} + 'JIS_C6229-1984-kana', {do not localize} + 'iso-ir-96', {do not localize} + 'csISO96JISC62291984kana', {do not localize} + 'ISO_2033-1983', {do not localize} + 'iso-ir-98', {do not localize} + 'e13b', {do not localize} + 'csISO2033', {do not localize} + 'ANSI_X3.110-1983', {do not localize} + 'iso-ir-99', {do not localize} + 'CSA_T500-1983', {do not localize} + 'NAPLPS', {do not localize} + 'csISO99NAPLPS', {do not localize} + 'ISO-8859-1', {do not localize} + 'ISO_8859-1:1987', {do not localize} + 'iso-ir-100', {do not localize} + 'ISO_8859-1', {do not localize} + 'latin1', {do not localize} + 'l1', {do not localize} + 'IBM819', {do not localize} + 'CP819', {do not localize} + 'csISOLatin1', {do not localize} + 'ISO-8859-2', {do not localize} + 'ISO_8859-2:1987', {do not localize} + 'iso-ir-101', {do not localize} + 'ISO_8859-2', {do not localize} + 'latin2', {do not localize} + 'l2', {do not localize} + 'csISOLatin2', {do not localize} + 'T.61-7bit', {do not localize} + 'iso-ir-102', {do not localize} + 'csISO102T617bit', {do not localize} + 'T.61-8bit', {do not localize} + 'T.61', {do not localize} + 'iso-ir-103', {do not localize} + 'csISO103T618bit', {do not localize} + 'ISO-8859-3', {do not localize} + 'ISO_8859-3:1988', {do not localize} + 'iso-ir-109', {do not localize} + 'ISO_8859-3', {do not localize} + 'latin3', {do not localize} + 'l3', {do not localize} + 'csISOLatin3', {do not localize} + 'ISO-8859-4', {do not localize} + 'ISO_8859-4:1988', {do not localize} + 'iso-ir-110', {do not localize} + 'ISO_8859-4', {do not localize} + 'latin4', {do not localize} + 'l4', {do not localize} + 'csISOLatin4', {do not localize} + 'ECMA-cyrillic', {do not localize} + 'iso-ir-111', {do not localize} + 'KOI8-E', {do not localize} + 'csISO111ECMACyrillic', {do not localize} + 'CSA_Z243.4-1985-1', {do not localize} + 'iso-ir-121', {do not localize} + 'ISO646-CA', {do not localize} + 'csa7-1', {do not localize} + 'ca', {do not localize} + 'csISO121Canadian1', {do not localize} + 'CSA_Z243.4-1985-2', {do not localize} + 'iso-ir-122', {do not localize} + 'ISO646-CA2', {do not localize} + 'csa7-2', {do not localize} + 'csISO122Canadian2', {do not localize} + 'CSA_Z243.4-1985-gr', {do not localize} + 'iso-ir-123', {do not localize} + 'csISO123CSAZ24341985gr', {do not localize} + 'ISO-8859-6', {do not localize} + 'ISO_8859-6:1987', {do not localize} + 'iso-ir-127', {do not localize} + 'ISO_8859-6', {do not localize} + 'ECMA-114', {do not localize} + 'ASMO-708', {do not localize} + 'arabic', {do not localize} + 'csISOLatinArabic', {do not localize} + 'ISO-8859-6-E', {do not localize} + 'ISO_8859-6-E', {do not localize} + 'csISO88596E', {do not localize} + 'ISO-8859-6-I', {do not localize} + 'ISO_8859-6-I', {do not localize} + 'csISO88596I', {do not localize} + 'ISO-8859-7', {do not localize} + 'ISO_8859-7:1987', {do not localize} + 'iso-ir-126', {do not localize} + 'ISO_8859-7', {do not localize} + 'ELOT_928', {do not localize} + 'ECMA-118', {do not localize} + 'greek', {do not localize} + 'greek8', {do not localize} + 'csISOLatinGreek', {do not localize} + 'T.101-G2', {do not localize} + 'iso-ir-128', {do not localize} + 'csISO128T101G2', {do not localize} + 'ISO-8859-8', {do not localize} + 'ISO_8859-8:1988', {do not localize} + 'iso-ir-138', {do not localize} + 'ISO_8859-8', {do not localize} + 'hebrew', {do not localize} + 'csISOLatinHebrew', {do not localize} + 'ISO-8859-8-E', {do not localize} + 'ISO_8859-8-E', {do not localize} + 'csISO88598E', {do not localize} + 'ISO-8859-8-I', {do not localize} + 'ISO_8859-8-I', {do not localize} + 'csISO88598I', {do not localize} + 'CSN_369103', {do not localize} + 'iso-ir-139', {do not localize} + 'csISO139CSN369103', {do not localize} + 'JUS_I.B1.002', {do not localize} + 'iso-ir-141', {do not localize} + 'ISO646-YU', {do not localize} + 'js', {do not localize} + 'yu', {do not localize} + 'csISO141JUSIB1002', {do not localize} + 'ISO_6937-2-add', {do not localize} + 'iso-ir-142', {do not localize} + 'csISOTextComm', {do not localize} + 'IEC_P27-1', {do not localize} + 'iso-ir-143', {do not localize} + 'csISO143IECP271', {do not localize} + 'ISO-8859-5', {do not localize} + 'ISO_8859-5:1988', {do not localize} + 'iso-ir-144', {do not localize} + 'ISO_8859-5', {do not localize} + 'cyrillic', {do not localize} + 'csISOLatinCyrillic', {do not localize} + 'JUS_I.B1.003-serb', {do not localize} + 'iso-ir-146', {do not localize} + 'serbian', {do not localize} + 'csISO146Serbian', {do not localize} + 'JUS_I.B1.003-mac', {do not localize} + 'macedonian', {do not localize} + 'iso-ir-147', {do not localize} + 'csISO147Macedonian', {do not localize} + 'ISO-8859-9', {do not localize} + 'ISO_8859-9:1989', {do not localize} + 'iso-ir-148', {do not localize} + 'ISO_8859-9', {do not localize} + 'latin5', {do not localize} + 'l5', {do not localize} + 'csISOLatin5', {do not localize} + 'greek-ccitt', {do not localize} + 'iso-ir-150', {do not localize} + 'csISO150', {do not localize} + 'csISO150GreekCCITT', {do not localize} + 'NC_NC00-10:81', {do not localize} + 'cuba', {do not localize} + 'iso-ir-151', {do not localize} + 'ISO646-CU', {do not localize} + 'csISO151Cuba', {do not localize} + 'ISO_6937-2-25', {do not localize} + 'iso-ir-152', {do not localize} + 'csISO6937Add', {do not localize} + 'GOST_19768-74', {do not localize} + 'ST_SEV_358-88', {do not localize} + 'iso-ir-153', {do not localize} + 'csISO153GOST1976874', {do not localize} + 'ISO_8859-supp', {do not localize} + 'iso-ir-154', {do not localize} + 'latin1-2-5', {do not localize} + 'csISO8859Supp', {do not localize} + 'ISO_10367-box', {do not localize} + 'iso-ir-155', {do not localize} + 'csISO10367Box', {do not localize} + 'ISO-8859-10', {do not localize} + 'iso-ir-157', {do not localize} + 'l6', {do not localize} + 'ISO_8859-10:1992', {do not localize} + 'csISOLatin6', {do not localize} + 'latin6', {do not localize} + 'latin-lap', {do not localize} + 'lap', {do not localize} + 'iso-ir-158', {do not localize} + 'csISO158Lap', {do not localize} + 'JIS_X0212-1990', {do not localize} + 'x0212', {do not localize} + 'iso-ir-159', {do not localize} + 'csISO159JISX02121990', {do not localize} + 'DS_2089', {do not localize} + 'DS2089', {do not localize} + 'ISO646-DK', {do not localize} + 'dk', {do not localize} + 'csISO646Danish', {do not localize} + 'us-dk', {do not localize} + 'csUSDK', {do not localize} + 'dk-us', {do not localize} + 'csDKUS', {do not localize} + 'JIS_X0201', {do not localize} + 'X0201', {do not localize} + 'csHalfWidthKatakana', {do not localize} + 'KSC5636', {do not localize} + 'ISO646-KR', {do not localize} + 'csKSC5636', {do not localize} + 'DEC-MCS', {do not localize} + 'dec', {do not localize} + 'csDECMCS', {do not localize} + 'hp-roman8', {do not localize} + 'roman8', {do not localize} + 'r8', {do not localize} + 'csHPRoman8', {do not localize} + 'macintosh', {do not localize} + 'mac', {do not localize} + 'csMacintosh', {do not localize} + 'IBM037', {do not localize} + 'cp037', {do not localize} + 'ebcdic-cp-us', {do not localize} + 'ebcdic-cp-ca', {do not localize} + 'ebcdic-cp-wt', {do not localize} + 'ebcdic-cp-nl', {do not localize} + 'csIBM037', {do not localize} + 'IBM038', {do not localize} + 'EBCDIC-INT', {do not localize} + 'cp038', {do not localize} + 'csIBM038', {do not localize} + 'IBM273', {do not localize} + 'CP273', {do not localize} + 'csIBM273', {do not localize} + 'IBM274', {do not localize} + 'EBCDIC-BE', {do not localize} + 'CP274', {do not localize} + 'csIBM274', {do not localize} + 'IBM275', {do not localize} + 'EBCDIC-BR', {do not localize} + 'cp275', {do not localize} + 'csIBM275', {do not localize} + 'IBM277', {do not localize} + 'EBCDIC-CP-DK', {do not localize} + 'EBCDIC-CP-NO', {do not localize} + 'csIBM277', {do not localize} + 'IBM278', {do not localize} + 'CP278', {do not localize} + 'ebcdic-cp-fi', {do not localize} + 'ebcdic-cp-se', {do not localize} + 'csIBM278', {do not localize} + 'IBM280', {do not localize} + 'CP280', {do not localize} + 'ebcdic-cp-it', {do not localize} + 'csIBM280', {do not localize} + 'IBM281', {do not localize} + 'EBCDIC-JP-E', {do not localize} + 'cp281', {do not localize} + 'csIBM281', {do not localize} + 'IBM284', {do not localize} + 'CP284', {do not localize} + 'ebcdic-cp-es', {do not localize} + 'csIBM284', {do not localize} + 'IBM285', {do not localize} + 'CP285', {do not localize} + 'ebcdic-cp-gb', {do not localize} + 'csIBM285', {do not localize} + 'IBM290', {do not localize} + 'cp290', {do not localize} + 'EBCDIC-JP-kana', {do not localize} + 'csIBM290', {do not localize} + 'IBM297', {do not localize} + 'cp297', {do not localize} + 'ebcdic-cp-fr', {do not localize} + 'csIBM297', {do not localize} + 'IBM420', {do not localize} + 'cp420', {do not localize} + 'ebcdic-cp-ar1', {do not localize} + 'csIBM420', {do not localize} + 'IBM423', {do not localize} + 'cp423', {do not localize} + 'ebcdic-cp-gr', {do not localize} + 'csIBM423', {do not localize} + 'IBM424', {do not localize} + 'cp424', {do not localize} + 'ebcdic-cp-he', {do not localize} + 'csIBM424', {do not localize} + 'IBM437', {do not localize} + 'cp437', {do not localize} + '437', {do not localize} + 'csPC8CodePage437', {do not localize} + 'IBM500', {do not localize} + 'CP500', {do not localize} + 'ebcdic-cp-be', {do not localize} + 'ebcdic-cp-ch', {do not localize} + 'csIBM500', {do not localize} + 'IBM775', {do not localize} + 'cp775', {do not localize} + 'csPC775Baltic', {do not localize} + 'IBM850', {do not localize} + 'cp850', {do not localize} + '850', {do not localize} + 'csPC850Multilingual', {do not localize} + 'IBM851', {do not localize} + 'cp851', {do not localize} + '851', {do not localize} + 'csIBM851', {do not localize} + 'IBM852', {do not localize} + 'cp852', {do not localize} + '852', {do not localize} + 'csPCp852', {do not localize} + 'IBM855', {do not localize} + 'cp855', {do not localize} + '855', {do not localize} + 'csIBM855', {do not localize} + 'IBM857', {do not localize} + 'cp857', {do not localize} + '857', {do not localize} + 'csIBM857', {do not localize} + 'IBM860', {do not localize} + 'cp860', {do not localize} + '860', {do not localize} + 'csIBM860', {do not localize} + 'IBM861', {do not localize} + 'cp861', {do not localize} + '861', {do not localize} + 'cp-is', {do not localize} + 'csIBM861', {do not localize} + 'IBM862', {do not localize} + 'cp862', {do not localize} + '862', {do not localize} + 'csPC862LatinHebrew', {do not localize} + 'IBM863', {do not localize} + 'cp863', {do not localize} + '863', {do not localize} + 'csIBM863', {do not localize} + 'IBM864', {do not localize} + 'cp864', {do not localize} + 'csIBM864', {do not localize} + 'IBM865', {do not localize} + 'cp865', {do not localize} + '865', {do not localize} + 'csIBM865', {do not localize} + 'IBM866', {do not localize} + 'cp866', {do not localize} + '866', {do not localize} + 'csIBM866', {do not localize} + 'IBM868', {do not localize} + 'CP868', {do not localize} + 'cp-ar', {do not localize} + 'csIBM868', {do not localize} + 'IBM869', {do not localize} + 'cp869', {do not localize} + '869', {do not localize} + 'cp-gr', {do not localize} + 'csIBM869', {do not localize} + 'IBM870', {do not localize} + 'CP870', {do not localize} + 'ebcdic-cp-roece', {do not localize} + 'ebcdic-cp-yu', {do not localize} + 'csIBM870', {do not localize} + 'IBM871', {do not localize} + 'CP871', {do not localize} + 'ebcdic-cp-is', {do not localize} + 'csIBM871', {do not localize} + 'IBM880', {do not localize} + 'cp880', {do not localize} + 'EBCDIC-Cyrillic', {do not localize} + 'csIBM880', {do not localize} + 'IBM891', {do not localize} + 'cp891', {do not localize} + 'csIBM891', {do not localize} + 'IBM903', {do not localize} + 'cp903', {do not localize} + 'csIBM903', {do not localize} + 'IBM904', {do not localize} + 'cp904', {do not localize} + '904', {do not localize} + 'csIBBM904', {do not localize} + 'IBM905', {do not localize} + 'CP905', {do not localize} + 'ebcdic-cp-tr', {do not localize} + 'csIBM905', {do not localize} + 'IBM918', {do not localize} + 'CP918', {do not localize} + 'ebcdic-cp-ar2', {do not localize} + 'csIBM918', {do not localize} + 'IBM1026', {do not localize} + 'CP1026', {do not localize} + 'csIBM1026', {do not localize} + 'EBCDIC-AT-DE', {do not localize} + 'csIBMEBCDICATDE', {do not localize} + 'EBCDIC-AT-DE-A', {do not localize} + 'csEBCDICATDEA', {do not localize} + 'EBCDIC-CA-FR', {do not localize} + 'csEBCDICCAFR', {do not localize} + 'EBCDIC-DK-NO', {do not localize} + 'csEBCDICDKNO', {do not localize} + 'EBCDIC-DK-NO-A', {do not localize} + 'csEBCDICDKNOA', {do not localize} + 'EBCDIC-FI-SE', {do not localize} + 'csEBCDICFISE', {do not localize} + 'EBCDIC-FI-SE-A', {do not localize} + 'csEBCDICFISEA', {do not localize} + 'EBCDIC-FR', {do not localize} + 'csEBCDICFR', {do not localize} + 'EBCDIC-IT', {do not localize} + 'csEBCDICIT', {do not localize} + 'EBCDIC-PT', {do not localize} + 'csEBCDICPT', {do not localize} + 'EBCDIC-ES', {do not localize} + 'csEBCDICES', {do not localize} + 'EBCDIC-ES-A', {do not localize} + 'csEBCDICESA', {do not localize} + 'EBCDIC-ES-S', {do not localize} + 'csEBCDICESS', {do not localize} + 'EBCDIC-UK', {do not localize} + 'csEBCDICUK', {do not localize} + 'EBCDIC-US', {do not localize} + 'csEBCDICUS', {do not localize} + 'UNKNOWN-8BIT', {do not localize} + 'csUnknown8BiT', {do not localize} + 'MNEMONIC', {do not localize} + 'csMnemonic', {do not localize} + 'MNEM', {do not localize} + 'csMnem', {do not localize} + 'VISCII', {do not localize} + 'csVISCII', {do not localize} + 'VIQR', {do not localize} + 'csVIQR', {do not localize} + 'KOI8-R', {do not localize} + 'csKOI8R', {do not localize} + 'KOI8-U', {do not localize} + 'IBM00858', {do not localize} + 'CCSID00858', {do not localize} + 'CP00858', {do not localize} + 'PC-Multilingual-850+euro', {do not localize} + 'IBM00924', {do not localize} + 'CCSID00924', {do not localize} + 'CP00924', {do not localize} + 'ebcdic-Latin9--euro', {do not localize} + 'IBM01140', {do not localize} + 'CCSID01140', {do not localize} + 'CP01140', {do not localize} + 'ebcdic-us-37+euro', {do not localize} + 'IBM01141', {do not localize} + 'CCSID01141', {do not localize} + 'CP01141', {do not localize} + 'ebcdic-de-273+euro', {do not localize} + 'IBM01142', {do not localize} + 'CCSID01142', {do not localize} + 'CP01142', {do not localize} + 'ebcdic-dk-277+euro', {do not localize} + 'ebcdic-no-277+euro', {do not localize} + 'IBM01143', {do not localize} + 'CCSID01143', {do not localize} + 'CP01143', {do not localize} + 'ebcdic-fi-278+euro', {do not localize} + 'ebcdic-se-278+euro', {do not localize} + 'IBM01144', {do not localize} + 'CCSID01144', {do not localize} + 'CP01144', {do not localize} + 'ebcdic-it-280+euro', {do not localize} + 'IBM01145', {do not localize} + 'CCSID01145', {do not localize} + 'CP01145', {do not localize} + 'ebcdic-es-284+euro', {do not localize} + 'IBM01146', {do not localize} + 'CCSID01146', {do not localize} + 'CP01146', {do not localize} + 'ebcdic-gb-285+euro', {do not localize} + 'IBM01147', {do not localize} + 'CCSID01147', {do not localize} + 'CP01147', {do not localize} + 'ebcdic-fr-297+euro', {do not localize} + 'IBM01148', {do not localize} + 'CCSID01148', {do not localize} + 'CP01148', {do not localize} + 'ebcdic-international-500+euro', {do not localize} + 'IBM01149', {do not localize} + 'CCSID01149', {do not localize} + 'CP01149', {do not localize} + 'ebcdic-is-871+euro', {do not localize} + 'Big5-HKSCS', {do not localize} + 'UTF-16BE', {do not localize} + 'UTF-16LE', {do not localize} + 'UTF-16', {do not localize} + 'CESU-8', {do not localize} + 'csCESU-8', {do not localize} + 'UTF-32', {do not localize} + 'UTF-32BE', {do not localize} + 'UTF-32LE', {do not localize} + 'UNICODE-1-1-UTF-7', {do not localize} + 'csUnicode11UTF7', {do not localize} + 'UTF-8', {do not localize} + 'ISO-8859-13', {do not localize} + 'ISO-8859-14', {do not localize} + 'iso-ir-199', {do not localize} + 'ISO_8859-14:1998', {do not localize} + 'ISO_8859-14', {do not localize} + 'latin8', {do not localize} + 'iso-celtic', {do not localize} + 'l8', {do not localize} + 'ISO-8859-15', {do not localize} + 'ISO_8859-15', {do not localize} + 'Latin-9', {do not localize} + 'ISO-8859-16', {do not localize} + 'iso-ir-226', {do not localize} + 'ISO_8859-16:2001', {do not localize} + 'ISO_8859-16', {do not localize} + 'latin10', {do not localize} + 'l10', {do not localize} + 'GBK', {do not localize} + 'CP936', {do not localize} + 'MS936', {do not localize} + 'windows-936', {do not localize} + 'GB18030', {do not localize} + 'JIS_Encoding', {do not localize} + 'csJISEncoding', {do not localize} + 'Shift_JIS', {do not localize} + 'MS_Kanji', {do not localize} + 'csShiftJIS', {do not localize} + 'EUC-JP', {do not localize} + 'Extended_UNIX_Code_Packed_Format_for_Japanese', {do not localize} + 'csEUCPkdFmtJapanese', {do not localize} + 'Extended_UNIX_Code_Fixed_Width_for_Japanese', {do not localize} + 'csEUCFixWidJapanese', {do not localize} + 'DOS-862', {do not localize} + 'windows-874', {do not localize} + 'cp875', {do not localize} + 'IBM01047', {do not localize} + 'unicodeFFFE', {do not localize} + 'Johab', {do not localize} + 'x-mac-japanese', {do not localize} + 'x-mac-chinesetrad', {do not localize} + 'x-mac-korean', {do not localize} + 'x-mac-arabic', {do not localize} + 'x-mac-hebrew', {do not localize} + 'x-mac-greek', {do not localize} + 'x-mac-cyrillic', {do not localize} + 'x-mac-chinesesimp', {do not localize} + 'x-mac-romanian', {do not localize} + 'x-mac-ukrainian', {do not localize} + 'x-mac-thai', {do not localize} + 'x-mac-ce', {do not localize} + 'x-mac-icelandic', {do not localize} + 'x-mac-turkish', {do not localize} + 'x-mac-croatian', {do not localize} + 'x-Chinese-CNS', {do not localize} + 'x-cp20001', {do not localize} + 'x-Chinese-Eten', {do not localize} + 'x-cp20003', {do not localize} + 'x-cp20004', {do not localize} + 'x-cp20005', {do not localize} + 'x-IA5', {do not localize} + 'x-IA5-German', {do not localize} + 'x-IA5-Swedish', {do not localize} + 'x-IA5-Norwegian', {do not localize} + 'x-cp20261', {do not localize} + 'x-cp20269', {do not localize} + 'x-EBCDIC-KoreanExtended', {do not localize} + 'x-cp20936', {do not localize} + 'x-cp20949', {do not localize} + 'cp1025', {do not localize} + 'x-Europa', {do not localize} + 'x-cp50227', {do not localize} + 'EUC-CN', {do not localize} + 'x-iscii-de', {do not localize} + 'x-iscii-be', {do not localize} + 'x-iscii-ta', {do not localize} + 'x-iscii-te', {do not localize} + 'x-iscii-as', {do not localize} + 'x-iscii-or', {do not localize} + 'x-iscii-ka', {do not localize} + 'x-iscii-ma', {do not localize} + 'x-iscii-gu', {do not localize} + 'x-iscii-pa', {do not localize} + 'x-EBCDIC-Arabic', {do not localize} + 'x-EBCDIC-CyrillicRussian', {do not localize} + 'x-EBCDIC-CyrillicSerbianBulgarian', {do not localize} + 'x-EBCDIC-DenmarkNorway', {do not localize} + 'x-ebcdic-denmarknorway-euro', {do not localize} + 'x-EBCDIC-FinlandSweden', {do not localize} + 'x-ebcdic-finlandsweden-euro', {do not localize} + 'X-EBCDIC-France', {do not localize} + 'x-ebcdic-france-euro', {do not localize} + 'x-EBCDIC-Germany', {do not localize} + 'x-ebcdic-germany-euro', {do not localize} + 'x-EBCDIC-GreekModern', {do not localize} + 'x-EBCDIC-Greek', {do not localize} + 'x-EBCDIC-Hebrew', {do not localize} + 'x-EBCDIC-Icelandic', {do not localize} + 'x-ebcdic-icelandic-euro', {do not localize} + 'x-ebcdic-international-euro', {do not localize} + 'x-EBCDIC-Italy', {do not localize} + 'x-ebcdic-italy-euro', {do not localize} + 'x-EBCDIC-JapaneseAndKana', {do not localize} + 'x-EBCDIC-JapaneseAndJapaneseLatin', {do not localize} + 'x-EBCDIC-JapaneseAndUSCanada', {do not localize} + 'x-EBCDIC-JapaneseKatakana', {do not localize} + 'x-EBCDIC-KoreanAndKoreanExtended', {do not localize} + 'x-EBCDIC-SimplifiedChinese', {do not localize} + 'X-EBCDIC-Spain', {do not localize} + 'x-ebcdic-spain-euro', {do not localize} + 'x-EBCDIC-Thai', {do not localize} + 'x-EBCDIC-TraditionalChinese', {do not localize} + 'x-EBCDIC-Turkish', {do not localize} + 'x-EBCDIC-UK', {do not localize} + 'x-ebcdic-uk-euro', {do not localize} + 'x-ebcdic-cp-us-euro', {do not localize} + 'OSD_EBCDIC_DF04_15', {do not localize} + 'OSD_EBCDIC_DF03_IRV', {do not localize} + 'OSD_EBCDIC_DF04_1', {do not localize} + 'ISO-11548-1', {do not localize} + 'ISO_11548-1', {do not localize} + 'ISO_TR_11548-1', {do not localize} + 'csISO115481', {do not localize} + 'KZ-1048', {do not localize} + 'STRK1048-2002', {do not localize} + 'RK1048', {do not localize} + 'csKZ1048', {do not localize} + 'ISO-10646-UCS-2', {do not localize} + 'csUnicode', {do not localize} + 'ISO-10646-UCS-4', {do not localize} + 'csUCS4', {do not localize} + 'UNICODE-1-1', {do not localize} + 'csUnicode11', {do not localize} + 'SCSU', {do not localize} + 'UTF-7', {do not localize} + 'ISO-10646-UCS-Basic', {do not localize} + 'csUnicodeASCII', {do not localize} + 'ISO-10646-Unicode-Latin1', {do not localize} + 'csUnicodeLatin1', {do not localize} + 'ISO-10646', {do not localize} + 'ISO-10646-J-1', {do not localize} + 'ISO-Unicode-IBM-1261', {do not localize} + 'csUnicodeIBM1261', {do not localize} + 'ISO-Unicode-IBM-1268', {do not localize} + 'csUnicodeIBM1268', {do not localize} + 'ISO-Unicode-IBM-1276', {do not localize} + 'csUnicodeIBM1276', {do not localize} + 'ISO-Unicode-IBM-1264', {do not localize} + 'csUnicodeIBM1264', {do not localize} + 'ISO-Unicode-IBM-1265', {do not localize} + 'csUnicodeIBM1265', {do not localize} + 'BOCU-1', {do not localize} + 'csBOCU-1', {do not localize} + 'ISO-8859-1-Windows-3.0-Latin-1', {do not localize} + 'csWindows30Latin1', {do not localize} + 'ISO-8859-1-Windows-3.1-Latin-1', {do not localize} + 'csWindows31Latin1', {do not localize} + 'ISO-8859-2-Windows-Latin-2', {do not localize} + 'csWindows31Latin2', {do not localize} + 'ISO-8859-9-Windows-Latin-5', {do not localize} + 'csWindows31Latin5', {do not localize} + 'Adobe-Standard-Encoding', {do not localize} + 'csAdobeStandardEncoding', {do not localize} + 'Ventura-US', {do not localize} + 'csVenturaUS', {do not localize} + 'Ventura-International', {do not localize} + 'csVenturaInternational', {do not localize} + 'PC8-Danish-Norwegian', {do not localize} + 'csPC8DanishNorwegian', {do not localize} + 'PC8-Turkish', {do not localize} + 'csPC8Turkish', {do not localize} + 'IBM-Symbols', {do not localize} + 'csIBMSymbols', {do not localize} + 'IBM-Thai', {do not localize} + 'csIBMThai', {do not localize} + 'HP-Legal', {do not localize} + 'csHPLegal', {do not localize} + 'HP-Pi-font', {do not localize} + 'csHPPiFont', {do not localize} + 'HP-Math8', {do not localize} + 'csHPMath8', {do not localize} + 'Adobe-Symbol-Encoding', {do not localize} + 'csHPPSMath', {do not localize} + 'HP-DeskTop', {do not localize} + 'csHPDesktop', {do not localize} + 'Ventura-Math', {do not localize} + 'csVenturaMath', {do not localize} + 'Microsoft-Publishing', {do not localize} + 'csMicrosoftPublishing', {do not localize} + 'Windows-31J', {do not localize} + 'csWindows31J', {do not localize} + 'GB2312', {do not localize} + 'csGB2312', {do not localize} + 'Big5', {do not localize} + 'csBig5', {do not localize} + 'HZ-GB-2312', {do not localize} + 'IBM1047', {do not localize} + 'IBM-1047', {do not localize} + 'PTCP154', {do not localize} + 'csPTCP154', {do not localize} + 'PT154', {do not localize} + 'CP154', {do not localize} + 'Cyrillic-Asian', {do not localize} + 'Amiga-1251', {do not localize} + 'Ami1251', {do not localize} + 'Amiga1251', {do not localize} + 'Ami-1251', {do not localize} + 'KOI7-switched', {do not localize} + 'BRF', {do not localize} + 'csBRF', {do not localize} + 'TSCII', {do not localize} + 'csTSCII', {do not localize} + 'windows-1250', {do not localize} + 'windows-1251', {do not localize} + 'windows-1252', {do not localize} + 'windows-1253', {do not localize} + 'windows-1254', {do not localize} + 'windows-1255', {do not localize} + 'windows-1256', {do not localize} + 'windows-1257', {do not localize} + 'windows-1258', {do not localize} + 'TIS-620', {do not localize} + 'DOS-720', {do not localize} + 'ibm737' {do not localize} + ); + +function FindPreferredCharset(const ACharSet: TIdCharSet): TIdCharSet; +function FindCharset(const ACharSet: string): TIdCharset; +function CharsetToCodePage(const ACharSet: TIdCharSet): Word; overload; +function CharsetToCodePage(const ACharSet: String): Word; overload; + +implementation + +uses + IdGlobal, + SysUtils; + +function FindPreferredCharset(const ACharSet: TIdCharSet): TIdCharSet; +begin + case ACharSet of + { US-ASCII } + idcs_ANSI_X3_4_1968, + idcs_iso_ir_6, + idcs_ANSI_X3_4_1986, + idcs_ISO_646_irv_1991, + idcs_ASCII, + idcs_ISO646_US, + idcs_us, + idcs_IBM367, + idcs_cp367, + idcs_csASCII: + Result := idcs_US_ASCII; + + { Korean (ISO) } + idcs_csISO2022KR: + Result := idcs_ISO_2022_KR; + + { Korean (EUC) } + idcs_csEUCKR: + Result := idcs_EUC_KR; + + { Japanese (JIS-Allow 1 byte Kana - SO/SI) } + idcs_csISO2022JP: + Result := idcs_ISO_2022_JP; + + idcs_csISO2022JP2: + Result := idcs_ISO_2022_JP_2; + + { Western European (ISO) } + idcs_ISO_8859_1_1987, + idcs_iso_ir_100, + idcs_ISO_8859_1_, + idcs_latin1, + idcs_l1, + idcs_IBM819, + idcs_CP819, + idcs_csISOLatin1: + Result := idcs_ISO_8859_1; + + { Central European (ISO) } + idcs_ISO_8859_2_1987, + idcs_iso_ir_101, + idcs_ISO_8859_2_, + idcs_latin2, + idcs_l2, + idcs_csISOLatin2: + Result := idcs_ISO_8859_2; + + { Latin 3 (ISO) } + idcs_ISO_8859_3_1988, + idcs_iso_ir_109, + idcs_ISO_8859_3_, + idcs_latin3, + idcs_l3, + idcs_csISOLatin3: + Result := idcs_ISO_8859_3; + + { Baltic (ISO) } + idcs_ISO_8859_4_1988, + idcs_iso_ir_110, + idcs_ISO_8859_4_, + idcs_latin4, + idcs_l4, + idcs_csISOLatin4: + Result := idcs_ISO_8859_4; + + { Arabic (ISO) } + idcs_ISO_8859_6_1987, + idcs_iso_ir_127, + idcs_ISO_8859_6_, + idcs_ECMA_114, + idcs_ASMO_708, + idcs_arabic, + idcs_csISOLatinArabic: + Result := idcs_ISO_8859_6; + + idcs_ISO_8859_6_E_, + idcs_csISO88596E: + Result := idcs_ISO_8859_6_E; + + idcs_ISO_8859_6_I_, + idcs_csISO88596I: + Result := idcs_ISO_8859_6_I; + + { Greek (ISO) } + idcs_ISO_8859_7_1987, + idcs_iso_ir_126, + idcs_ISO_8859_7_, + idcs_ELOT_928, + idcs_ECMA_118, + idcs_greek, + idcs_greek8, + idcs_csISOLatinGreek: + Result := idcs_ISO_8859_7; + + { Hebrew (ISO-Visual) } + idcs_ISO_8859_8_1988, + idcs_iso_ir_138, + idcs_ISO_8859_8_, + idcs_hebrew, + idcs_csISOLatinHebrew: + Result := idcs_ISO_8859_8; + + idcs_ISO_8859_8_E_, + idcs_csISO88598E: + Result := idcs_ISO_8859_8_E; + + { Hebrew (ISO-Logical) } + idcs_ISO_8859_8_I_, + idcs_csISO88598I: + Result := idcs_ISO_8859_8_I; + + { Cyrillic (ISO) } + idcs_ISO_8859_5_1988, + idcs_iso_ir_144, + idcs_ISO_8859_5_, + idcs_cyrillic, + idcs_csISOLatinCyrillic: + Result := idcs_ISO_8859_5; + + { Turkish (ISO) } + idcs_ISO_8859_9_1989, + idcs_iso_ir_148, + idcs_ISO_8859_9_, + idcs_latin5, + idcs_l5, + idcs_csISOLatin5: + Result := idcs_ISO_8859_9; + + idcs_iso_ir_157, + idcs_l6, + idcs_ISO_8859_10_1992, + idcs_csISOLatin6, + idcs_latin6: + Result := idcs_ISO_8859_10; + + { Cyrillic (KOI8-R) } + idcs_csKOI8R: + Result := idcs_KOI8_R; + + { Japanese (Shift-JIS) } + idcs_MS_Kanji, + idcs_csShiftJIS: + Result := idcs_Shift_JIS; + + { Japanese (EUC) } + idcs_Extended_UNIX_Code_Packed_Format_for_Japanese, + idcs_csEUCPkdFmtJapanese: + Result := idcs_EUC_JP; + + { Chinese Simplified (GB2312) } + idcs_csGB2312: + Result := idcs_GB2312; + + { Chinese Traditional (Big5) } + idcs_csBig5: + Result := idcs_Big5; + + else + Result := ACharSet; + end; +end; + +{ + REFERENCES + + [RFC1345] Simonsen, K., "Character Mnemonics & Character Sets", + RFC 1345, Rationel Almen Planlaegning, Rationel Almen + Planlaegning, June 1992. + + [RFC1428] Vaudreuil, G., "Transition of Internet Mail from + Just-Send-8 to 8bit-SMTP/MIME", RFC1428, CNRI, February + 1993. + + [RFC1456] Vietnamese Standardization Working Group, "Conventions for + Encoding the Vietnamese Language VISCII: VIetnamese + Standard Code for Information Interchange VIQR: VIetnamese + Quoted-Readable Specification Revision 1.1", RFC 1456, May + 1993. + + [RFC1468] Murai, J., Crispin, M., and E. van der Poel, "Japanese + Character Encoding for Internet Messages", RFC 1468, + Keio University, Panda Programming, June 1993. + + [RFC1489] Chernov, A., "Registration of a Cyrillic Character Set", + RFC1489, RELCOM Development Team, July 1993. + + [RFC1554] Ohta, M., and K. Handa, "ISO-2022-JP-2: Multilingual + Extension of ISO-2022-JP", RFC1554, Tokyo Institute of + Technology, ETL, December 1993. + + [RFC1556] Nussbacher, H., "Handling of Bi-directional Texts in MIME", + RFC1556, Israeli Inter-University, December 1993. + + [RFC1557] Choi, U., Chon, K., and H. Park, "Korean Character Encoding + for Internet Messages", KAIST, Solvit Chosun Media, + December 1993. + + [RFC1641] Goldsmith, D., and M. Davis, "Using Unicode with MIME", + RFC1641, Taligent, Inc., July 1994. + + [RFC1642] Goldsmith, D., and M. Davis, "UTF-7", RFC1642, Taligent, + Inc., July 1994. + + [RFC1815] Ohta, M., "Character Sets ISO-10646 and ISO-10646-J-1", + RFC 1815, Tokyo Institute of Technology, July 1995. + + + [Adobe] Adobe Systems Incorporated, PostScript Language Reference + Manual, second edition, Addison-Wesley Publishing Company, + Inc., 1990. + + [HP-PCL5] Hewlett-Packard Company, "HP PCL 5 Comparison Guide", + (P/N 5021-0329) pp B-13, 1996. + + [IBM-CIDT] IBM Corporation, "ABOUT TYPE: IBM's Technical Reference + for Core Interchange Digitized Type", Publication number + S544-3708-01 + + [RFC1842] Wei, Y., J. Li, and Y. Jiang, "ASCII Printable + Characters-Based Chinese Character Encoding for Internet + Messages", RFC 1842, Harvard University, Rice University, + University of Maryland, August 1995. + + [RFC1843] Lee, F., "HZ - A Data Format for Exchanging Files of + Arbitrarily Mixed Chinese and ASCII Characters", RFC 1843, + Stanford University, August 1995. + + [RFC2152] Goldsmith, D., M. Davis, "UTF-7: A Mail-Safe Transformation + Format of Unicode", RFC 2152, Apple Computer, Inc., + Taligent Inc., May 1997. + + [RFC2279] Yergeau, F., "UTF-8, A Transformation Format of ISO 10646", + RFC 2279, Alis Technologies, January, 1998. + + [RFC2781] Hoffman, P., Yergeau, F., "UTF-16, an encoding of ISO 10646", + RFC 2781, February 2000. + + + PEOPLE + + [KXS2] Keld Simonsen + + [Choi] Woohyong Choi + + [Davis] Mark Davis, , April 2002. + + [Lazhintseva] Katya Lazhintseva, , May 1996. + + [Mahdi] Tamer Mahdi, , August 2000. + + [Murai] Jun Murai + + [Nussbacher] Hank Nussbacher, + + [Ohta] Masataka Ohta, , July 1995. + + [Phipps] Toby Phipps, , March 2002. + + [Pond] Rick Pond, March 1997. + + [Scherer] Markus Scherer, , August 2000. + + [Simonsen] Keld Simonsen, , August 2000. +} + +{ this is for searching a charset from a string, it must be case-insensitive } +function FindCharset(const ACharSet: string): TIdCharset; +var + Lcset: TIdCharset; +begin + Result := idcs_INVALID; + for Lcset := Low(TIdCharSet) to High(TIdCharSet) do begin + if TextIsSame(IdCharsetNames[Lcset], ACharSet) then begin + Result := Lcset; + Break; + end; + end; +end; + +// RLebeau: this table was generated by scanning my PC's Windows Registry key: +// "HKEY_CLASSES_ROOT\Mime\Database\Charset" +// and then filling in missing values using various online resources. +// This may be incomplete or not entirely accurate... + +const + IdCharsetCodePages : array[Low(TIdCharSet)..High(TIdCharSet)] of Word = ( + 0, // Unknown + + 20127, // US-ASCII + 20127, // ANSI_X3.4-1968 + 20127, // iso-ir-6 + 20127, // ANSI_X3.4-1986 + 20127, // ISO_646.irv:1991 + 20127, // ASCII + 20127, // ISO646-US + 20127, // us + 20127, // IBM367 + 20127, // cp367 + 20127, // csASCII + + 0, // ISO-10646-UTF-1 + 0, // csISO10646UTF1 + + 0, // ISO_646.basic:1983 + 0, // ref + 0, // csISO646basic1983 + + 0, // INVARIANT + 0, // csINVARIANT + + 0, // ISO_646.irv:1983 + 0, // iso-ir-2 + 0, // irv + 0, // csISO2IntlRefVersion + + 0, // BS_4730 + 0, // iso-ir-4 + 0, // ISO646-GB + 0, // gb + 0, // uk + 0, // csISO4UnitedKingdom + + 0, // NATS-SEFI + 0, // iso-ir-8-1 + 0, // csNATSSEFI + + 0, // NATS-SEFI-ADD + 0, // iso-ir-8-2 + 0, // csNATSSEFIADD + + 0, // NATS-DANO + 0, // iso-ir-9-1 + 0, // csNATSDANO + + 0, // NATS-DANO-ADD + 0, // iso-ir-9-2 + 0, // csNATSDANOADD + + 0, // SEN_850200_B + 0, // iso-ir-10 + 0, // FI + 0, // ISO646-FI + 0, // ISO646-SE + 0, // se + 0, // csISO10Swedish + + 0, // SEN_850200_C + 0, // iso-ir-11 + 0, // ISO646-SE2 + 0, // se2 + 0, // csISO11SwedishForNames + + 949, // KS_C_5601-1987 + 949, // iso-ir-149 + 949, // KS_C_5601-1989 + 949, // KSC_5601 + 949, // korean + 949, // csKSC56011987 + + 50225, // ISO-2022-KR + 50225, // csISO2022KR + + 51949, // EUC-KR + 51949, // csEUCKR + + 50220, // ISO-2022-JP [need to verify] + 50221, // csISO2022JP + + 0, // ISO-2022-JP-2 + 0, // csISO2022JP2 + + 0, // ISO-2022-CN + + 0, // ISO-2022-CN-EXT + + 0, // JIS_C6220-1969-jp + 0, // JIS_C6220-1969 + 0, // iso-ir-13 + 0, // katakana + 0, // x0201-7 + 0, // csISO13JISC6220jp + + 0, // JIS_C6220-1969-ro + 0, // iso-ir-14 + 0, // jp + 0, // ISO646-JP + 0, // csISO14JISC6220ro + + 0, // IT + 0, // iso-ir-15 + 0, // ISO646-IT + 0, // csISO15Italian + + 0, // PT + 0, // iso-ir-16 + 0, // ISO646-PT + 0, // csISO16Portuguese + + 0, // ES + 0, // iso-ir-17 + 0, // ISO646-ES + 0, // csISO17Spanish + + 0, // greek7-old + 0, // iso-ir-18 + 0, // csISO18Greek7Old + + 0, // latin-greek + 0, // iso-ir-19 + 0, // csISO19LatinGreek + + 0, // DIN_66003 + 0, // iso-ir-21 + 0, // de + 0, // ISO646-DE + 0, // csISO21German + + 0, // NF_Z_62-010_(1973) + 0, // iso-ir-25 + 0, // ISO646-FR1 + 0, // csISO25French + + 0, // Latin-greek-1 + 0, // iso-ir-27 + 0, // csISO27LatinGreek1 + + 0, // ISO_5427 + 0, // iso-ir-37 + 0, // csISO5427Cyrillic + + 0, // JIS_C6226-1978 + 0, // iso-ir-42 + 0, // csISO42JISC62261978 + + 0, // BS_viewdata + 0, // iso-ir-47 + 0, // csISO47BSViewdata + + 0, // INIS + 0, // iso-ir-49 + 0, // csISO49INIS + + 0, // INIS-8 + 0, // iso-ir-50 + 0, // csISO50INIS8 + + 0, // INIS-cyrillic + 0, // iso-ir-51 + 0, // csISO51INISCyrillic + + 0, // ISO_5427:1981 + 0, // iso-ir-54 + 0, // ISO5427Cyrillic1981 + + 0, // ISO_5428:1980 + 0, // iso-ir-55 + 0, // csISO5428Greek + + 0, // GB_1988-80 + 0, // iso-ir-57 + 0, // cn + 0, // ISO646-CN + 0, // csISO57GB1988 + + 936, // GB_2312-80 + 936, // iso-ir-58 + 936, // chinese //aliases to gb2312 on Windows + 936, // csISO58GB231280 + + 0, // NS_4551-1 + 0, // iso-ir-60 + 0, // ISO646-NO + 0, // no + 0, // csISO60DanishNorwegian + 0, // csISO60Norwegian1 + + 0, // NS_4551-2 + 0, // ISO646-NO2 + 0, // iso-ir-61 + 0, // no2 + 0, // csISO61Norwegian2 + + 0, // NF_Z_62-010 + 0, // iso-ir-69 + 0, // ISO646-FR + 0, // fr + 0, // csISO69French + + 0, // videotex-suppl + 0, // iso-ir-70 + 0, // csISO70VideotexSupp1 + + 0, // PT2 + 0, // iso-ir-84 + 0, // ISO646-PT2 + 0, // csISO84Portuguese2 + + 0, // ES2 + 0, // iso-ir-85 + 0, // ISO646-ES2 + 0, // csISO85Spanish2 + + 0, // MSZ_7795.3 + 0, // iso-ir-86 + 0, // ISO646-HU + 0, // hu + 0, // csISO86Hungarian + + 0, // JIS_C6226-1983 + 0, // iso-ir-87 + 0, // x0208 + 0, // JIS_X0208-1983 + 0, // csISO87JISX0208 + + 0, // greek7 + 0, // iso-ir-88 + 0, // csISO88Greek7 + + 0, // ASMO_449 + 0, // ISO_9036 + 0, // arabic7 + 0, // iso-ir-89 + 0, // csISO89ASMO449 + + 0, // iso-ir-90 + 0, // csISO90 + + 0, // JIS_C6229-1984-a + 0, // iso-ir-91 + 0, // jp-ocr-a + 0, // csISO91JISC62291984a + + 0, // JIS_C6229-1984-b + 0, // iso-ir-92 + 0, // ISO646-JP-OCR-B + 0, // jp-ocr-b + 0, // csISO92JISC62991984b + + 0, // JIS_C6229-1984-b-add + 0, // iso-ir-93 + 0, // jp-ocr-b-add + 0, // csISO93JIS62291984badd + + 0, // JIS_C6229-1984-hand + 0, // iso-ir-94 + 0, // jp-ocr-hand + 0, // csISO94JIS62291984hand + + 0, // JIS_C6229-1984-hand-add + 0, // iso-ir-95 + 0, // jp-ocr-hand-add + 0, // csISO95JIS62291984handadd + + 0, // JIS_C6229-1984-kana + 0, // iso-ir-96 + 0, // csISO96JISC62291984kana + + 0, // ISO_2033-1983 + 0, // iso-ir-98 + 0, // e13b + 0, // csISO2033 + + 0, // ANSI_X3.110-1983 + 0, // iso-ir-99 + 0, // CSA_T500-1983 + 0, // NAPLPS + 0, // csISO99NAPLPS + + 28591, // ISO-8859-1 + 28591, // ISO_8859-1:1987 + 28591, // iso-ir-100 + 28591, // ISO_8859-1 + 28591, // latin1 + 28591, // l1 + 28591, // IBM819 + 28591, // CP819 + 28591, // csISOLatin1 + + 28592, // ISO-8859-2 + 28592, // ISO_8859-2:1987 + 28592, // iso-ir-101 + 28592, // ISO_8859-2 + 28592, // latin2 + 28592, // l2 + 28592, // csISOLatin2 + + 0, // T.61-7bit + 0, // iso-ir-102 + 0, // csISO102T617bit + + 0, // T.61-8bit + 0, // T.61 + 0, // iso-ir-103 + 0, // csISO103T618bit + + 28593, // ISO-8859-3 + 28593, // ISO_8859-3:1988 + 28593, // iso-ir-109 + 28593, // ISO_8859-3 + 28593, // latin3 + 28593, // l3 + 28593, // csISOLatin3 + + 28594, // ISO-8859-4 + 28594, // ISO_8859-4:1988 + 28594, // iso-ir-110 + 28594, // ISO_8859-4 + 28594, // latin4 + 28594, // l4 + 28594, // csISOLatin4 + + 0, // ECMA-cyrillic + 0, // iso-ir-111 + 0, // KOI8-E + 0, // csISO111ECMACyrillic + + 0, // CSA_Z243.4-1985-1 + 0, // iso-ir-121 + 0, // ISO646-CA + 0, // csa7-1 + 0, // ca + 0, // csISO121Canadian1 + + 0, // CSA_Z243.4-1985-2 + 0, // iso-ir-122 + 0, // ISO646-CA2 + 0, // csa7-2 + 0, // csISO122Canadian2 + + 0, // CSA_Z243.4-1985-gr + 0, // iso-ir-123 + 0, // csISO123CSAZ24341985gr + + 28596, // ISO-8859-6 + 708, // ISO_8859-6:1987 + 708, // iso-ir-127 + 708, // ISO_8859-6 + 708, // ECMA-114 + 708, // ASMO-708 + 708, // arabic + 708, // csISOLatinArabic + + 0, // ISO-8859-6-E + 0, // ISO_8859-6-E + 0, // csISO88596E + + 0, // ISO-8859-6-I + 0, // ISO_8859-6-I + 0, // csISO88596I + + 28597, // ISO-8859-7 + 28597, // ISO_8859-7:1987 + 28597, // iso-ir-126 + 28597, // ISO_8859-7 + 28597, // ELOT_928 + 28597, // ECMA-118 + 28597, // greek + 28597, // greek8 + 28597, // csISOLatinGreek + + 0, // T.101-G2 + 0, // iso-ir-128 + 0, // csISO128T101G2 + + 28598, // ISO-8859-8 + 28598, // ISO_8859-8:1988 + 28598, // iso-ir-138 + 28598, // ISO_8859-8 + 28598, // hebrew + 28598, // csISOLatinHebrew + + 0, // ISO-8859-8-E + 0, // ISO_8859-8-E + 0, // csISO88598E + + 38598, // ISO-8859-8-I + 38598, // ISO_8859-8-I + 38598, // csISO88598I + + 0, // CSN_369103 + 0, // iso-ir-139 + 0, // csISO139CSN369103 + + 0, // JUS_I.B1.002 + 0, // iso-ir-141 + 0, // ISO646-YU + 0, // js + 0, // yu + 0, // csISO141JUSIB1002 + + 0, // ISO_6937-2-add + 0, // iso-ir-142 + 0, // csISOTextComm + + 0, // IEC_P27-1 + 0, // iso-ir-143 + 0, // csISO143IECP271 + + 28595, // ISO-8859-5 + 28595, // ISO_8859-5:1988 + 28595, // iso-ir-144 + 28595, // ISO_8859-5 + 28595, // cyrillic + 28595, // csISOLatinCyrillic + + 0, // JUS_I.B1.003-serb + 0, // iso-ir-146 + 0, // serbian + 0, // csISO146Serbian + + 0, // JUS_I.B1.003-mac + 0, // macedonian + 0, // iso-ir-147 + 0, // csISO147Macedonian + + 28599, // ISO-8859-9 + 28599, // ISO_8859-9:1989 + 28599, // iso-ir-148 + 28599, // ISO_8859-9 + 28599, // latin5 + 28599, // l5 + 28599, // csISOLatin5 + + 0, // greek-ccitt + 0, // iso-ir-150 + 0, // csISO150 + 0, // csISO150GreekCCITT + + 0, // NC_NC00-10:81 + 0, // cuba + 0, // iso-ir-151 + 0, // ISO646-CU + 0, // csISO151Cuba + + 0, // ISO_6937-2-25 + 0, // iso-ir-152 + 0, // csISO6937Add + + 0, // GOST_19768-74 + 0, // ST_SEV_358-88 + 0, // iso-ir-153 + 0, // csISO153GOST1976874 + + 0, // ISO_8859-supp + 0, // iso-ir-154 + 0, // latin1-2-5 + 0, // csISO8859Supp + + 0, // ISO_10367-box + 0, // iso-ir-155 + 0, // csISO10367Box + + 0, // ISO-8859-10 + 0, // iso-ir-157 + 0, // l6 + 0, // ISO_8859-10:1992 + 0, // csISOLatin6 + 0, // latin6 + + 0, // latin-lap + 0, // lap + 0, // iso-ir-158 + 0, // csISO158Lap + + 0, // JIS_X0212-1990 + 0, // x0212 + 0, // iso-ir-159 + 0, // csISO159JISX02121990 + + 0, // DS_2089 + 0, // DS2089 + 0, // ISO646-DK + 0, // dk + 0, // csISO646Danish + + 0, // us-dk + 0, // csUSDK + + 0, // dk-us + 0, // csDKUS + + 0, // JIS_X0201 + 0, // X0201 + 0, // csHalfWidthKatakana + + 0, // KSC5636 + 0, // ISO646-KR + 0, // csKSC5636 + + 0, // DEC-MCS + 0, // dec + 0, // csDECMCS + + 0, // hp-roman8 + 0, // roman8 + 0, // r8 + 0, // csHPRoman8 + + 10000, // macintosh + 10000, // mac + 10000, // csMacintosh + + 37, // IBM037 + 37, // cp037 + 37, // ebcdic-cp-us + 37, // ebcdic-cp-ca + 37, // ebcdic-cp-wt + 37, // ebcdic-cp-nl + 37, // csIBM037 + + 0, // IBM038 + 0, // EBCDIC-INT + 0, // cp038 + 0, // csIBM038 + + 20273, // IBM273 + 20273, // CP273 + 20273, // csIBM273 + + 0, // IBM274 + 0, // EBCDIC-BE + 0, // CP274 + 0, // csIBM274 + + 0, // IBM275 + 0, // EBCDIC-BR + 0, // cp275 + 0, // csIBM275 + + 20277, // IBM277 + 20277, // EBCDIC-CP-DK + 20277, // EBCDIC-CP-NO + 20277, // csIBM277 + + 20278, // IBM278 + 20278, // CP278 + 20278, // ebcdic-cp-fi + 20278, // ebcdic-cp-se + 20278, // csIBM278 + + 20280, // IBM280 + 20280, // CP280 + 20280, // ebcdic-cp-it + 20280, // csIBM280 + + 0, // IBM281 + 0, // EBCDIC-JP-E + 0, // cp281 + 0, // csIBM281 + + 20284, // IBM284 + 20284, // CP284 + 20284, // ebcdic-cp-es + 20284, // csIBM284 + + 20285, // IBM285 + 20285, // CP285 + 20285, // ebcdic-cp-gb + 20285, // csIBM285 + + 20290, // IBM290 + 20290, // cp290 + 20290, // EBCDIC-JP-kana + 20290, // csIBM290 + + 20297, // IBM297 + 20297, // cp297 + 20297, // ebcdic-cp-fr + 20297, // csIBM297 + + 20420, // IBM420 + 20420, // cp420 + 20420, // ebcdic-cp-ar1 + 20420, // csIBM420 + + 20423, // IBM423 + 20423, // cp423 + 20423, // ebcdic-cp-gr + 20423, // csIBM423 + + 20424, // IBM424 + 20424, // cp424 + 20424, // ebcdic-cp-he + 20424, // csIBM424 + + 437, // IBM437 + 437, // cp437 + 437, // 437 + 437, // csPC8CodePage437 + + 500, // IBM500 + 500, // CP500 + 500, // ebcdic-cp-be + 500, // ebcdic-cp-ch + 500, // csIBM500 + + 775, // IBM775 + 775, // cp775 + 775, // csPC775Baltic + + 850, // IBM850 + 850, // cp850 + 850, // 850 + 850, // csPC850Multilingual + + 0, // IBM851 + 0, // cp851 + 0, // 851 + 0, // csIBM851 + + 852, // IBM852 + 852, // cp852 + 852, // 852 + 852, // csPCp852 + + 855, // IBM855 + 855, // cp855 + 855, // 855 + 855, // csIBM855 + + 857, // IBM857 + 857, // cp857 + 857, // 857 + 857, // csIBM857 + + 860, // IBM860 + 860, // cp860 + 860, // 860 + 860, // csIBM860 + + 861, // IBM861 + 861, // cp861 + 861, // 861 + 861, // cp-is + 861, // csIBM861 + + 0, // IBM862 + 0, // cp862 + 0, // 862 + 0, // csPC862LatinHebrew + + 863, // IBM863 + 863, // cp863 + 863, // 863 + 863, // csIBM863 + + 864, // IBM864 + 864, // cp864 + 864, // csIBM864 + + 865, // IBM865 + 865, // cp865 + 865, // 865 + 865, // csIBM865 + + 866, // IBM866 + 866, // cp866 + 866, // 866 + 866, // csIBM866 + + 0, // IBM868 + 0, // CP868 + 0, // cp-ar + 0, // csIBM868 + + 869, // IBM869 + 869, // cp869 + 869, // 869 + 869, // cp-gr + 869, // csIBM869 + + 870, // IBM870 + 870, // CP870 + 870, // ebcdic-cp-roece + 870, // ebcdic-cp-yu + 870, // csIBM870 + + 20871, // IBM871 + 20871, // CP871 + 20871, // ebcdic-cp-is + 20871, // csIBM871 + + 20880, // IBM880 + 20880, // cp880 + 20880, // EBCDIC-Cyrillic + 20880, // csIBM880 + + 0, // IBM891 + 0, // cp891 + 0, // csIBM891 + + 0, // IBM903 + 0, // cp903 + 0, // csIBM903 + + 0, // IBM904 + 0, // cp904 + 0, // 904 + 0, // csIBBM904 + + 20905, // IBM905 + 20905, // CP905 + 20905, // ebcdic-cp-tr + 20905, // csIBM905 + + 0, // IBM918 + 0, // CP918 + 0, // ebcdic-cp-ar2 + 0, // csIBM918 + + 1026, // IBM1026 + 1026, // CP1026 + 1026, // csIBM1026 + + 0, // EBCDIC-AT-DE + 0, // csIBMEBCDICATDE + + 0, // EBCDIC-AT-DE-A + 0, // csEBCDICATDEA + + 0, // EBCDIC-CA-FR + 0, // csEBCDICCAFR + + 0, // EBCDIC-DK-NO + 0, // csEBCDICDKNO + + 0, // EBCDIC-DK-NO-A + 0, // csEBCDICDKNOA + + 0, // EBCDIC-FI-SE + 0, // csEBCDICFISE + + 0, // EBCDIC-FI-SE-A + 0, // csEBCDICFISEA + + 0, // EBCDIC-FR + 0, // csEBCDICFR + + 0, // EBCDIC-IT + 0, // csEBCDICIT + + 0, // EBCDIC-PT + 0, // csEBCDICPT + + 0, // EBCDIC-ES + 0, // csEBCDICES + + 0, // EBCDIC-ES-A + 0, // csEBCDICESA + + 0, // EBCDIC-ES-S + 0, // csEBCDICESS + + 0, // EBCDIC-UK + 0, // csEBCDICUK + + 0, // EBCDIC-US + 0, // csEBCDICUS + + 0, // UNKNOWN-8BIT + 0, // csUnknown8BiT + + 0, // MNEMONIC + 0, // csMnemonic + + 0, // MNEM + 0, // csMnem + + 0, // VISCII + 0, // csVISCII + + 0, // VIQR + 0, // csVIQR + + 20866, // KOI8-R + 20866, // csKOI8R + + 21866, // KOI8-U + + 858, // IBM00858 + 858, // CCSID00858 + 858, // CP00858 + 858, // PC-Multilingual-850+euro + + 20924, // IBM00924 + 20924, // CCSID00924 + 20924, // CP00924 + 20924, // ebcdic-Latin9--euro + + 1140, // IBM01140 + 1140, // CCSID01140 + 1140, // CP01140 + 1140, // ebcdic-us-37+euro + + 1141, // IBM01141 + 1141, // CCSID01141 + 1141, // CP01141 + 1141, // ebcdic-de-273+euro + + 1142, // IBM01142 + 1142, // CCSID01142 + 1142, // CP01142 + 1142, // ebcdic-dk-277+euro + 1142, // ebcdic-no-277+euro + + 1143, // IBM01143 + 1143, // CCSID01143 + 1143, // CP01143 + 1143, // ebcdic-fi-278+euro + 1143, // ebcdic-se-278+euro + + 1144, // IBM01144 + 1144, // CCSID01144 + 1144, // CP01144 + 1144, // ebcdic-it-280+euro + + 1145, // IBM01145 + 1145, // CCSID01145 + 1145, // CP01145 + 1145, // ebcdic-es-284+euro + + 1146, // IBM01146 + 1146, // CCSID01146 + 1146, // CP01146 + 1146, // ebcdic-gb-285+euro + + 1147, // IBM01147 + 1147, // CCSID01147 + 1147, // CP01147 + 1147, // ebcdic-fr-297+euro + + 1148, // IBM01148 + 1148, // CCSID01148 + 1148, // CP01148 + 1148, // ebcdic-international-500+euro + + 1149, // IBM01149 + 1149, // CCSID01149 + 1149, // CP01149 + 1149, // ebcdic-is-871+euro + + 0, // Big5-HKSCS + + 1201, // UTF-16BE + + 1200, // UTF-16LE + + 1200, // UTF-16 + + 0, // CESU-8 + 0, // csCESU-8 + + 12000, // UTF-32 + + 12001, // UTF-32BE + + 12000, // UTF-32LE + + 0, // UNICODE-1-1-UTF-7 + 0, // csUnicode11UTF7 + + 65001, // UTF-8 + + 28603, // ISO-8859-13 + + 0, // ISO-8859-14 + 0, // iso-ir-199 + 0, // ISO_8859-14:1998 + 0, // ISO_8859-14 + 0, // latin8 + 0, // iso-celtic + 0, // l8 + + 28605, // ISO-8859-15 + 28605, // ISO_8859-15 + 28605, // Latin-9 + + 0, // ISO-8859-16 + 0, // iso-ir-226 + 0, // ISO_8859-16:2001 + 0, // ISO_8859-16 + 0, // latin10 + 0, // l10 + + 936, // GBK + 936, // CP936 + 936, // MS936 + 936, // windows-936 + + 54936, // GB18030 + + 0, // JIS_Encoding + 0, // csJISEncoding + + 932, // Shift_JIS + 932, // MS_Kanji + 932, // csShiftJIS + + 20932, // EUC-JP [need to verify] + 20932, // Extended_UNIX_Code_Packed_Format_for_Japanese + 20932, // csEUCPkdFmtJapanese + + 0, // Extended_UNIX_Code_Fixed_Width_for_Japanese + 0, // csEUCFixWidJapanese + + 862, // DOS-862 + + 874, // windows-874 + + 875, // cp875 + + 1047, // IBM01047 + + 1201, // unicodeFFFE + + 1361, // Johab + + 10001, // x-mac-japanese + + 10002, // x-mac-chinesetrad + + 10003, // x-mac-korean + + 10004, // x-mac-arabic + + 10005, // x-mac-hebrew + + 10006, // x-mac-greek + + 10007, // x-mac-cyrillic + + 10008, // x-mac-chinesesimp + + 10010, // x-mac-romanian + + 10017, // x-mac-ukrainian + + 10021, // x-mac-thai + + 10029, // x-mac-ce + + 10079, // x-mac-icelandic + + 10081, // x-mac-turkish + + 10082, // x-mac-croatian + + 20000, // x-Chinese-CNS + + 20001, // x-cp20001 + + 20002, // x-Chinese-Eten + + 20003, // x-cp20003 + + 20004, // x-cp20004 + + 20005, // x-cp20005 + + 20105, // x-IA5 + + 20106, // x-IA5-German + + 20107, // x-IA5-Swedish + + 20108, // x-IA5-Norwegian + + 20261, // x-cp20261 + + 20269, // x-cp20269 + + 20833, // x-EBCDIC-KoreanExtended + + 20936, // x-cp20936 + + 20949, // x-cp20949 + + 21025, // cp1025 + + 29001, // x-Europa + + 50227, // x-cp50227 + + 51936, // EUC-CN + + 57002, // x-iscii-de + + 57003, // x-iscii-be + + 57004, // x-iscii-ta + + 57005, // x-iscii-te + + 57006, // x-iscii-as + + 57007, // x-iscii-or + + 57008, // x-iscii-ka + + 57009, // x-iscii-ma + + 57010, // x-iscii-gu + + 57011, // x-iscii-pa + + 20420, // x-EBCDIC-Arabic + + 20880, // x-EBCDIC-CyrillicRussian + + 21025, // x-EBCDIC-CyrillicSerbianBulgarian + + 20277, // x-EBCDIC-DenmarkNorway + + 1142, // x-ebcdic-denmarknorway-euro + + 20278, // x-EBCDIC-FinlandSweden + + 1143, // x-ebcdic-finlandsweden-euro + 1143, // X-EBCDIC-France + + 1147, // x-ebcdic-france-euro + + 20273, // x-EBCDIC-Germany + + 1141, // x-ebcdic-germany-euro + + 875, // x-EBCDIC-GreekModern + + 20423, // x-EBCDIC-Greek + + 20424, // x-EBCDIC-Hebrew + + 20871, // x-EBCDIC-Icelandic + + 1149, // x-ebcdic-icelandic-euro + + 1148, // x-ebcdic-international-euro + + 20280, // x-EBCDIC-Italy + + 1144, // x-ebcdic-italy-euro + + 50930, // x-EBCDIC-JapaneseAndKana + + 50939, // x-EBCDIC-JapaneseAndJapaneseLatin + + 50931, // x-EBCDIC-JapaneseAndUSCanada + + 20290, // x-EBCDIC-JapaneseKatakana + + 50933, // x-EBCDIC-KoreanAndKoreanExtended + + 50935, // x-EBCDIC-SimplifiedChinese + + 20284, // X-EBCDIC-Spain + + 1145, // x-ebcdic-spain-euro + + 20838, // x-EBCDIC-Thai + + 50937, // x-EBCDIC-TraditionalChinese + + 20905, // x-EBCDIC-Turkish + + 20285, // x-EBCDIC-UK + + 1146, // x-ebcdic-uk-euro + + 1140, // x-ebcdic-cp-us-euro + + 0, // OSD_EBCDIC_DF04_15 + + 0, // OSD_EBCDIC_DF03_IRV + + 0, // OSD_EBCDIC_DF04_1 + + 0, // ISO-11548-1 + 0, // ISO_11548-1 + 0, // ISO_TR_11548-1 + 0, // csISO115481 + + 0, // KZ-1048 + 0, // STRK1048-2002 + 0, // RK1048 + 0, // csKZ1048 + + 0, // ISO-10646-UCS-2 + 0, // csUnicode + + 0, // ISO-10646-UCS-4 + 0, // csUCS4 + + 0, // UNICODE-1-1 + 0, // csUnicode11 + + 0, // SCSU + + 65000, // UTF-7 + + 0, // ISO-10646-UCS-Basic + 0, // csUnicodeASCII + + 0, // ISO-10646-Unicode-Latin1 + 0, // csUnicodeLatin1 + 0, // ISO-10646 + + 0, // ISO-10646-J-1 + + 0, // ISO-Unicode-IBM-1261 + 0, // csUnicodeIBM1261 + + 0, // ISO-Unicode-IBM-1268 + 0, // csUnicodeIBM1268 + + 0, // ISO-Unicode-IBM-1276 + 0, // csUnicodeIBM1276 + + 0, // ISO-Unicode-IBM-1264 + 0, // csUnicodeIBM1264 + + 0, // ISO-Unicode-IBM-1265 + 0, // csUnicodeIBM1265 + + 0, // BOCU-1 + 0, // csBOCU-1 + + 0, // ISO-8859-1-Windows-3.0-Latin-1 + 0, // csWindows30Latin1 + + 0, // ISO-8859-1-Windows-3.1-Latin-1 + 0, // csWindows31Latin1 + + 0, // ISO-8859-2-Windows-Latin-2 + 0, // csWindows31Latin2 + + 0, // ISO-8859-9-Windows-Latin-5 + 0, // csWindows31Latin5 + + 0, // Adobe-Standard-Encoding + 0, // csAdobeStandardEncoding + + 0, // Ventura-US + 0, // csVenturaUS + + 0, // Ventura-International + 0, // csVenturaInternational + + 0, // PC8-Danish-Norwegian + 0, // csPC8DanishNorwegian + + 0, // PC8-Turkish + 0, // csPC8Turkish + + 0, // IBM-Symbols + 0, // csIBMSymbols + + 20838, // IBM-Thai + 20838, // csIBMThai + + 0, // HP-Legal + 0, // csHPLegal + + 0, // HP-Pi-font + 0, // csHPPiFont + + 0, // HP-Math8 + 0, // csHPMath8 + + 0, // Adobe-Symbol-Encoding + 0, // csHPPSMath + + 0, // HP-DeskTop + 0, // csHPDesktop + + 0, // Ventura-Math + 0, // csVenturaMath + + 0, // Microsoft-Publishing + 0, // csMicrosoftPublishing + + 0, // Windows-31J + 0, // csWindows31J + + 936, // GB2312 + 936, // csGB2312 + + 950, // Big5 + 950, // csBig5 + + 52936, // HZ-GB-2312 + + 0, // IBM1047 + 0, // IBM-1047 + + 0, // PTCP154 + 0, // csPTCP154 + 0, // PT154 + 0, // CP154 + 0, // Cyrillic-Asian + + 0, // Amiga-1251 + 0, // Ami1251 + 0, // Amiga1251 + 0, // Ami-1251 + + 0, // KOI7-switched + + 0, // BRF + 0, // csBRF + + 0, // TSCII + 0, // csTSCII + + 1250, // windows-1250 + + 1251, // windows-1251 + + 1252, // windows-1252 + + 1253, // windows-1253 + + 1254, // windows-1254 + + 1255, // windows-1255 + + 1256, // windows-1256 + + 1257, // windows-1257 + + 1258, // windows-1258 + + 0, // TIS-620 + + 720, // DOS-720 + + 737 // ibm737 + + ); + +function CharsetToCodePage(const ACharSet: TIdCharSet): Word; +begin + Result := IdCharsetCodePages[ACharSet]; +end; + +function CharsetToCodePage(const ACharSet: String): Word; overload; +begin + Result := IdCharsetCodePages[FindCharset(ACharSet)]; +end; + +end. diff --git a/indy/Protocols/IdCoder.pas b/indy/Protocols/IdCoder.pas new file mode 100644 index 0000000..7f430d5 --- /dev/null +++ b/indy/Protocols/IdCoder.pas @@ -0,0 +1,480 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.18 27.08.2004 22:03:20 Andreas Hausladen + Optimized encoders + speed optimization ("const" for string parameters) + + + Rev 1.17 7/23/04 7:00:14 PM RLebeau + Added extra exception handling to DecodeString() and Encode() + + + Rev 1.16 2004.06.14 9:23:06 PM czhower + Bug fix. + + + Rev 1.15 22/05/2004 12:05:20 CCostelloe + Bug fix + + + Rev 1.14 2004.05.20 1:39:20 PM czhower + Last of the IdStream updates + + + Rev 1.13 2004.05.20 11:37:08 AM czhower + IdStreamVCL + + + Rev 1.12 2004.05.20 11:13:10 AM czhower + More IdStream conversions + + + Rev 1.11 2004.05.19 3:06:48 PM czhower + IdStream / .NET fix + + + Rev 1.10 2004.02.03 5:44:56 PM czhower + Name changes + + + Rev 1.9 1/27/2004 3:58:16 PM SPerry + StringStream ->IdStringStream + + + Rev 1.8 27/1/2004 1:57:58 PM SGrobety + Additional bug fix + + + Rev 1.6 11/10/2003 7:39:22 PM BGooijen + Did all todo's ( TStream to TIdStream mainly ) + + + Rev 1.5 2003.10.02 10:52:48 PM czhower + .Net + + + Rev 1.4 2003.06.24 12:02:08 AM czhower + Coders now decode properly again. + + + Rev 1.3 2003.06.13 6:57:08 PM czhower + Speed improvement + + + Rev 1.2 2003.06.13 3:41:18 PM czhower + Optimizaitions. + + + Rev 1.1 2003.06.13 2:24:06 PM czhower + Speed improvement + + + Rev 1.0 11/14/2002 02:14:30 PM JPMugaas +} +unit IdCoder; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdBaseComponent, + IdGlobal; + +type + TIdEncoder = class(TIdBaseComponent) + public + function Encode(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + procedure Encode(const AIn: string; ADestStrings: TStrings; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + procedure Encode(const AIn: string; ADestStream: TStream; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + + function Encode(ASrcStream: TStream; const ABytes: Integer = -1): string; overload; + procedure Encode(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); overload; + procedure Encode(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); overload; virtual; abstract; + + class function EncodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + class procedure EncodeString(const AIn: string; ADestStrings: TStrings; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + class procedure EncodeString(const AIn: string; ADestStream: TStream; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); overload; + + class function EncodeBytes(const ABytes: TIdBytes): string; overload; + class procedure EncodeBytes(const ABytes: TIdBytes; ADestStrings: TStrings); overload; + class procedure EncodeBytes(const ABytes: TIdBytes; ADestStream: TStream); overload; + + class function EncodeStream(ASrcStream: TStream; const ABytes: Integer = -1): string; overload; + class procedure EncodeStream(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); overload; + class procedure EncodeStream(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); overload; + end; + + TIdEncoderClass = class of TIdEncoder; + + TIdDecoder = class(TIdBaseComponent) + protected + FStream: TStream; + public + procedure DecodeBegin(ADestStream: TStream); virtual; + procedure DecodeEnd; virtual; + + procedure Decode(const AIn: string); overload; + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); overload; virtual; abstract; + + class function DecodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; + class function DecodeBytes(const AIn: string): TIdBytes; + class procedure DecodeStream(const AIn: string; ADestStream: TStream); + end; + + TIdDecoderClass = class of TIdDecoder; + +implementation + +uses + {$IFDEF DOTNET} + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + IdGlobalProtocols, SysUtils; + +{ TIdDecoder } + +procedure TIdDecoder.DecodeBegin(ADestStream: TStream); +begin + FStream := ADestStream; +end; + +procedure TIdDecoder.DecodeEnd; +begin + FStream := nil; +end; + +procedure TIdDecoder.Decode(const AIn: string); +var + LStream: TMemoryStream; + LEncoding: IIdTextEncoding; +begin + LStream := TMemoryStream.Create; + try + LEncoding := IndyTextEncoding_8Bit; + WriteStringToStream(LStream, AIn, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + LEncoding := nil; + LStream.Position := 0; + Decode(LStream); + finally + LStream.Free; + end; +end; + +class function TIdDecoder.DecodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + DecodeStream(AIn, LStream); + LStream.Position := 0; + EnsureEncoding(AByteEncoding, enc8Bit); + Result := ReadStringFromStream(LStream, -1, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + finally + LStream.Free; + end; +end; + +class function TIdDecoder.DecodeBytes(const AIn: string): TIdBytes; +var + LStream: TMemoryStream; +begin + Result := nil; + LStream := TMemoryStream.Create; + try + DecodeStream(AIn, LStream); + LStream.Position := 0; + ReadTIdBytesFromStream(LStream, Result, -1); + finally + FreeAndNil(LStream); + end; +end; + +class procedure TIdDecoder.DecodeStream(const AIn: string; ADestStream: TStream); +var + LDecoder: TIdDecoder; +begin + LDecoder := Create(nil); + try + LDecoder.DecodeBegin(ADestStream); + try + LDecoder.Decode(AIn); + finally + LDecoder.DecodeEnd; + end; + finally + LDecoder.Free; + end; +end; + +{ TIdEncoder } + +function TIdEncoder.Encode(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LStream: TMemoryStream; +begin + if AIn <> '' then begin + LStream := TMemoryStream.Create; + try + EnsureEncoding(AByteEncoding, enc8Bit); + WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + LStream.Position := 0; + Result := Encode(LStream); + finally + FreeAndNil(LStream); + end; + end else begin + Result := ''; + end; +end; + +procedure TIdEncoder.Encode(const AIn: string; ADestStrings: TStrings; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + EnsureEncoding(AByteEncoding, enc8Bit); + WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + LStream.Position := 0; + Encode(LStream, ADestStrings); + finally + FreeAndNil(LStream); + end; +end; + +procedure TIdEncoder.Encode(const AIn: string; ADestStream: TStream; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + EnsureEncoding(AByteEncoding, enc8Bit); + WriteStringToStream(LStream, AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + LStream.Position := 0; + Encode(LStream, ADestStream); + finally + FreeAndNil(LStream); + end; +end; + +function TIdEncoder.Encode(ASrcStream: TStream; const ABytes: Integer = -1) : string; +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + Encode(ASrcStream, LStream, ABytes); + LStream.Position := 0; + Result := ReadStringFromStream(LStream, -1, IndyTextEncoding_8Bit); + finally + FreeAndNil(LStream); + end; +end; + +procedure TIdEncoder.Encode(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); +var + LStream: TMemoryStream; +begin + ADestStrings.Clear; + // TODO: provide an Encode() implementation that can save its output directly + // to ADestStrings without having to waste memory encoding the data entirely + // to memory first. In Delphi 2009+ in particular, TStrings.LoadFromStream() + // wastes a lot of memory handling large streams... + LStream := TMemoryStream.Create; + try + Encode(ASrcStream, LStream, ABytes); + LStream.Position := 0; + ADestStrings.LoadFromStream(LStream); + finally + FreeAndNil(LStream); + end; +end; + +class function TIdEncoder.EncodeString(const AIn: string; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LEncoder: TIdEncoder; +begin + LEncoder := Create(nil); + try + Result := LEncoder.Encode(AIn, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + finally + LEncoder.Free; + end; +end; + +class procedure TIdEncoder.EncodeString(const AIn: string; ADestStrings: TStrings; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LEncoder: TIdEncoder; +begin + LEncoder := Create(nil); + try + LEncoder.Encode(AIn, ADestStrings, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + finally + LEncoder.Free; + end; +end; + +class procedure TIdEncoder.EncodeString(const AIn: string; ADestStream: TStream; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LEncoder: TIdEncoder; +begin + LEncoder := Create(nil); + try + LEncoder.Encode(AIn, ADestStream, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + finally + LEncoder.Free; + end; +end; + +class function TIdEncoder.EncodeBytes(const ABytes: TIdBytes): string; +var + LStream: TMemoryStream; +begin + if ABytes <> nil then begin + LStream := TMemoryStream.Create; + try + WriteTIdBytesToStream(LStream, ABytes); + LStream.Position := 0; + Result := EncodeStream(LStream); + finally + FreeAndNil(LStream); + end; + end else begin + Result := ''; + end; +end; + +class procedure TIdEncoder.EncodeBytes(const ABytes: TIdBytes; ADestStrings: TStrings); +var + LStream: TMemoryStream; +begin + if ABytes <> nil then begin + LStream := TMemoryStream.Create; + try + WriteTIdBytesToStream(LStream, ABytes); + LStream.Position := 0; + EncodeStream(LStream, ADestStrings); + finally + FreeAndNil(LStream); + end; + end; +end; + +class procedure TIdEncoder.EncodeBytes(const ABytes: TIdBytes; ADestStream: TStream); +var + LStream: TMemoryStream; +begin + if ABytes <> nil then begin + LStream := TMemoryStream.Create; + try + WriteTIdBytesToStream(LStream, ABytes); + LStream.Position := 0; + EncodeStream(LStream, ADestStream); + finally + FreeAndNil(LStream); + end; + end; +end; + +class function TIdEncoder.EncodeStream(ASrcStream: TStream; const ABytes: Integer = -1): string; +var + LEncoder: TIdEncoder; +begin + if ASrcStream <> nil then begin + LEncoder := Create(nil); + try + Result := LEncoder.Encode(ASrcStream, ABytes); + finally + LEncoder.Free; + end; + end else begin + Result := ''; + end; +end; + +class procedure TIdEncoder.EncodeStream(ASrcStream: TStream; ADestStrings: TStrings; const ABytes: Integer = -1); +var + LEncoder: TIdEncoder; +begin + if ASrcStream <> nil then begin + LEncoder := Create(nil); + try + LEncoder.Encode(ASrcStream, ADestStrings, ABytes); + finally + LEncoder.Free; + end; + end; +end; + +class procedure TIdEncoder.EncodeStream(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); +var + LEncoder: TIdEncoder; +begin + if ASrcStream <> nil then begin + LEncoder := Create(nil); + try + LEncoder.Encode(ASrcStream, ADestStream, ABytes); + finally + LEncoder.Free; + end; + end; +end; + +end. + + diff --git a/indy/Protocols/IdCoder00E.pas b/indy/Protocols/IdCoder00E.pas new file mode 100644 index 0000000..40cbaca --- /dev/null +++ b/indy/Protocols/IdCoder00E.pas @@ -0,0 +1,131 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 2004.05.20 1:39:26 PM czhower + Last of the IdStream updates + + Rev 1.3 2004.05.20 11:37:20 AM czhower + IdStreamVCL + + Rev 1.2 2004.05.20 11:13:16 AM czhower + More IdStream conversions + + Rev 1.1 2003.06.13 6:57:10 PM czhower + Speed improvement + + Rev 1.0 2003.06.13 4:59:36 PM czhower + Initial checkin +} + +unit IdCoder00E; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdCoder3to4; + +type + TIdDecoder00E = class(TIdDecoder4to3) + public + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override; + end; + + TIdEncoder00E = class(TIdEncoder3to4) + public + procedure Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); override; + end; + +implementation + +uses + IdGlobal, + IdStream, + SysUtils; + +{ TIdDecoder00E } + +procedure TIdDecoder00E.Decode(ASrcStream: TStream; const ABytes: Integer = -1); +var + LBuf: TIdBytes; + LSize: TIdStreamSize; + LDataLen, LExpected: Integer; +begin + LSize := IndyLength(ASrcStream, ABytes); + if LSize > 0 then begin + //Param 2 - Start at second char since 00E's have byte 1 as length + TIdStreamHelper.ReadBytes(ASrcStream, LBuf, 1); + //Param 3 - Get output length of input. This is length in bytes, + // not encoded chars. DO NOT include fill chars in calculation + {Assert(Ord(FDecodeTable[LBuf[0]]) = (((LSize-1) div 4) * 3));} + LDataLen := FDecodeTable[LBuf[0]]; + SetLength(LBuf, LSize-1); + TIdStreamHelper.ReadBytes(ASrcStream, LBuf, LSize-1); + // RLebeau 4/28/2014: encountered a situation where a UUE encoded attachment + // had some encoded lines that were supposed to end with a space character + // but were actually truncated off. Turns out that Outlook Express is known + // for doing that, for instance. Some other encoding apps might also have a + // similar flaw, so just in case let's calculate what the input length is + // supposed to be and pad the input with spaces if needed before then + // decoding it... + LExpected := ((LDataLen + 2) div 3) * 4; + if Length(LBuf) < LExpected then begin + ExpandBytes(LBuf, Length(LBuf), LExpected-Length(LBuf), Ord(' ')); // should this use FillChar instead? + end; + LBuf := InternalDecode(LBuf, True); + if Assigned(FStream) then begin + TIdStreamHelper.Write(FStream, LBuf, LDataLen); + end; + end; +end; + +{ TIdEncoder00E } + +procedure TIdEncoder00E.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); +var + LStream: TMemoryStream; + LSize: TIdStreamSize; + LEncodeSize: Integer; + LBuf: TIdBytes; +begin + SetLength(LBuf, 1); + LStream := TMemoryStream.Create; + try + LSize := IndyLength(ASrcStream, ABytes); + while LSize > 0 do + begin + LEncodeSize := IndyMin(LSize, Length(FCodingTable)-1); + inherited Encode(ASrcStream, LStream, LEncodeSize); + Dec(LSize, LEncodeSize); + LBuf[0] := FCodingTable[Integer(LEncodeSize)]; + TIdStreamHelper.Write(ADestStream, LBuf, 1); + LStream.Position := 0; + ADestStream.CopyFrom(LStream, 0); + if LSize > 0 then begin + WriteStringToStream(ADestStream, EOL); + LStream.Clear; + end; + end; + finally + FreeAndNil(LStream); + end; +end; + +end. diff --git a/indy/Protocols/IdCoder3to4.pas b/indy/Protocols/IdCoder3to4.pas new file mode 100644 index 0000000..0884dfc --- /dev/null +++ b/indy/Protocols/IdCoder3to4.pas @@ -0,0 +1,407 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.30 15.09.2004 22:38:22 Andreas Hausladen + Added "Delphi 7.1 compiler warning bug" fix code + + + Rev 1.29 27.08.2004 22:03:22 Andreas Hausladen + Optimized encoders + speed optimization ("const" for string parameters) + + + Rev 1.28 7/8/04 5:09:04 PM RLebeau + Updated Encode() to remove use of local TIdBytes variable + + + Rev 1.27 2004.05.20 1:39:20 PM czhower + Last of the IdStream updates + + + Rev 1.26 2004.05.20 11:37:08 AM czhower + IdStreamVCL + + + Rev 1.25 2004.05.20 11:13:12 AM czhower + More IdStream conversions + + + Rev 1.24 2004.05.19 3:06:54 PM czhower + IdStream / .NET fix + + + Rev 1.23 2004.03.12 7:54:18 PM czhower + Removed old commented out code. + + + Rev 1.22 11/03/2004 22:36:14 CCostelloe + Bug fix (1 to 3 spurious extra characters at the end of UUE encoded messages, + see comment starting CC3. + + + Rev 1.21 2004.02.03 5:44:56 PM czhower + Name changes + + + Rev 1.20 28/1/2004 6:22:16 PM SGrobety + Removed base 64 encoding stream length check is stream size was provided + + + Rev 1.19 16/01/2004 17:47:48 CCostelloe + Restructured slightly to allow IdCoderBinHex4 reuse some of its code + + + Rev 1.18 02/01/2004 20:59:28 CCostelloe + Fixed bugs to get ported code to work in Delphi 7 (changes marked CC2) + + + Rev 1.17 11/10/2003 7:54:14 PM BGooijen + Did all todo's ( TStream to TIdStream mainly ) + + + Rev 1.16 2003.10.24 10:43:02 AM czhower + TIdSTream to dos + + + Rev 1.15 22/10/2003 12:25:36 HHariri + Stephanes changes + + + Rev 1.14 10/16/2003 11:10:18 PM DSiders + Added localization comments, whitespace. + + + Rev 1.13 2003.10.11 10:00:12 PM czhower + Compiles again + + + Rev 1.12 10/5/2003 4:31:02 PM GGrieve + use ToBytes for Cardinal to Bytes conversion + + + Rev 1.11 10/4/2003 9:12:18 PM GGrieve + DotNet + + + Rev 1.10 2003.06.24 12:02:10 AM czhower + Coders now decode properly again. + + + Rev 1.9 2003.06.23 10:53:16 PM czhower + Removed unused overriden methods. + + + Rev 1.8 2003.06.13 6:57:10 PM czhower + Speed improvement + + + Rev 1.7 2003.06.13 3:41:18 PM czhower + Optimizaitions. + + + Rev 1.6 2003.06.13 2:24:08 PM czhower + Speed improvement + + + Rev 1.5 10/6/2003 5:37:02 PM SGrobety + Bug fix in decoders. + + + Rev 1.4 6/6/2003 4:50:30 PM SGrobety + Reworked the 3to4decoder for performance and stability. + Note that encoders haven't been touched. Will come later. Another problem: + input is ALWAYS a string. Should be a TStream. + + 1/ Fix: added filtering for #13,#10 and #32 to the decoding mechanism. + 2/ Optimization: Speed the decoding by a factor 7-10 AND added filtering ;) + Could still do better by using a pointer and a stiding window by a factor 2-3. + 3/ Improvement: instead of writing everything to the output stream, there is + an internal buffer of 4k. It should speed things up when working on large + data (no large chunk of memory pre-allocated while keeping a decent perf by + not requiring every byte to be written separately). + + + Rev 1.3 28/05/2003 10:06:56 CCostelloe + StripCRLFs changes stripped out at the request of Chad + + + Rev 1.2 20/05/2003 02:01:00 CCostelloe + + + Rev 1.1 20/05/2003 01:44:12 CCostelloe + Bug fix: decoder code altered to ensure that any CRLFs inserted by an MTA are + removed + + + Rev 1.0 11/14/2002 02:14:36 PM JPMugaas +} +unit IdCoder3to4; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdCoder, + IdGlobal, + SysUtils; + +type + TIdDecodeTable = array[1..127] of Byte; + + TIdEncoder3to4 = class(TIdEncoder) + protected + FCodingTable: TIdBytes; + FFillChar: Char; + function InternalEncode(const ABuffer: TIdBytes): TIdBytes; + public + procedure Encode(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); override; + property CodingTable: TIdBytes read FCodingTable; + published + property FillChar: Char read FFillChar write FFillChar; + end; + + TIdEncoder3to4Class = class of TIdEncoder3to4; + + TIdDecoder4to3 = class(TIdDecoder) + protected + FCodingTable: TIdBytes; + FDecodeTable: TIdDecodeTable; + FFillChar: Char; + function InternalDecode(const ABuffer: TIdBytes; const AIgnoreFiller: Boolean = False): TIdBytes; + public + class procedure ConstructDecodeTable(const ACodingTable: String; var ADecodeArray: TIdDecodeTable); + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override; + published + property FillChar: Char read FFillChar write FFillChar; + end; + +implementation + +uses + IdException, IdResourceStrings, IdStream; + +{ TIdDecoder4to3 } + +class procedure TIdDecoder4to3.ConstructDecodeTable(const ACodingTable: string; + var ADecodeArray: TIdDecodeTable); +var + c, i: integer; +begin + //TODO: See if we can find an efficient way, or maybe an option to see if the requested + //decode char is valid, that is it returns a 255 from the DecodeTable, or at maybe + //check its presence in the encode table. + for i := Low(ADecodeArray) to High(ADecodeArray) do begin + ADecodeArray[i] := $FF; + end; + c := 0; + for i := 1 to Length(ACodingTable) do begin + ADecodeArray[Ord(ACodingTable[i])] := c; + Inc(c); + end; +end; + +procedure TIdDecoder4to3.Decode(ASrcStream: TStream; const ABytes: Integer = -1); +var + LBuffer: TIdBytes; + LBufSize: Integer; +begin + // No no - this will read the whole thing into memory and what if its MBs? + // need to load it in smaller buffered chunks MaxInt is WAY too big.... + LBufSize := IndyLength(ASrcStream, ABytes); + if LBufSize > 0 then begin + SetLength(LBuffer, LBufSize); + TIdStreamHelper.ReadBytes(ASrcStream, LBuffer, LBufSize); + LBuffer := InternalDecode(LBuffer); + if Assigned(FStream) then begin + TIdStreamHelper.Write(FStream, LBuffer); + end; + end; +end; + +function TIdDecoder4to3.InternalDecode(const ABuffer: TIdBytes; const AIgnoreFiller: Boolean): TIdBytes; +var + LInBufSize: Integer; + LEmptyBytes: Integer; + LInBytes: TIdBytes; + LOutPos: Integer; + LOutSize: Integer; + LInLimit: Integer; + LInPos: Integer; +begin + SetLength(LInBytes, 4); + + LInPos := 0; + + LInBufSize := Length(ABuffer); + if (LInBufSize mod 4) <> 0 then begin + LInLimit := (LInBufSize div 4) * 4; + end else begin + LInLimit := LInBufSize; + end; + + // Presize output buffer + //CC2, bugfix: was LOutPos := 1; + LOutPos := 0; + LOutSize := (LInLimit div 4) * 3; + SetLength(Result, LOutSize); + + while LInPos < LInLimit do begin + // Read 4 bytes in for processing + //CC2 bugfix: was CopyTIdBytes(LIn, LInPos, LInBytes, 0, LInBytesLen); + //CopyTIdBytes(LIn, LInPos-1, LInBytes, 0, LInBytesLen); + // Faster than CopyTIdBytes + LInBytes[0] := ABuffer[LInPos]; + LInBytes[1] := ABuffer[LInPos + 1]; + LInBytes[2] := ABuffer[LInPos + 2]; + LInBytes[3] := ABuffer[LInPos + 3]; + // Inc pointer + Inc(LInPos, 4); + + // Reduce to 3 bytes + Result[LOutPos] := ((FDecodeTable[LInBytes[0]] and 63) shl 2) or ((FDecodeTable[LInBytes[1]] shr 4) and 3); + Result[LOutPos + 1] := ((FDecodeTable[LInBytes[1]] and 15) shl 4) or ((FDecodeTable[LInBytes[2]] shr 2) and 15); + Result[LOutPos + 2] := ((FDecodeTable[LInBytes[2]] and 3) shl 6) or (FDecodeTable[LInBytes[3]] and 63); + Inc(LOutPos, 3); + + // If we dont know how many bytes we need to watch for fill chars. MIME + // is this way. + // + // In best case, the end is not before the end of the input, but the input + // may be right padded with spaces, or even contain the EOL chars. + // + // Because of this we watch for early ends beyond what we originally + // estimated. + end; + + // RLebeau: normally, the FillChar does not appear inside the encoded bytes, + // however UUE/XXE does allow it, where encoded lines are prefixed with the + // unencoded data lengths instead... + if (not AIgnoreFiller) and (LInPos > 0) then begin + if ABuffer[LInPos-1] = Ord(FillChar) then begin + if ABuffer[LInPos-2] = Ord(FillChar) then begin + LEmptyBytes := 2; + end else begin + LEmptyBytes := 1; + end; + SetLength(Result, LOutSize - LEmptyBytes); + end; + end; +end; + +{ TIdEncoder3to4 } + +procedure TIdEncoder3to4.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); +var + LBuffer: TIdBytes; + LBufSize: Integer; +begin + // No no - this will read the whole thing into memory and what if its MBs? + // need to load it in smaller buffered chunks MaxInt is WAY too big.... + LBufSize := IndyLength(ASrcStream, ABytes); + if LBufSize > 0 then begin + SetLength(LBuffer, LBufSize); + TIdStreamHelper.ReadBytes(ASrcStream, LBuffer, LBufSize); + LBuffer := InternalEncode(LBuffer); + TIdStreamHelper.Write(ADestStream, LBuffer); + end; +end; + +//TODO: Make this more efficient. Profile it to test, but maybe make single +// calls to ReadBuffer then pull from memory +function TIdEncoder3to4.InternalEncode(const ABuffer: TIdBytes): TIdBytes; +var + LInBufSize : Integer; + LOutSize: Integer; + LLen : integer; + LPos : Integer; + LBufDataLen: Integer; + LIn1, LIn2, LIn3: Byte; + LSize : Integer; +begin + LInBufSize := Length(ABuffer); + LOutSize := ((LInBufSize + 2) div 3) * 4; + SetLength(Result, LOutSize); // we know that the string will grow by 4/3 adjusted to 3 boundary + LLen := 0; + LPos := 0; + + // S.G. 21/10/2003: Copy the relevant bytes into the temporary buffer. + // S.G. 21/10/2003: Record the data length and force exit loop when necessary + while LPos < LInBufSize do + begin + Assert((LLen + 4) <= LOutSize, + 'TIdEncoder3to4.Encode: Calculated length exceeded (expected '+ {do not localize} + IntToStr(LOutSize) + + ', about to go '+ {do not localize} + IntToStr(LLen + 4) + + ' at offset ' + {do not localize} + IntToStr(LPos) + + ' of '+ {do not localize} + IntToStr(LInBufSize)); + + LBufDataLen := LInBufSize - LPos; + if LBufDataLen > 2 then begin + LIn1 := ABuffer[LPos]; + LIn2 := ABuffer[LPos+1]; + LIn3 := ABuffer[LPos+2]; + LSize := 3; + end + else if LBufDataLen > 1 then begin + LIn1 := ABuffer[LPos]; + LIn2 := ABuffer[LPos+1]; + LIn3 := 0; + LSize := 2; + end + else begin + LIn1 := ABuffer[LPos]; + LIn2 := 0; + LIn3 := 0; + LSize := 1; + end; + Inc(LPos, LSize); + + //possible to do a better assert than this? + Assert(Length(FCodingTable)>0); + Result[LLen] := FCodingTable[(LIn1 shr 2) and 63]; + Result[LLen + 1] := FCodingTable[(((LIn1 and 3) shl 4) or ((LIn2 shr 4) and 15)) and 63]; + Result[LLen + 2] := FCodingTable[(((LIn2 and 15) shl 2) or ((LIn3 shr 6) and 3)) and 63]; + Result[LLen + 3] := FCodingTable[LIn3 and 63]; + Inc(LLen, 4); + + if LSize < 3 then begin + Result[LLen-1] := Ord(FillChar); + if LSize = 1 then begin + Result[LLen-2] := Ord(FillChar); + end; + end; + end; + + SetLength(Result, LLen); + + Assert(LLen = LOutSize, + 'TIdEncoder3to4.Encode: Calculated length not met (expected ' + {do not localize} + IntToStr(LOutSize) + + ', finished at ' + {do not localize} + IntToStr(LLen) + + ', BufSize = ' + {do not localize} + IntToStr(LInBufSize)); +end; + +end. + diff --git a/indy/Protocols/IdCoderBinHex4.pas b/indy/Protocols/IdCoderBinHex4.pas new file mode 100644 index 0000000..def2015 --- /dev/null +++ b/indy/Protocols/IdCoderBinHex4.pas @@ -0,0 +1,511 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 10/6/2004 10:47:00 PM BGooijen + changed array indexer from 64 to 32 bit, it gave errors in dotnet, and making + >2GB arrays is not done anyways + + Rev 1.6 2004.05.20 1:39:28 PM czhower + Last of the IdStream updates + + Rev 1.5 2004.05.20 11:37:24 AM czhower + IdStreamVCL + + Rev 1.4 2004.05.19 3:06:56 PM czhower + IdStream / .NET fix + + Rev 1.3 2004.02.03 5:45:50 PM czhower + Name changes + + Rev 1.2 1/21/2004 1:19:58 PM JPMugaas + InitComponent. + + Rev 1.1 16/01/2004 18:00:26 CCostelloe + This is now working code. + + Rev 1.0 14/01/2004 00:46:14 CCostelloe + An implementation of Apple's BinHex4 encoding. It is a "work-in-progress", + it does not yet work properly, only checked in as a placeholder. +} + +unit IdCoderBinHex4; + +{ + Written by Ciaran Costelloe, ccostelloe@flogas.ie, December 2003. + Based on TIdCoderMIME, derived from TIdCoder3to4, derived from TIdCoder. + + DESCRIPTION: + + This is an implementation of the BinHex 4.0 decoder used particularly by Apple. + It is defined in RFC 1741. It is a variant of a 3-to-4 decoder, but it uses + character 90 for sequences of repeating characters, allowing some compression, + but thereby not allowing it to be mapped in as another 3-to-4 decoder. + Per the RFC, it must be encapsulated in a MIME part (it cannot be directly coded + inline in an email "body"), the part is strictly defined to have a header entry + (with the appropriate "myfile.ext"): + Content-Type: application/mac-binhex40; name="myfile.ext" + After the header, the part MUST start with the text (NOT indented): + (This file must be converted with BinHex 4.0) + This allows the option AND the ambiguity of identifying it by either the + Content-Type OR by the initial text line. However, it is also stated that any + text before the specified text line must be ignored, implying the line does not + have to be the first - an apparent contradiction. + The encoded file then follows, split with CRLFs (to avoid lines that are too long + for emails) that must be discarded. + The file starts with a colon (:), a header, followed by the file contents, and + ending in another colon. + There is also an interesting article on the web, "BinHex 4.0 Definition by Peter + N Lewis, Aug 1991", which has very useful information on what is implemeted in + practice, and seems to come with the good provenance of bitter experience. + + From RFC 1741: + + 1) 8 bit encoding of the file: + + Byte: Length of FileName (1->63) + Bytes: FileName ("Length" bytes) + Byte: Version + Long: Type + Long: Creator + Word: Flags (And $F800) + Long: Length of Data Fork + Long: Length of Resource Fork + Word: CRC + Bytes: Data Fork ("Data Length" bytes) + Word: CRC + Bytes: Resource Fork ("Rsrc Length" bytes) + Word: CRC + + 2) Compression of repetitive characters. + + ($90 is the marker, encoding is made for 3->255 characters) + + 00 11 22 33 44 55 66 77 -> 00 11 22 33 44 55 66 77 + 11 22 22 22 22 22 22 33 -> 11 22 90 06 33 + 11 22 90 33 44 -> 11 22 90 00 33 44 + + The whole file is considered as a stream of bits. This stream will + be divided in blocks of 6 bits and then converted to one of 64 + characters contained in a table. The characters in this table have + been chosen for maximum noise protection. The format will start + with a ":" (first character on a line) and end with a ":". + There will be a maximum of 64 characters on a line. It must be + preceded, by this comment, starting in column 1 (it does not start + in column 1 in this document): + + (This file must be converted with BinHex 4.0) + + Any text before this comment is to be ignored. + + The characters used are: + + !"#$%&'()*+,- 012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr + + IMPLEMENTATION NOTES: + + There are older variants referred to in RFC 1741, but I have only come + across encodings in current use as separate MIME parts, which this + implementation is targetted at. + + When encoding into BinHex4, you do NOT have to implement the run-length + encoding (the character 90 for sequences of repeating characters), and + this encoder does not do it. The CRC values generated in the header have + NOT been tested (because this decoder ignores them). + + The decoder has to allow for the run-length encoding. The decoder works + irrespective of whether it is preceded by the identification string + or not (GBinHex4IdentificationString below). The string to be decoded must + include the starting and ending colons. It can deal with embedded CR and LFs. + Unlike base64 and quoted-printable, we cannot decode line-by-line cleanly, + because the lines do not contain a clean number of 4-byte blocks due to the + first line starting with a colon, leaving 63 bytes on that line, plus you have + the problem of dealing with the run-length encoding and stripping the header. + If the attachment only has a data fork, it is saved; if only a resource fork, + it is saved; if both, only the data fork is saved. The decoder does NOT + check that the CRC values are correct. + + Indy units use the content-type to decide if the part is BinHex4: + Content-Type: application/mac-binhex40; name="myfile.ext" + + WARNING: This code only implements BinHex4.0 when used as a part in a + MIME-encoded email. To have a part encoded, set the parts + ContentTransfer := 'binhex40'. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdCoder, + IdCoder3to4, + IdGlobal, + IdStream, + SysUtils; + +type + TIdEncoderBinHex4 = class(TIdEncoder3to4) + protected + FFileName: String; + function GetCRC(const ABlock: TIdBytes; const AOffset: Integer = 0; const ASize: Integer = -1): Word; + procedure AddByteCRC(var ACRC: Word; AByte: Byte); + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + procedure Encode(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); override; + //We need to specify this value before calling Encode... + property FileName: String read FFileName write FFileName; + end; + + TIdDecoderBinHex4 = class(TIdDecoder4to3) + protected + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override; + end; + +const + //Note the 7th characeter is a ' which is represented in a string as '' + GBinHex4CodeTable: string = '!"#$%&''()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr'; {Do not Localize} + GBinHex4IdentificationString: string = '(This file must be converted with BinHex 4.0)'; {Do not Localize} + +type + EIdMissingColon = class(EIdException); + EIdMissingFileName = class(EIdException); + +var + GBinHex4DecodeTable: TIdDecodeTable; + +implementation + +uses + IdResourceStrings; + +{ TIdDecoderBinHex4 } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDecoderBinHex4.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDecoderBinHex4.InitComponent; +begin + inherited InitComponent; + FDecodeTable := GBinHex4DecodeTable; + FCodingTable := ToBytes(GBinHex4CodeTable); + FFillChar := '='; {Do not Localize} +end; + +procedure TIdDecoderBinHex4.Decode(ASrcStream: TStream; const ABytes: Integer = -1); +var + LCopyToPos: integer; + LIn : TIdBytes; + LInSize: Integer; + LOut: TIdBytes; + LN: Integer; + LRepetition: Integer; + LForkLength: Integer; +begin + LInSize := IndyLength(ASrcStream, ABytes); + if LInSize <= 0 then begin + Exit; + end; + + SetLength(LIn, LInSize); + TIdStreamHelper.ReadBytes(ASrcStream, LIn, LInSize); + + //We don't need to check if the identification string is present, since the + //attachment is bounded by a : at the start and end, and the identification + //string may have been stripped off already. + //While we are at it, remove all the CRs and LFs... + LCopyToPos := -1; + for LN := 0 to LInSize-1 do begin + if LIn[LN] = 58 then begin //Ascii 58 is a colon : + if LCopyToPos = -1 then begin + //This is the start of the file... + LCopyToPos := 0; + end else begin + //This is the second :, i.e. the end of the file... + SetLength(LIn, LCopyToPos); + LCopyToPos := -2; //Flag that we got an end marker + Break; + end; + end else begin + if (LCopyToPos > -1) and (not ByteIsInEOL(LIn, LN)) then begin + LIn[LCopyToPos] := LIn[LN]; + Inc(LCopyToPos); + end; + end; + end; + + //did we get the initial colon? + if LCopyToPos = -1 then begin + raise EIdMissingColon.Create('Block passed to TIdDecoderBinHex4.Decode is missing a starting colon :'); {Do not Localize} + end; + //did we get the terminating colon? + if LCopyToPos <> -2 then begin + raise EIdMissingColon.Create('Block passed to TIdDecoderBinHex4.Decode is missing a terminating colon :'); {Do not Localize} + end; + + if Length(LIn) = 0 then begin + Exit; + end; + + LOut := InternalDecode(LIn); + + // Now expand the run-length encoding. + // $90 is the marker, encoding is made for 3->255 characters + // 00 11 22 33 44 55 66 77 -> 00 11 22 33 44 55 66 77 + // 11 22 22 22 22 22 22 33 -> 11 22 90 06 33 + // 11 22 90 33 44 -> 11 22 90 00 33 44 + LN := 0; + while LN < Length(LOut) do begin + if LOut[LN] = $90 then begin + LRepetition := LOut[LN+1]; + if LRepetition = 0 then begin + //90 is by itself, so just remove the 00 + //22 90 00 -> 22 90 + RemoveBytes(LOut, LN+1, 1); + Inc(LN); //Move past the $90 + end + else if LRepetition = 1 then begin + //Not allowed: 22 90 01 -> 22 + //Throw an exception or deal with it? Deal with it. + RemoveBytes(LOut, LN, 2); + end + else if LRepetition = 2 then begin + //Not allowed: 22 90 02 -> 22 22 + //Throw an exception or deal with it? Deal with it. + LOut[LN] := LOut[LN-1]; + RemoveBytes(LOut, LN+1, 1); + Inc(LN); + end + else if LRepetition = 3 then begin + //22 90 03 -> 22 22 22 + LOut[LN] := LOut[LN-1]; + LOut[LN+1] := LOut[LN-1]; + Inc(LN, 2); + end + else begin + //Repetition is 4 to 255: expand the sequence. + //22 90 04 -> 22 22 22 22 + LOut[LN] := LOut[LN-1]; + LOut[LN+1] := LOut[LN-1]; + ExpandBytes(LOut, LN+2, LRepetition-2, LOut[LN-1]); + Inc(LN, LRepetition-1); + end; + end else begin + Inc(LN); + end; + end; + + //We are not finished yet. Strip off the header, by calculating the offset + //of the start of the attachment and it's length. + LN := 1 + LOut[0]; //Length byte + length of filename + Inc(LN, 1 + 4 + 4 + 2); //Version, type, creator, flags + // TODO: use one of the BytesTo...() functions here instead? + LForkLength := (((((LOut[LN]*256)+LOut[LN+1])*256)+LOut[LN+2])*256)+LOut[LN+3]; + Inc(LN, 4); //Go past the data fork length + if LForkLength = 0 then begin + //No data fork present, save the resource fork instead... + // TODO: use one of the BytesTo...() functions here instead? + LForkLength := (((((LOut[LN]*256)+LOut[LN+1])*256)+LOut[LN+2])*256)+LOut[LN+3]; + end; + Inc(LN, 4); //Go past the resource fork length + Inc(LN, 2); //CRC + + //At this point, LOut[LN] points to the actual data (the data fork, if there + //is one, or else the resource fork if there is no data fork). + if Assigned(FStream) then begin + TIdStreamHelper.Write(FStream, LOut, LForkLength, LN); + end; +end; + +{ TIdEncoderBinHex4 } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdEncoderBinHex4.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdEncoderBinHex4.InitComponent; +begin + inherited InitComponent; + FCodingTable := ToBytes(GBinHex4CodeTable); + FFillChar := '='; {Do not Localize} +end; + +function TIdEncoderBinHex4.GetCRC(const ABlock: TIdBytes; const AOffset: Integer = 0; + const ASize: Integer = -1): Word; +var + LN: Integer; + LActual: Integer; +begin + Result := 0; + LActual := IndyLength(ABlock, ASize, AOffset); + if LActual > 0 then + begin + for LN := 0 to LActual-1 do begin + AddByteCRC(Result, ABlock[AOffset+LN]); + end; + end; +end; + +procedure TIdEncoderBinHex4.AddByteCRC(var ACRC: Word; AByte: Byte); + //BinHex 4.0 uses a 16-bit CRC with an 0x1021 seed. +var + LWillShiftedOutBitBeA1: boolean; + LN: integer; +begin + for LN := 1 to 8 do begin + LWillShiftedOutBitBeA1 := (ACRC and $8000) <> 0; + //Shift the CRC left, and add the next bit from our byte... + ACRC := (ACRC shl 1) or (AByte shr 7); + if LWillShiftedOutBitBeA1 then begin + ACRC := ACRC xor $1021; + end; + AByte := (AByte shl 1) and $FF; + end; +end; + +procedure TIdEncoderBinHex4.Encode(ASrcStream: TStream; ADestStream: TStream; const ABytes: Integer = -1); +var + LN: Integer; + LOffset: Integer; + LBlocks: Integer; + LOut: TIdBytes; + LSSize, LTemp: Integer; + LFileName: {$IFDEF HAS_AnsiString}AnsiString{$ELSE}TIdBytes{$ENDIF}; + LCRC: word; + LRemainder: integer; +begin + if FFileName = '' then begin + raise EIdMissingFileName.Create('Data passed to TIdEncoderBinHex4.Encode is missing a filename'); {Do not Localize} + end; + //Read in the attachment first... + LSSize := IndyLength(ASrcStream, ABytes); + //BinHex4.0 allows filenames to be only 255 bytes long (because the length + //is stored in a byte), so truncate the filename to 255 bytes... + {$IFNDEF HAS_AnsiString} + LFileName := IndyTextEncoding_OSDefault.GetBytes(FFileName); + {$ELSE} + {$IFDEF STRING_IS_UNICODE} + LFileName := AnsiString(FFileName); // explicit convert to Ansi + {$ELSE} + LFileName := FFileName; + {$ENDIF} + {$ENDIF} + if Length(FFileName) > 255 then begin + SetLength(LFileName, 255); + end; + //Construct the header... + SetLength(LOut, 1+Length(LFileName)+1+4+4+2+4+4+2+LSSize+2); + LOut[0] := Length(LFileName); //Length of filename in 1st byte + for LN := 1 to Length(LFileName) do begin + LOut[LN] := {$IFNDEF HAS_AnsiString}LFileName[LN-1]{$ELSE}Byte(LFileName[LN]){$ENDIF}; + end; + LOffset := 1+Length(LFileName); //Points to byte after filename + LOut[LOffset] := 0; //Version + Inc(LOffset); + for LN := 0 to 7 do begin + LOut[LOffset+LN] := 32; //Use spaces for Type & Creator + end; + Inc(LOffset, 8); + LOut[LOffset] := 0; //Flags + LOut[LOffset] := 0; //Flags + Inc(LOffset, 2); + LTemp := LSSize; + LOut[LOffset] := LTemp mod 256; //Length of data fork + LTemp := LTemp div 256; + LOut[LOffset+1] := LTemp mod 256; //Length of data fork + LTemp := LTemp div 256; + LOut[LOffset+2] := LTemp mod 256; //Length of data fork + LTemp := LTemp div 256; + LOut[LOffset+3] := LTemp; //Length of data fork + Inc(LOffset, 4); + LOut[LOffset] := 0; //Length of resource fork + LOut[LOffset+1] := 0; //Length of resource fork + LOut[LOffset+2] := 0; //Length of resource fork + LOut[LOffset+3] := 0; //Length of resource fork + Inc(LOffset, 4); + //Next comes the CRC for the header... + LCRC := GetCRC(LOut, 0, LOffset); + LOut[LOffset] := LCRC mod 256; //CRC of data fork + LCRC := LCRC div 256; + LOut[LOffset+1] := LCRC; //CRC of data fork + Inc(LOffset, 2); + //Next comes the data fork (we will not be using the resource fork)... + //Copy in the attachment... + TIdStreamHelper.ReadBytes(ASrcStream, LOut, LSSize, LOffset); + LCRC := GetCRC(LOut, LOffset, LSSize); + Inc(LOffset, LSSize); + LOut[LOffset] := LCRC mod 256; //CRC of data fork + LCRC := LCRC div 256; + LOut[LOffset+1] := LCRC; //CRC of data fork + Inc(LOffset, 2); + //To prepare for the 3to4 encoder, make sure our block is a multiple of 3... + LSSize := LOffset mod 3; + if LSSize > 0 then begin + ExpandBytes(LOut, LOffset, 3-LSSize); + end; + //We now need to 3to4 encode LOut... + //TODO: compress repetitive bytes to " $90 " + LOut := InternalEncode(LOut); + //Need to add a colon at the start & end of the block... + InsertByte(LOut, 58, 0); + AppendByte(LOut, 58); + //Expand any bare $90 to $90 $00 + LN := 0; + while LN < Length(LOut) do begin + if LOut[LN] = $90 then begin + InsertByte(LOut, 0, LN+1); + Inc(LN); + end; + Inc(LN); + end; + + WriteStringToStream(ADestStream, GBinHex4IdentificationString + EOL); + + //Put back in our CRLFs. A max of 64 chars are allowed per line. + LBlocks := Length(LOut) div 64; + for LN := 0 to LBlocks-1 do begin + TIdStreamHelper.Write(ADestStream, LOut, 64, LN*64); + WriteStringToStream(ADestStream, EOL); + end; + LRemainder := Length(LOut) mod 64; + if LRemainder > 0 then begin + TIdStreamHelper.Write(ADestStream, LOut, LRemainder, LBlocks*64); + WriteStringToStream(ADestStream, EOL); + end; +end; + +initialization + TIdDecoder4to3.ConstructDecodeTable(GBinHex4CodeTable, GBinHex4DecodeTable); +end. + diff --git a/indy/Protocols/IdCoderHeader.pas b/indy/Protocols/IdCoderHeader.pas new file mode 100644 index 0000000..c580296 --- /dev/null +++ b/indy/Protocols/IdCoderHeader.pas @@ -0,0 +1,600 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.13 9/8/2004 8:55:46 PM JPMugaas + Fix for compile problem where a char is being compared with an incompatible + type in some compilers. + + + Rev 1.12 02/07/2004 21:59:28 CCostelloe + Bug fix + + + Rev 1.11 17/06/2004 14:19:00 CCostelloe + Bug fix for long subject lines that have characters needing CharSet encoding + + + Rev 1.10 23/04/2004 20:33:04 CCostelloe + Minor change to support From headers holding multiple addresses + + + Rev 1.9 2004.02.03 5:44:58 PM czhower + Name changes + + + Rev 1.8 24/01/2004 19:08:14 CCostelloe + Cleaned up warnings + + + Rev 1.7 1/22/2004 3:56:38 PM SPerry + fixed set problems + + + Rev 1.6 2004.01.22 2:34:58 PM czhower + TextIsSame + D8 bug workaround + + + Rev 1.5 10/16/2003 11:11:02 PM DSiders + Added localization comments. + + + Rev 1.4 10/8/2003 9:49:36 PM GGrieve + Use IdDelete + + + Rev 1.3 6/10/2003 5:48:46 PM SGrobety + DotNet updates + + + Rev 1.2 04/09/2003 20:35:28 CCostelloe + Parameter AUseAddressForNameIfNameMissing (defaulting to False to preserve + existing code) added to EncodeAddressItem + + + Rev 1.1 2003.06.23 9:46:52 AM czhower + Russian, Ukranian support for headers. + + + Rev 1.0 11/14/2002 02:14:46 PM JPMugaas +} +unit IdCoderHeader; + +//refer http://www.faqs.org/rfcs/rfc2047.html + +//TODO: Optimize and restructure code +//TODO: Redo this unit to fit with the new coders and use the exisiting MIME stuff + +{ +2002-08-21 JM Berg + - brought in line with the RFC regarding + whitespace between encoded words + - added logic so that lines that already seem encoded are really encoded again + (so that if a user types =?iso8859-1?Q?======?= its really encoded again + and displayed like that on the other side) +2001-Nov-18 Peter Mee + - Fixed multiple QP decoding in single header. +11-10-2001 - J. Peter Mugaas + - tiny fix for 8bit header encoding suggested by Andrew P.Rybin +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdComponent, + IdEMailAddress, + IdHeaderCoderBase; + +// Procs + function EncodeAddressItem(EmailAddr: TIdEmailAddressItem; const HeaderEncoding: Char; + const MimeCharSet: string; AUseAddressForNameIfNameMissing: Boolean = False): string; + function EncodeHeader(const Header: string; Specials: String; const HeaderEncoding: Char; + const MimeCharSet: string): string; + function EncodeAddress(EmailAddr: TIdEMailAddressList; const HeaderEncoding: Char; + const MimeCharSet: string; AUseAddressForNameIfNameMissing: Boolean = False): string; + function DecodeHeader(const Header: string): string; + procedure DecodeAddress(EMailAddr: TIdEmailAddressItem); + procedure DecodeAddresses(AEMails: String; EMailAddr: TIdEmailAddressList); + +implementation + +uses + IdException, + IdGlobal, + IdGlobalProtocols, + IdAllHeaderCoders, + SysUtils; + +const + csAddressSpecials: String = '()[]<>:;.,@\"'; {Do not Localize} + + base64_tbl: array [0..63] of Char = ( + 'A','B','C','D','E','F','G','H', {Do not Localize} + 'I','J','K','L','M','N','O','P', {Do not Localize} + 'Q','R','S','T','U','V','W','X', {Do not Localize} + 'Y','Z','a','b','c','d','e','f', {Do not Localize} + 'g','h','i','j','k','l','m','n', {Do not Localize} + 'o','p','q','r','s','t','u','v', {Do not Localize} + 'w','x','y','z','0','1','2','3', {Do not Localize} + '4','5','6','7','8','9','+','/'); {Do not Localize} + +function EncodeAddressItem(EmailAddr: TIdEmailAddressItem; const HeaderEncoding: Char; + const MimeCharSet: string; AUseAddressForNameIfNameMissing: Boolean = False): string; +var + S : string; + I : Integer; + NeedEncode : Boolean; +begin + if AUseAddressForNameIfNameMissing and (EmailAddr.Name = '') then begin + {CC: Use Address as Name...} + EmailAddr.Name := EmailAddr.Address; + end; + if EmailAddr.Name <> '' then {Do not Localize} + begin + NeedEncode := False; + for I := 1 to Length(EmailAddr.Name) do begin + if (EmailAddr.Name[I] < #32) or (EmailAddr.Name[I] >= #127) then + begin + NeedEncode := True; + Break; + end; + end; + if NeedEncode then begin + S := EncodeHeader(EmailAddr.Name, csAddressSpecials, HeaderEncoding, MimeCharSet); + end else begin + { quoted string } + S := '"'; {Do not Localize} + for I := 1 to Length(EmailAddr.Name) do + begin { quote special characters } + if (EmailAddr.Name[I] = '\') or (EmailAddr.Name[I] = '"') then begin + S := S + '\'; {Do not Localize} + end; + S := S + EmailAddr.Name[I]; + end; + S := S + '"'; {Do not Localize} + end; + Result := IndyFormat('%s <%s>', [S, EmailAddr.Address]) {Do not Localize} + end + else begin + Result := IndyFormat('%s', [EmailAddr.Address]); {Do not Localize} + end; +end; + +function B64(AChar: Char): Byte; +//TODO: Make this use the more efficient MIME Coder +begin + for Result := Low(base64_tbl) to High(base64_tbl) do begin + if AChar = base64_tbl[Result] then begin + Exit; + end; + end; + Result := 0; +end; + +function DecodeHeader(const Header: string): string; +var + HeaderCharSet, HeaderEncoding, HeaderData, S: string; + LDecoded: Boolean; + LStartPos, LLength, LEncodingStartPos, LEncodingEndPos, LLastStartPos: Integer; + LLastWordWasEncoded: Boolean; + Buf: TIdBytes; + + function ExtractEncoding(const AHeader: string; const AStartPos: Integer; + var VStartPos, VEndPos: Integer; var VCharSet, VEncoding, VData: String): Boolean; + var + LCharSet, LEncoding, LData, LDataEnd: Integer; + begin + Result := False; + + //we need a '=? followed by 2 question marks followed by a '?='. {Do not Localize} + //to find the end of the substring, we can't just search for '?=', {Do not Localize} + //example: '=?ISO-8859-1?Q?=E4?=' {Do not Localize} + + LCharSet := PosIdx('=?', AHeader, AStartPos); {Do not Localize} + if (LCharSet = 0) or (LCharSet > VEndPos) then begin + Exit; + end; + Inc(LCharSet, 2); + + LEncoding := PosIdx('?', AHeader, LCharSet); {Do not Localize} + if (LEncoding = 0) or (LEncoding > VEndPos) then begin + Exit; + end; + Inc(LEncoding); + + LData := PosIdx('?', AHeader, LEncoding); {Do not Localize} + if (LData = 0) or (LData > VEndPos) then begin + Exit; + end; + Inc(LData); + + LDataEnd := PosIdx('?=', AHeader, LData); {Do not Localize} + if (LDataEnd = 0) or (LDataEnd > VEndPos) then begin + Exit; + end; + Inc(LDataEnd); + + VStartPos := LCharSet-2; + VEndPos := LDataEnd; + VCharSet := Copy(AHeader, LCharSet, LEncoding-LCharSet-1); + VEncoding := Copy(AHeader, LEncoding, LData-LEncoding-1); + VData := Copy(AHeader, LData, LDataEnd-LData-1); + + Result := True; + end; + + // TODO: use TIdCoderQuotedPrintable and TIdCoderMIME instead + function ExtractEncodedData(const AEncoding, AData: String; var VDecoded: TIdBytes): Boolean; + var + I, J: Integer; + a3: TIdBytes; + a4: array [0..3] of Byte; + begin + Result := False; + SetLength(VDecoded, 0); + case PosInStrArray(AEncoding, ['Q', 'B', '8'], False) of {Do not Localize} + 0: begin // quoted-printable + I := 1; + while I <= Length(AData) do begin + if AData[i] = '_' then begin {Do not Localize} + AppendByte(VDecoded, Ord(' ')); {Do not Localize} + end + else if (AData[i] = '=') and (Length(AData) >= (i+2)) then begin //make sure we can access i+2 + AppendByte(VDecoded, IndyStrToInt('$' + Copy(AData, i+1, 2), 32)); {Do not Localize} + Inc(I, 2); + end else + begin + AppendByte(VDecoded, Ord(AData[i])); + end; + Inc(I); + end; + Result := True; + end; + 1: begin // base64 + J := Length(AData) div 4; + if J > 0 then + begin + SetLength(a3, 3); + for I := 0 to J-1 do + begin + a4[0] := B64(AData[(I*4)+1]); + a4[1] := B64(AData[(I*4)+2]); + a4[2] := B64(AData[(I*4)+3]); + a4[3] := B64(AData[(I*4)+4]); + + a3[0] := Byte((a4[0] shl 2) or (a4[1] shr 4)); + a3[1] := Byte((a4[1] shl 4) or (a4[2] shr 2)); + a3[2] := Byte((a4[2] shl 6) or (a4[3] shr 0)); + + if AData[(I*4)+4] = '=' then begin + if AData[(I*4)+3] = '=' then begin + AppendByte(VDecoded, a3[0]); + end else begin + AppendBytes(VDecoded, a3, 0, 2); + end; + Break; + end else begin + AppendBytes(VDecoded, a3, 0, 3); + end; + end; + end; + Result := True; + end; + 2: begin // 8-bit + {$IFDEF STRING_IS_ANSI} + if AData <> '' then begin + VDecoded := RawToBytes(AData[1], Length(AData)); + end; + {$ELSE} + VDecoded := IndyTextEncoding_8Bit.GetBytes(AData); + {$ENDIF} + Result := True; + end; + end; + end; + +begin + Result := Header; + + LStartPos := 1; + LLength := Length(Result); + + LLastWordWasEncoded := False; + LLastStartPos := LStartPos; + + while LStartPos <= LLength do + begin + // valid encoded words can not contain spaces + // if the user types something *almost* like an encoded word, + // and its sent as-is, we need to find this!! + LStartPos := FindFirstNotOf(LWS, Result, LLength, LStartPos); + if LStartPos = 0 then begin + Break; + end; + LEncodingEndPos := FindFirstOf(LWS, Result, LLength, LStartPos); + if LEncodingEndPos <> 0 then begin + Dec(LEncodingEndPos); + end else begin + LEncodingEndPos := LLength; + end; + if ExtractEncoding(Result, LStartPos, LEncodingStartPos, LEncodingEndPos, HeaderCharSet, HeaderEncoding, HeaderData) then + begin + LDecoded := False; + if ExtractEncodedData(HeaderEncoding, HeaderData, Buf) then begin + LDecoded := DecodeHeaderData(HeaderCharSet, Buf, S); + end; + if LDecoded then + begin + //replace old substring in header with decoded string, + // ignoring whitespace that separates encoded words: + if LLastWordWasEncoded then begin + Result := Copy(Result, 1, LLastStartPos - 1) + S + Copy(Result, LEncodingEndPos + 1, MaxInt); + LStartPos := LLastStartPos + Length(S); + end else begin + Result := Copy(Result, 1, LEncodingStartPos - 1) + S + Copy(Result, LEncodingEndPos + 1, MaxInt); + LStartPos := LEncodingStartPos + Length(S); + end; + end else + begin + // could not decode the data, so preserve it in case the user + // wants to do it manually. Though, they really should use the + // IdHeaderCoderBase.GHeaderDecodingNeeded hook for that instead... + LStartPos := LEncodingEndPos + 1; + end; + LLength := Length(Result); + LLastWordWasEncoded := True; + LLastStartPos := LStartPos; + end else + begin + LStartPos := FindFirstOf(LWS, Result, LLength, LStartPos); + if LStartPos = 0 then begin + Break; + end; + LLastWordWasEncoded := False; + end; + end; +end; + +procedure DecodeAddress(EMailAddr : TIdEmailAddressItem); +begin + EMailAddr.Name := UnquotedStr(DecodeHeader(EMailAddr.Name)); +end; + +procedure DecodeAddresses(AEMails : String; EMailAddr: TIdEmailAddressList); +var + idx : Integer; +begin + EMailAddr.EMailAddresses := AEMails; + for idx := 0 to EMailAddr.Count-1 do begin + DecodeAddress(EMailAddr[idx]); + end; +end; + +function EncodeAddress(EmailAddr: TIdEMailAddressList; const HeaderEncoding: Char; + const MimeCharSet: string; AUseAddressForNameIfNameMissing: Boolean = False): string; +var + idx : Integer; +begin + if EmailAddr.Count > 0 then begin + Result := EncodeAddressItem(EMailAddr[0], HeaderEncoding, MimeCharSet, AUseAddressForNameIfNameMissing); + for idx := 1 to EmailAddr.Count-1 do begin + Result := Result + ', ' + {Do not Localize} + EncodeAddressItem(EMailAddr[idx], HeaderEncoding, MimeCharSet, AUseAddressForNameIfNameMissing); + end; + end else begin + Result := ''; {Do not Localize} + end; +end; + +{ encode a header field if non-ASCII characters are used } +function EncodeHeader(const Header: string; Specials: String; const HeaderEncoding: Char; + const MimeCharSet: string): string; +const + SPACES = [Ord(' '), 9, 13, 10]; {Do not Localize} +var + T: string; + Buf: TIdBytes; + L, P, Q, R: Integer; + B0, B1, B2: Integer; + InEncode: Integer; + NeedEncode: Boolean; + csNoEncode, csNoReqQuote, csSpecials: TIdBytes; + BeginEncode, EndEncode: string; + + procedure EncodeWord(AP: Integer); + const + MaxEncLen = 75; + var + LQ: Integer; + EncLen: Integer; + Enc1: string; + begin + T := T + BeginEncode; + if L < AP then AP := L + 1; + LQ := InEncode; + InEncode := -1; + EncLen := Length(BeginEncode) + 2; + + case PosInStrArray(HeaderEncoding, ['Q', 'B'], False) of {Do not Localize} + 0: begin { quoted-printable } + while LQ < AP do + begin + if Buf[LQ] = Ord(' ') then begin {Do not Localize} + Enc1 := '_'; {Do not Localize} + end + else if (not ByteIsInSet(Buf, LQ, csNoReqQuote)) or ByteIsInSet(Buf, LQ, csSpecials) then begin + Enc1 := '=' + IntToHex(Buf[LQ], 2); {Do not Localize} + end + else begin + Enc1 := Char(Buf[LQ]); + end; + if (EncLen + Length(Enc1)) > MaxEncLen then begin + //T := T + EndEncode + #13#10#9 + BeginEncode; + //CC: The #13#10#9 above caused the subsequent call to FoldWrapText to + //insert an extra #13#10 which, being a blank line in the headers, + //was interpreted by email clients, etc., as the end of the headers + //and the start of the message body. FoldWrapText seems to look for + //and treat correctly the sequence #13#10 + ' ' however... + T := T + EndEncode + EOL + ' ' + BeginEncode; + EncLen := Length(BeginEncode) + 2; + end; + T := T + Enc1; + Inc(EncLen, Length(Enc1)); + Inc(LQ); + end; + end; + 1: begin { base64 } + while LQ < AP do begin + if (EncLen + 4) > MaxEncLen then begin + //T := T + EndEncode + #13#10#9 + BeginEncode; + //CC: The #13#10#9 above caused the subsequent call to FoldWrapText to + //insert an extra #13#10 which, being a blank line in the headers, + //was interpreted by email clients, etc., as the end of the headers + //and the start of the message body. FoldWrapText seems to look for + //and treat correctly the sequence #13#10 + ' ' however... + T := T + EndEncode + EOL + ' ' + BeginEncode; + EncLen := Length(BeginEncode) + 2; + end; + + B0 := Buf[LQ]; + case AP - LQ of + 1: + begin + T := T + base64_tbl[B0 shr 2] + base64_tbl[B0 and $03 shl 4] + '=='; {Do not Localize} + end; + 2: + begin + B1 := Buf[LQ + 1]; + T := T + base64_tbl[B0 shr 2] + + base64_tbl[B0 and $03 shl 4 + B1 shr 4] + + base64_tbl[B1 and $0F shl 2] + '='; {Do not Localize} + end; + else + begin + B1 := Buf[LQ + 1]; + B2 := Buf[LQ + 2]; + T := T + base64_tbl[B0 shr 2] + + base64_tbl[B0 and $03 shl 4 + B1 shr 4] + + base64_tbl[B1 and $0F shl 2 + B2 shr 6] + + base64_tbl[B2 and $3F]; + end; + end; + Inc(EncLen, 4); + Inc(LQ, 3); + end; + end; + end; + T := T + EndEncode; + end; + + function CreateEncodeRange(AStart, AEnd: Byte): TIdBytes; + var + I: Integer; + begin + SetLength(Result, AEnd-AStart+1); + for I := 0 to Length(Result)-1 do begin + Result[I] := AStart+I; + end; + end; + +begin + if Header = '' then begin + Result := ''; + Exit; + end; + + // TODO: this function needs to take encoded codeunits into account when + // deciding where to split the encoded data between adjacent encoded-words, + // so that a single encoded character does not get split between encoded-words + // thus corrupting that character... + + Buf := EncodeHeaderData(MimeCharSet, Header); + + {Suggested by Andrew P.Rybin for easy 8bit support} + if HeaderEncoding = '8' then begin {Do not Localize} + Result := BytesToStringRaw(Buf); + Exit; + end;//if + + // 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... + + // RLebeau 2/12/09: changed the logic to use "no-encode" sets instead, so + // that words containing codeunits outside the ASCII range are always + // encoded. This is easier to manage when Unicode data is involved. + + csNoEncode := CreateEncodeRange(32, 126); + + csNoReqQuote := CreateEncodeRange(33, 60); + AppendByte(csNoReqQuote, 62); + AppendBytes(csNoReqQuote, CreateEncodeRange(64, 94)); + AppendBytes(csNoReqQuote, CreateEncodeRange(96, 126)); + + csSpecials := ToBytes(Specials, IndyTextEncoding_8Bit); + + BeginEncode := '=?' + MimeCharSet + '?' + HeaderEncoding + '?'; {Do not Localize} + EndEncode := '?='; {Do not Localize} + + // JMBERG: We want to encode stuff that the user typed + // as if it already is encoded!! + if DecodeHeader(Header) <> Header then begin + RemoveBytes(csNoEncode, 1, ByteIndex(Ord('='), csNoEncode)); + end; + + L := Length(Buf); + P := 0; + T := ''; {Do not Localize} + InEncode := -1; + while P < L do + begin + Q := P; + while (P < L) and (Buf[P] in SPACES) do begin + Inc(P); + end; + R := P; + NeedEncode := False; + while (P < L) and (not (Buf[P] in SPACES)) do begin + if (not ByteIsInSet(Buf, P, csNoEncode)) or ByteIsInSet(Buf, P, csSpecials) then begin + NeedEncode := True; + end; + Inc(P); + end; + if NeedEncode then begin + if InEncode = -1 then begin + T := T + BytesToString(Buf, Q, R - Q); + InEncode := R; + end; + end else + begin + if InEncode <> -1 then begin + EncodeWord(Q); + end; + T := T + BytesToString(Buf, Q, P - Q); + end; + end; + if InEncode <> -1 then begin + EncodeWord(P); + end; + Result := T; +end; + +end. diff --git a/indy/Protocols/IdCoderMIME.pas b/indy/Protocols/IdCoderMIME.pas new file mode 100644 index 0000000..eb63d64 --- /dev/null +++ b/indy/Protocols/IdCoderMIME.pas @@ -0,0 +1,187 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 26/03/2005 19:19:30 CCostelloe + Fixes for "uneven size" exception + + Rev 1.2 2004.01.21 1:04:54 PM czhower + InitComponenet + + Rev 1.1 10/6/2003 5:37:02 PM SGrobety + Bug fix in decoders. + + Rev 1.0 11/14/2002 02:14:54 PM JPMugaas +} + +unit IdCoderMIME; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdCoder3to4, + IdGlobal; + +type + TIdEncoderMIME = class(TIdEncoder3to4) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + + TIdDecoderMIME = class(TIdDecoder4to3) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + + {WARNING: This is not a general-purpose decoder. It is used, for example, by + IdMessageCoderMIME for line-by-line decoding of base64 encoded parts that are + processed on a line-by-line basis, as against the complete encoded block.} + TIdDecoderMIMELineByLine = class(TIdDecoderMIME) + protected + FLeftFromLastTime: TIdBytes; + public + procedure DecodeBegin(ADestStream: TStream); override; + procedure DecodeEnd; override; + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override; + end; + +const + GBase64CodeTable: string = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; {Do not Localize} + +var + GBase64DecodeTable: TIdDecodeTable; + +implementation + +uses + {$IFDEF DOTNET} + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + SysUtils; + +{ TIdDecoderMIMELineByLine } + +procedure TIdDecoderMIMELineByLine.DecodeBegin(ADestStream: TStream); +begin + inherited DecodeBegin(ADestStream); + {Clear out any bytes that may be left from a previous decode...} + SetLength(FLeftFromLastTime, 0); +end; + +procedure TIdDecoderMIMELineByLine.DecodeEnd; +var + LStream: TMemoryStream; + LPos: Integer; +begin + if Length(FLeftFromLastTime) > 0 then begin + LPos := Length(FLeftFromLastTime); + SetLength(FLeftFromLastTime, 4); + while LPos < 4 do begin + FLeftFromLastTime[LPos] := Ord(FFillChar); + Inc(LPos); + end; + LStream := TMemoryStream.Create; + try + WriteTIdBytesToStream(LStream, FLeftFromLastTime); + LStream.Position := 0; + inherited Decode(LStream); + finally + FreeAndNil(LStream); + SetLength(FLeftFromLastTime, 0); + end; + end; + inherited DecodeEnd; +end; + +procedure TIdDecoderMIMELineByLine.Decode(ASrcStream: TStream; const ABytes: Integer = -1); +var + LMod, LDiv: integer; + LIn, LSrc: TIdBytes; + LStream: TMemoryStream; +begin + LIn := FLeftFromLastTime; + if ReadTIdBytesFromStream(ASrcStream, LSrc, ABytes) > 0 then begin + AppendBytes(LIn, LSrc); + end; + LMod := Length(LIn) mod 4; + if LMod <> 0 then begin + LDiv := (Length(LIn) div 4) * 4; + FLeftFromLastTime := Copy(LIn, LDiv, Length(LIn)-LDiv); + LIn := Copy(LIn, 0, LDiv); + end else begin + SetLength(FLeftFromLastTime, 0); + end; + LStream := TMemoryStream.Create; + try + WriteTIdBytesToStream(LStream, LIn); + LStream.Position := 0; + inherited Decode(LStream, ABytes); + finally + FreeAndNil(LStream); + end; +end; + +{ TIdDecoderMIME } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDecoderMIME.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDecoderMIME.InitComponent; +begin + inherited InitComponent; + FDecodeTable := GBase64DecodeTable; + FCodingTable := ToBytes(GBase64CodeTable); + FFillChar := '='; {Do not Localize} +end; + +{ TIdEncoderMIME } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdEncoderMIME.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdEncoderMIME.InitComponent; +begin + inherited InitComponent; + FCodingTable := ToBytes(GBase64CodeTable); + FFillChar := '='; {Do not Localize} +end; + +initialization + TIdDecoder4to3.ConstructDecodeTable(GBase64CodeTable, GBase64DecodeTable); +end. diff --git a/indy/Protocols/IdCoderQuotedPrintable.pas b/indy/Protocols/IdCoderQuotedPrintable.pas new file mode 100644 index 0000000..8badccc --- /dev/null +++ b/indy/Protocols/IdCoderQuotedPrintable.pas @@ -0,0 +1,317 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.20 10/26/2004 11:08:10 PM JPMugaas + Updated refs. + + Rev 1.19 27.08.2004 22:03:22 Andreas Hausladen + Optimized encoders + speed optimization ("const" for string parameters) + + Rev 1.18 24/08/2004 10:33:44 CCostelloe + Was too slow (~45 mins for 2MB down to ~1sec) + + Rev 1.17 2004.05.20 1:39:22 PM czhower + Last of the IdStream updates + + Rev 1.16 2004.05.20 11:37:10 AM czhower + IdStreamVCL + + Rev 1.15 2004.05.20 11:13:14 AM czhower + More IdStream conversions + + Rev 1.14 2004.05.19 3:06:54 PM czhower + IdStream / .NET fix + + Rev 1.13 2/19/2004 11:52:02 PM JPMugaas + Removed IFDEF's. Moved some functions into IdGlobalProtocols for reuse + elsewhere. + + Rev 1.12 2004.02.03 5:45:00 PM czhower + Name changes + + Rev 1.11 2004.02.03 2:12:06 PM czhower + $I path change + + Rev 1.10 1/22/2004 3:59:14 PM SPerry + fixed set problems + + Rev 1.9 11/10/2003 7:41:30 PM BGooijen + Did all todo's ( TStream to TIdStream mainly ) + + Rev 1.8 2003.10.17 6:14:44 PM czhower + Fix to match new IdStream + + Rev 1.7 2003.10.12 3:38:26 PM czhower + Added path to .inc + + Rev 1.6 10/12/2003 1:33:42 PM BGooijen + Compiles on D7 now too + + Rev 1.5 10/12/2003 12:02:50 PM BGooijen + DotNet + + Rev 1.4 6/13/2003 12:07:44 PM JPMugaas + QP was broken again. + + Rev 1.3 6/13/2003 07:58:50 AM JPMugaas + Should now compile with new decoder design. + + Rev 1.2 6/13/2003 06:17:06 AM JPMugaas + Should now compil,e. + + Rev 1.1 12.6.2003 . 12:00:28 DBondzhev + Fix for . at the begining of new line + + Rev 1.0 11/14/2002 02:15:00 PM JPMugaas + + 2002-08-13/14 - Johannes Berg + completely rewrote the Encoder. May do the Decoder later. + The encoder will add an EOL to the end of the file if it had no EOL + at start. I can't avoid this due to the design of IdStream.ReadLn, + but its also no problem, because in transmission this would happen + anyway. + + 9-17-2001 - J. Peter Mugaas + made the interpretation of =20 + EOL to mean a hard line break + soft line breaks are now ignored. It does not make much sense + in plain text. Soft breaks do not indicate the end of paragraphs unlike + hard line breaks that do end paragraphs. + + 3-24-2001 - J. Peter Mugaas + Rewrote the Decoder according to a new design. + + 3-25-2001 - J. Peter Mugaas + Rewrote the Encoder according to the new design +} + +unit IdCoderQuotedPrintable; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdCoder, + IdStream, + SysUtils; + +type + TIdDecoderQuotedPrintable = class(TIdDecoder) + public + procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override; + end; + + TIdEncoderQuotedPrintable = class(TIdEncoder) + public + procedure Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); override; + end; + +implementation + +uses + IdException, + IdGlobal, + IdGlobalProtocols; + +{ TIdDecoderQuotedPrintable } + +procedure TIdDecoderQuotedPrintable.Decode(ASrcStream: TStream; const ABytes: Integer = -1); +var + LBuffer: TIdBytes; + i : Integer; + B, DecodedByte : Byte; + LBufferLen: Integer; + LBufferIndex: Integer; + LPos: integer; + + procedure StripEOLChars; + var + j: Integer; + begin + for j := 1 to 2 do begin + if (LBufferIndex >= LBufferLen) or (not ByteIsInEOL(LBuffer, LBufferIndex)) then begin + Break; + end; + Inc(LBufferIndex); + end; + end; + + function TrimRightWhiteSpace(const ABuf: TIdBytes): TIdBytes; + var + LSaveBytes: TIdBytes; + li, LLen: Integer; + begin + SetLength(LSaveBytes, 0); + LLen := Length(ABuf); + for li := Length(ABuf)-1 downto 0 do begin + case ABuf[li] of + 9, 32: ; + 10, 13: + begin + //BGO: TODO: Change this + InsertByte(LSaveBytes, ABuf[li], 0); + end; + else + begin + Break; + end; + end; + Dec(LLen); + end; + SetLength(Result, LLen + Length(LSaveBytes)); + if LLen > 0 then begin + CopyTIdBytes(ABuf, 0, Result, 0, LLen); + end; + if Length(LSaveBytes) > 0 then begin + CopyTIdBytes(LSaveBytes, 0, Result, LLen, Length(LSaveBytes)); + end; + end; + + procedure WriteByte(AValue: Byte; AWriteEOL: Boolean); + var + LTemp: TIdBytes; + begin + SetLength(LTemp, iif(AWriteEOL, 3, 1)); + LTemp[0] := AValue; + if AWriteEOL then begin + LTemp[1] := Ord(CR); + LTemp[2] := Ord(LF); + end; + TIdStreamHelper.Write(FStream, LTemp); + end; + +begin + LBufferLen := IndyLength(ASrcStream, ABytes); + if LBufferLen <= 0 then begin + Exit; + end; + SetLength(LBuffer, LBufferLen); + TIdStreamHelper.ReadBytes(ASrcStream, LBuffer, LBufferLen); + { when decoding a Quoted-Printable body, any trailing + white space on a line must be deleted, - RFC 1521} + LBuffer := TrimRightWhiteSpace(LBuffer); + LBufferLen := Length(LBuffer); + LBufferIndex := 0; + while LBufferIndex < LBufferLen do begin + LPos := ByteIndex(Ord('='), LBuffer, LBufferIndex); + if LPos = -1 then begin + if Assigned(FStream) then begin + TIdStreamHelper.Write(FStream, LBuffer, -1, LBufferIndex); + end; + Break; + end; + if Assigned(FStream) then begin + TIdStreamHelper.Write(FStream, LBuffer, LPos-LBufferIndex, LBufferIndex); + end; + LBufferIndex := LPos+1; + // process any following hexidecimal representation + if LBufferIndex < LBufferLen then begin + i := 0; + DecodedByte := 0; + while LBufferIndex < LBufferLen do begin + B := LBuffer[LBufferIndex]; + case B of + 48..57: //0-9 {Do not Localize} + DecodedByte := (DecodedByte shl 4) or (B - 48); {Do not Localize} + 65..70: //A-F {Do not Localize} + DecodedByte := (DecodedByte shl 4) or (B - 65 + 10); {Do not Localize} + 97..102://a-f {Do not Localize} + DecodedByte := (DecodedByte shl 4) or (B - 97 + 10); {Do not Localize} + else + Break; + end; + Inc(i); + Inc(LBufferIndex); + if i > 1 then begin + Break; + end; + end; + if i > 0 then begin + //if =20 + EOL, this is a hard line break after a space + if (DecodedByte = 32) and (LBufferIndex < LBufferLen) and ByteIsInEOL(LBuffer, LBufferIndex) then begin + if Assigned(FStream) then begin + WriteByte(DecodedByte, True); + end; + StripEOLChars; + end else begin + if Assigned(FStream) then begin + WriteByte(DecodedByte, False); + end; + end; + end else begin + //ignore soft line breaks - + StripEOLChars; + end; + end; + end; +end; + +{ TIdEncoderQuotedPrintable } + +function CharToHex(const AChar: Char): String; +begin + Result := '=' + ByteToHex(Ord(AChar)); {do not localize} +end; + +procedure TIdEncoderQuotedPrintable.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); +const + SafeChars = '!"#$%&''()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmonpqrstuvwxyz{|}~'; + HalfSafeChars = #9' '; + // Rule #2, #3 +var + I, CurrentLen: Integer; + LSourceSize: TIdStreamSize; + S, SourceLine: String; + LEncoding: IIdTextEncoding; +begin + //ie while not eof + LSourceSize := ASrcStream.Size; + if ASrcStream.Position < LSourceSize then begin + LEncoding := IndyTextEncoding_8Bit; + repeat + SourceLine := ReadLnFromStream(ASrcStream, -1, False, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + CurrentLen := 0; + for I := 1 to Length(SourceLine) do begin + if not CharIsInSet(SourceLine, I, SafeChars) then + begin + if CharIsInSet(SourceLine, I, HalfSafeChars) and (I < Length(SourceLine)) then begin + S := SourceLine[I]; + end else begin + S := CharToHex(SourceLine[I]); + end; + end + else if ((CurrentLen = 0) or (CurrentLen >= 70)) and (SourceLine[I] = '.') then begin {do not localize} + S := CharToHex(SourceLine[I]); + end else begin + S := SourceLine[I]; + end; + WriteStringToStream(ADestStream, S); + Inc(CurrentLen, Length(S)); + if CurrentLen >= 70 then begin + WriteStringToStream(ADestStream, '='+EOL); {Do not Localize} + CurrentLen := 0; + end; + end; + WriteStringToStream(ADestStream, EOL); + until ASrcStream.Position >= LSourceSize; + end; +end; + +end. diff --git a/indy/Protocols/IdCoderTNEF.pas b/indy/Protocols/IdCoderTNEF.pas new file mode 100644 index 0000000..ffe4b17 --- /dev/null +++ b/indy/Protocols/IdCoderTNEF.pas @@ -0,0 +1,2810 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ +{ Rev 1.3 16/01/2005 22:33:06 CCostelloe +{ Minor update +} +{ +{ Rev 1.2 22/12/2004 23:09:52 CCostelloe +{ Another intermediate check-in, this sorts out the compressed RTF message and +{ (if it has one) the message .Body. +} +{ +{ Rev 1.1 18/12/2004 20:35:24 CCostelloe +{ Intermediate check-in, lots more to be done. +} +{ +{ Rev 1.0 02/12/2004 22:56:26 CCostelloe +{ Initial version +} +unit IdCoderTNEF; + +{ + This is for decoding Microsoft's TNEF email messages, which are usually + transmitted as an attachment called either "WINMAIL.DAT" or "ATT00001.DAT" + in a MIME-encoded message (though other variants of the file name, but not + the extension, are occasionally encountered). + The MIME type should be "application/ms-tnef". + This is deliberately NOT similar to the other coder classes, this + should usually be used by passing in a TNEF TIdAttachment which has + been extracted from a message. + You can optionally get a debugging log of the parsing by setting the third + optional parameter to True, as in "TIdCoderTNEF.Parse(A_TIdBytes, + A_TIdMessage, True);", and then load the log into a TMemo by calling + "Memo1.Text := A_TIdCoderTNEF.Log;". + The aim of the initial implementation was just to extract any attachments from + the TNEF, along with their filenames, and put them in the TIdMessage's + MessageParts for the user. + Some additional applicable fields have also been parsed out of the TNEF + and inserted into the TIdMessage. This is an ongoing development. + METHODOLOGY: + A TNEF is a collection of blocks of data, each block is either a + IdTNEFLvlMessage or a IdTNEFLvlAttachment block. IdTNEFLvlAttachment + blocks are straightforward, see the parsing below. + A IdTNEFLvlMessage block may contain an item like the message subject, + date sent, etc. However, a block may also contain a IdTNEFattMAPIProps, + which contains sub-blocks corresponding to MAPI properties which will + also typically include the message subject, date sent, etc. + The methodology used by me is that the IdTNEFLvlMessage level takes + priority, i.e. if there is a message subject at both the IdTNEFLvlMessage + and the IdTNEFattMAPIProps level, then the IdTNEFLvlMessage is the one + that will be used. + The TIdMessage is filled in from the TNEF in a similar manner to the way + Indy decodes MIME messages. Particularly note that the message text + is often only in an RTF format and so it will be put in a TIdText part + and NOT in the TIdMessage.Body. The TIdMessage.Body MAY contain the + plain-text version of the message, IF it is present. + Parts (either TIdText or TIdAttachmentFile) are added to the TIdMessage + in the order they are encountered. This may be different from an + RFC-compliant MIME email, which (in the case of alternative versions + of the message text) puts the simplest first, i.e. text/plain + before text/html before text/rtf. + +To do: + When finished decoding, rescan the parts and insert parts like + 'multipart/alternative' and set up the ParentPart pointers as required to + imitate Indy's treatment of MIME parts in Indy10. + For LLong, LShort, get them to get signed values. + GetMapiSysTime or GetTime may be out by an hour? Time zone issue? + Add meaningful headers like ContentType to message parts. + Some TIdMessage fields may be found in different TNEF fields (e.g. sender/from). + They don't appear to be consistent in practice, and there may be semantical + differences between the usage of terms like Sender between TNEF and MIME. + See the "TODO"s in the code below. +} + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdMessage, + IdException, + IdAttachment, + IdAttachmentFile, + SysUtils; + +type + TIdCoderTNEF = class(TObject) + private + FData: TStream; //Used for walking through the file + FKey: Word; //Every TNEF has one, but no-one seems to know why! + FLog: string; //The (optional) debugging log goes here + FDoLogging: Boolean; //Should we be doing the optional logging? + FMsg: TIdMessage; //The destination for our extracted attachments + FCurrentAttachment: TIdAttachment; //Attachment we are currently decoding into + FReceiptRequested: Boolean; //Need to cache this because receipt flag may precede sender address + // + procedure DoLog(const AMsg: String; const AAppendSize: Boolean = True); + procedure DoLogFmt(const AFormat: string; const Args: array of const; AAppendSize: Boolean = True); + //Low-level utility functions: + function GetMultipleUnicodeOrString8String(AType: Word): TIdUnicodeString; + function GetUnicodeOrString8String(AType: Word): TIdUnicodeString; + function GetByte: Byte; + function GetBytes(ALength: Integer; APeek: Boolean = False): TIdBytes; + function GetByteAsHexString: string; overload; + function GetByteAsHexString(AByte: Byte): string; overload; + function GetByteAsChar(AByte: Byte): char; + function GetBytesAsHexString(ACount: integer): string; + function GetWord: Word; + function GetLongWord: LongWord; + function GetInt64: Int64; + function GetString(ALength: Word): string; + function GetDate(ALength: Word): TDateTime; + procedure Skip(ACount: integer); + procedure CheckForEof(ANumBytesRequested: integer); + procedure Checksum(ANumBytesToCheck: integer); + function PadWithZeroes(const AStr: string; ACount: integer): string; + procedure DumpBytes(const ABytes: TIdBytes); + //Attribute-specific stuff... + function GetAttributeString(const AAttributeName: string; AType: Word): string; + //MAPI parsing... + function GetStringForMapiType(AType: Word): string; + function GetMapiBoolean(AType: Word; const AText: string): Smallint; + function GetMapiLong(AType: Word; const AText: string): Longint; + function GetMapiStrings(AType: Word; const AText: string): string; + function GetMapiBinary(AType: Word; const AText: string): TIdBytes; + function GetMapiBinaryAsEmailName(AType: Word; const AText: string): string; + function GetMapiBinaryAsString(AType: Word; const AText: string): string; + //function GetMapiObject(AType: Word; AText: string): TIdBytes; + function GetMapiItemAsBytes(AType: Word; const AText: string): TIdBytes; + function GetMapiItemAsBytesPossiblyCompressed(AType: Word; const AText: string): TIdBytes; + function DecompressRtf(ACount, ALength: LongWord; AType: Word; const AText: string): TIdBytes; + function GetMapiSysTime(AType: Word; const AText: string): TDateTime; + function InternalGetMapiItemAsBytes(ACount, ALength: LongWord; AType: Word; const AText: string): TIdBytes; + protected + procedure ParseMessageBlock; + procedure ParseAttachmentBlock; + procedure ParseAttribute(AAttribute, AType: Word); + procedure ParseMapiProps(ALength: LongWord); + procedure ParseMapiProp; + procedure IsCurrentAttachmentValid; + //For debugging log... + function GetStringForAttribute(AAttribute: Word): string; + function GetStringForType(AType: Word): string; + public + //The following is the normal parser call you would use... + procedure Parse(const AIn: TIdAttachment; AMsg: TIdMessage; ALog: Boolean = False); overload; + //TIdIMAP4 should set up a stream & use this... + procedure Parse(const AIn: TStream; AMsg: TIdMessage; ALog: Boolean = False); overload; + //The TIdBytes and string versions are really for debugging... + procedure Parse(const AIn: TIdBytes; AMsg: TIdMessage; ALog: Boolean = False); overload; + procedure Parse(const AIn: string; AMsg: TIdMessage; ALog: Boolean = False); overload; + //Tells you if a filename matches TNEF semantics (winmail.dat, att0001.dat) + class function IsFilenameTnef(const AFilename: string): Boolean; static; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use standalone IsFilenameTnef() function'{$ENDIF};{$ENDIF} + property Key: Word read FKey; //TODO: Does this have a meaningful use? + property Log: string read FLog; + end; + + EIdTnefInvalidTNEFSignature = class(EIdException); + EIdTnefRanOutOfBytes = class(EIdException); + EIdTnefUnknownBlockType = class(EIdException); + EIdTnefChecksumFailure = class(EIdException); + EIdTnefCurrentAttachmentInvalid = class(EIdException); + EIdTnefAttributeUnexpectedType = class(EIdException); + EIdTnefUnexpectedType = class(EIdException); + EIdTnefUnexpectedValue = class(EIdException); + EIdTnefNotSupported = class(EIdException); + EIdTnefUnknownMapiType = class(EIdException); + EIdTnefCorruptData = class(EIdException); + +function IsFilenameTnef(const AFilename: string): Boolean; + +implementation + +uses + DateUtils, IdMessageClient, IdText, IdStream; + +const + //Initial RTF-compression decode string... + IdTNEF_decode_string: string = '{\rtf1\ansi\mac\deff0\deftab720{\fonttbl;}{\f0\fnil \froman \fswiss \fmodern \fscript \fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\colortbl\red0\green0\blue0'+#10#13+'\par \pard\plain\f0\fs20\b\i\u\tab\tx'; {Do not localize} + + //The following are equivalents to those defined Microsoft's TNEF.H. + //File signature... + IdTNEFSignature = $223E9F78; //Every TNEF should have this + + //Highest-level block types... + IdTNEFLvlMessage = 1; //Corresponds to Microsoft's LVL_MESSAGE + IdTNEFLvlAttachment = 2; //Corresponds to Microsoft's LVL_ATTACHMENT + + //Data types... + IdTNEFAtpTriples = 0; //Corresponds to Microsoft's AtpTriples, similarly for the following + IdTNEFAtpString = 1; + IdTNEFAtpText = 2; + IdTNEFAtpDate = 3; + IdTNEFAtpShort = 4; + IdTNEFAtpLong = 5; + IdTNEFAtpByte = 6; + IdTNEFAtpWord = 7; + IdTNEFAtpDWord = 8; + IdTNEFAtpMax = 9; + + //Attribute types... + IdTNEFattNull = $0000; + IdTNEFattFrom = $8000; // /* PR_ORIGINATOR_RETURN_ADDRESS */ + IdTNEFattSubject = $8004; // /* PR_SUBJECT */ + IdTNEFattDateSent = $8005; // /* PR_CLIENT_SUBMIT_TIME */ + IdTNEFattDateRecd = $8006; // /* PR_MESSAGE_DELIVERY_TIME */ + IdTNEFattMessageStatus = $8007; // /* PR_MESSAGE_FLAGS */ + IdTNEFattMessageClass = $8008; // /* PR_MESSAGE_CLASS */ + IdTNEFattMessageID = $8009; // /* PR_MESSAGE_ID */ + IdTNEFattParentID = $800A; // /* PR_PARENT_ID */ + IdTNEFattConversationID = $800B; // /* PR_CONVERSATION_ID */ + IdTNEFattBody = $800C; // /* PR_BODY */ + IdTNEFattPriority = $800D; // /* PR_IMPORTANCE */ + IdTNEFattAttachData = $800F; // /* PR_ATTACH_DATA_xxx */ + IdTNEFattAttachTitle = $8010; // /* PR_ATTACH_FILENAME */ + IdTNEFattAttachMetaFile = $8011; // /* PR_ATTACH_RENDERING */ + IdTNEFattAttachCreateDate = $8012; // /* PR_CREATION_TIME */ + IdTNEFattAttachModifyDate = $8013; // /* PR_LAST_MODIFICATION_TIME */ + IdTNEFattDateModified = $8020; // /* PR_LAST_MODIFICATION_TIME */ + IdTNEFattAttachTransportFilename = $9001; // /* PR_ATTACH_TRANSPORT_NAME */ + IdTNEFattAttachRenddata = $9002; // + IdTNEFattMAPIProps = $9003; // + IdTNEFattRecipTable = $9004; // /* PR_MESSAGE_RECIPIENTS */ + IdTNEFattAttachment = $9005; // + IdTNEFattTnefVersion = $9006; // + IdTNEFattOemCodepage = $9007; // + IdTNEFattOriginalMessageClass = $0006; // /* PR_ORIG_MESSAGE_CLASS */ + + IdTNEFattOwner = $0000; // /* PR_RCVD_REPRESENTING_xxx or PR_SENT_REPRESENTING_xxx */ + IdTNEFattSentFor = $0001; // /* PR_SENT_REPRESENTING_xxx */ + IdTNEFattDelegate = $0002; // /* PR_RCVD_REPRESENTING_xxx */ + IdTNEFattDateStart = $0006; // /* PR_DATE_START */ + IdTNEFattDateEnd = $0007; // /* PR_DATE_END */ + IdTNEFattAidOwner = $0008; // /* PR_OWNER_APPT_ID */ + IdTNEFattRequestRes = $0009; // /* PR_RESPONSE_REQUESTED */ + + //Message priorities... + IdTNEFprioLow = 3; + IdTNEFprioNorm = 2; + IdTNEFprioHigh = 1; + + //MAPI property value types... + IdTNEF_PT_UNSPECIFIED = 0; //TODO: * (Reserved for interface use) type doesn't matter to caller */ + IdTNEF_PT_NULL = 1; //TODO: * NULL property value */ + IdTNEF_PT_I2 = 2; //* Signed 16-bit value */ + IdTNEF_PT_LONG = 3; //* Signed 32-bit value */ + IdTNEF_PT_R4 = 4; //* 4-byte floating point */ + IdTNEF_PT_DOUBLE = 5; //TODO: * Floating point double */ + IdTNEF_PT_CURRENCY = 6; //TODO: * Signed 64-bit int (decimal w/ 4 digits right of decimal pt) */ + IdTNEF_PT_APPTIME = 7; //TODO: * Application time */ + IdTNEF_PT_ERROR = 10; //TODO: * 32-bit error value */ + IdTNEF_PT_BOOLEAN = 11; //* 16-bit boolean (non-zero true) */ + IdTNEF_PT_OBJECT = 13; //* Embedded object in a property */ + IdTNEF_PT_I8 = 20; //TODO: * 8-byte signed integer */ + IdTNEF_PT_STRING8 = 30; //* Null terminated 8-bit character string */ + IdTNEF_PT_UNICODE = 31; //* Null terminated Unicode string */ + IdTNEF_PT_SYSTIME = 64; //TODO: * FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */ + IdTNEF_PT_CLSID = 72; //TODO:* OLE GUID */ + IdTNEF_PT_BINARY = 258; //* Uninterpreted (counted byte array) */ + + IdTNEF_MV_FLAG = $1000; //TODO: * Multi-value flag */ + + + //MAPI property tags... + IdTNEF_PR_ALTERNATE_RECIPIENT_ALLOWED = $0002; //PROP_TAG( PT_BOOLEAN, 0x0002) + IdTNEF_PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED = $0023; //PROP_TAG( PT_BOOLEAN, 0x0023) + IdTNEF_PR_PRIORITY = $0026; //PROP_TAG( PT_LONG, 0x0026) + IdTNEF_PR_READ_RECEIPT_REQUESTED = $0029; //PROP_TAG( PT_BOOLEAN, 0x0029) + IdTNEF_PR_ORIGINAL_SENSITIVITY = $002E; //PROP_TAG( PT_LONG, 0x002E) + IdTNEF_PR_SENSITIVITY = $0036; //PROP_TAG( PT_LONG, 0x0036) + IdTNEF_PR_CLIENT_SUBMIT_TIME = $0039; //PROP_TAG( PT_SYSTIME, 0x0039) + IdTNEF_PR_SUBJECT_PREFIX = $003D; //PROP_TAG( PT_TSTRING, 0x003D) + IdTNEF_PR_MESSAGE_SUBMISSION_ID = $0047; //PROP_TAG( PT_BINARY, 0x0047) + IdTNEF_PR_ORIGINAL_SUBJECT = $0049; //PROP_TAG( PT_TSTRING, 0x0049) + IdTNEF_PR_ORIGINAL_AUTHOR_NAME = $004D; //PROP_TAG( PT_TSTRING, 0x004D) + IdTNEF_PR_ORIGINAL_SUBMIT_TIME = $004E; //PROP_TAG( PT_SYSTIME, 0x004E) + IdTNEF_PR_ORIGINAL_SENDER_NAME = $005A; //PROP_TAG( PT_TSTRING, 0x005A) + IdTNEF_PR_ORIGINAL_SENDER_ENTRYID = $005B; //PROP_TAG( PT_BINARY, 0x005B) + IdTNEF_PR_ORIGINAL_SENDER_SEARCH_KEY = $005C; //PROP_TAG( PT_BINARY, 0x005C) + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_NAME = $005D; //PROP_TAG( PT_TSTRING, 0x005D) + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_ENTRYID = $005E; //PROP_TAG( PT_BINARY, 0x005E) + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY = $005F; //PROP_TAG( PT_BINARY, 0x005F) + IdTNEF_PR_ORIGINAL_SENDER_ADDRTYPE = $0066; //PROP_TAG( PT_TSTRING, 0x0066) + IdTNEF_PR_ORIGINAL_SENDER_EMAIL_ADDRESS = $0067; //PROP_TAG( PT_TSTRING, 0x0067) + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE = $0068; //PROP_TAG( PT_TSTRING, 0x0068) + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS= $0069; //PROP_TAG( PT_TSTRING, 0x0069) + IdTNEF_PR_CONVERSATION_TOPIC = $0070; //PROP_TAG( PT_TSTRING, 0x0070) + IdTNEF_PR_CONVERSATION_INDEX = $0071; //PROP_TAG( PT_BINARY, 0x0071) + IdTNEF_PR_ORIGINAL_DISPLAY_CC = $0073; //PROP_TAG( PT_TSTRING, 0x0073) + IdTNEF_PR_ORIGINAL_DISPLAY_TO = $0074; //PROP_TAG( PT_TSTRING, 0x0074) + IdTNEF_PR_REPLY_REQUESTED = $0C17; //PROP_TAG( PT_BOOLEAN, 0x0C17) + IdTNEF_PR_SENDER_SEARCH_KEY = $0C1D; //PROP_TAG( PT_BINARY, 0x0C1D) + IdTNEF_PR_SENDER_NAME = $0C1A; //PROP_TAG( PT_TSTRING, 0x0C1A) + IdTNEF_PR_DELETE_AFTER_SUBMIT = $0E01; //PROP_TAG( PT_BOOLEAN, 0x0E01) + IdTNEF_PR_MESSAGE_DELIVERY_TIME = $0E06; //PROP_TAG( PT_SYSTIME, 0x0E06) + IdTNEF_PR_SENTMAIL_ENTRYID = $0E0A; //PROP_TAG( PT_BINARY, 0x0E0A) + IdTNEF_PR_NORMALIZED_SUBJECT = $0E1D; //PROP_TAG( PT_TSTRING, 0x0E1D) + IdTNEF_PR_RTF_IN_SYNC = $0E1F; //PROP_TAG( PT_BOOLEAN, 0x0E1F) + IdTNEF_PR_MAPPING_SIGNATURE = $0FF8; //PROP_TAG( PT_BINARY, 0x0FF8) + IdTNEF_PR_STORE_RECORD_KEY = $0FFA; //PROP_TAG( PT_BINARY, 0x0FFA) + IdTNEF_PR_STORE_ENTRYID = $0FFB; //PROP_TAG( PT_BINARY, 0x0FFB) + IdTNEF_PR_OBJECT_TYPE = $0FFE; //PROP_TAG( PT_LONG, 0x0FFE) + IdTNEF_PR_STORE_SUPPORT_MASK = $340D; //PROP_TAG( PT_LONG, 0x340D) + IdTNEF_PR_TNEF_CORRELATION_KEY = $007F; //PROP_TAG(PT_BINARY, 0x007F) + IdTNEF_PR_BODY = $1000; //PROP_TAG( PT_TSTRING, 0x1000) + IdTNEF_PR_RTF_SYNC_BODY_CRC = $1006; //PROP_TAG( PT_LONG, 0x1006) + IdTNEF_PR_RTF_SYNC_BODY_COUNT = $1007; //PROP_TAG( PT_LONG, 0x1007) + IdTNEF_PR_RTF_SYNC_BODY_TAG = $1008; //PROP_TAG( PT_TSTRING, 0x1008) + IdTNEF_PR_RTF_COMPRESSED = $1009; //PROP_TAG( PT_BINARY, 0x1009) + IdTNEF_PR_RTF_SYNC_PREFIX_COUNT = $1010; //PROP_TAG( PT_LONG, 0x1010) + IdTNEF_PR_RTF_SYNC_TRAILING_COUNT = $1011; //PROP_TAG( PT_LONG, 0x1011) + IdTNEF_PR_ORIGINALLY_INTENDED_RECIP_ENTRYID = $1012; //PROP_TAG( PT_BINARY, 0x1012) +{ +#define PR_ACKNOWLEDGEMENT_MODE PROP_TAG( PT_LONG, 0x0001) +//#define PR_ALTERNATE_RECIPIENT_ALLOWED PROP_TAG( PT_BOOLEAN, 0x0002) +#define PR_AUTHORIZING_USERS PROP_TAG( PT_BINARY, 0x0003) +#define PR_AUTO_FORWARD_COMMENT PROP_TAG( PT_TSTRING, 0x0004) +#define PR_AUTO_FORWARD_COMMENT_W PROP_TAG( PT_UNICODE, 0x0004) +#define PR_AUTO_FORWARD_COMMENT_A PROP_TAG( PT_STRING8, 0x0004) +#define PR_AUTO_FORWARDED PROP_TAG( PT_BOOLEAN, 0x0005) +#define PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID PROP_TAG( PT_BINARY, 0x0006) +#define PR_CONTENT_CORRELATOR PROP_TAG( PT_BINARY, 0x0007) +#define PR_CONTENT_IDENTIFIER PROP_TAG( PT_TSTRING, 0x0008) +#define PR_CONTENT_IDENTIFIER_W PROP_TAG( PT_UNICODE, 0x0008) +#define PR_CONTENT_IDENTIFIER_A PROP_TAG( PT_STRING8, 0x0008) +#define PR_CONTENT_LENGTH PROP_TAG( PT_LONG, 0x0009) +#define PR_CONTENT_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x000A) + + + +#define PR_CONVERSATION_KEY PROP_TAG( PT_BINARY, 0x000B) + +#define PR_CONVERSION_EITS PROP_TAG( PT_BINARY, 0x000C) +#define PR_CONVERSION_WITH_LOSS_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x000D) +#define PR_CONVERTED_EITS PROP_TAG( PT_BINARY, 0x000E) +#define PR_DEFERRED_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x000F) +#define PR_DELIVER_TIME PROP_TAG( PT_SYSTIME, 0x0010) +#define PR_DISCARD_REASON PROP_TAG( PT_LONG, 0x0011) +#define PR_DISCLOSURE_OF_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x0012) +#define PR_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x0013) +#define PR_DL_EXPANSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0014) +#define PR_EXPIRY_TIME PROP_TAG( PT_SYSTIME, 0x0015) +#define PR_IMPLICIT_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0016) +#define PR_IMPORTANCE PROP_TAG( PT_LONG, 0x0017) +#define PR_IPM_ID PROP_TAG( PT_BINARY, 0x0018) +#define PR_LATEST_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0019) +#define PR_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x001A) +#define PR_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x001A) +#define PR_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x001A) +#define PR_MESSAGE_DELIVERY_ID PROP_TAG( PT_BINARY, 0x001B) + + + + + +#define PR_MESSAGE_SECURITY_LABEL PROP_TAG( PT_BINARY, 0x001E) +#define PR_OBSOLETED_IPMS PROP_TAG( PT_BINARY, 0x001F) +#define PR_ORIGINALLY_INTENDED_RECIPIENT_NAME PROP_TAG( PT_BINARY, 0x0020) +#define PR_ORIGINAL_EITS PROP_TAG( PT_BINARY, 0x0021) +#define PR_ORIGINATOR_CERTIFICATE PROP_TAG( PT_BINARY, 0x0022) +//#define PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0023) +#define PR_ORIGINATOR_RETURN_ADDRESS PROP_TAG( PT_BINARY, 0x0024) + + + +#define PR_PARENT_KEY PROP_TAG( PT_BINARY, 0x0025) +//#define PR_PRIORITY PROP_TAG( PT_LONG, 0x0026) + + + +#define PR_ORIGIN_CHECK PROP_TAG( PT_BINARY, 0x0027) +#define PR_PROOF_OF_SUBMISSION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0028) +#define PR_READ_RECEIPT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0029) +#define PR_RECEIPT_TIME PROP_TAG( PT_SYSTIME, 0x002A) +#define PR_RECIPIENT_REASSIGNMENT_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x002B) +#define PR_REDIRECTION_HISTORY PROP_TAG( PT_BINARY, 0x002C) +#define PR_RELATED_IPMS PROP_TAG( PT_BINARY, 0x002D) +//#define PR_ORIGINAL_SENSITIVITY PROP_TAG( PT_LONG, 0x002E) +#define PR_LANGUAGES PROP_TAG( PT_TSTRING, 0x002F) +#define PR_LANGUAGES_W PROP_TAG( PT_UNICODE, 0x002F) +#define PR_LANGUAGES_A PROP_TAG( PT_STRING8, 0x002F) +#define PR_REPLY_TIME PROP_TAG( PT_SYSTIME, 0x0030) +#define PR_REPORT_TAG PROP_TAG( PT_BINARY, 0x0031) +#define PR_REPORT_TIME PROP_TAG( PT_SYSTIME, 0x0032) +#define PR_RETURNED_IPM PROP_TAG( PT_BOOLEAN, 0x0033) +#define PR_SECURITY PROP_TAG( PT_LONG, 0x0034) +#define PR_INCOMPLETE_COPY PROP_TAG( PT_BOOLEAN, 0x0035) +#define PR_SENSITIVITY PROP_TAG( PT_LONG, 0x0036) +#define PR_SUBJECT PROP_TAG( PT_TSTRING, 0x0037) +#define PR_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0037) +#define PR_SUBJECT_A PROP_TAG( PT_STRING8, 0x0037) +#define PR_SUBJECT_IPM PROP_TAG( PT_BINARY, 0x0038) +//#define PR_CLIENT_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0039) +#define PR_REPORT_NAME PROP_TAG( PT_TSTRING, 0x003A) +#define PR_REPORT_NAME_W PROP_TAG( PT_UNICODE, 0x003A) +#define PR_REPORT_NAME_A PROP_TAG( PT_STRING8, 0x003A) +#define PR_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x003B) +#define PR_X400_CONTENT_TYPE PROP_TAG( PT_BINARY, 0x003C) +//#define PR_SUBJECT_PREFIX PROP_TAG( PT_TSTRING, 0x003D) +//#define PR_SUBJECT_PREFIX_W PROP_TAG( PT_UNICODE, 0x003D) +//#define PR_SUBJECT_PREFIX_A PROP_TAG( PT_STRING8, 0x003D) +#define PR_NON_RECEIPT_REASON PROP_TAG( PT_LONG, 0x003E) +#define PR_RECEIVED_BY_ENTRYID PROP_TAG( PT_BINARY, 0x003F) +#define PR_RECEIVED_BY_NAME PROP_TAG( PT_TSTRING, 0x0040) +#define PR_RECEIVED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x0040) +#define PR_RECEIVED_BY_NAME_A PROP_TAG( PT_STRING8, 0x0040) +#define PR_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0041) +#define PR_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0042) +#define PR_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0042) +#define PR_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0042) +#define PR_RCVD_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0043) +#define PR_RCVD_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0044) +#define PR_RCVD_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0044) +#define PR_RCVD_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0044) +#define PR_REPORT_ENTRYID PROP_TAG( PT_BINARY, 0x0045) +#define PR_READ_RECEIPT_ENTRYID PROP_TAG( PT_BINARY, 0x0046) +#define PR_MESSAGE_SUBMISSION_ID PROP_TAG( PT_BINARY, 0x0047) +#define PR_PROVIDER_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0048) +//#define PR_ORIGINAL_SUBJECT PROP_TAG( PT_TSTRING, 0x0049) +#define PR_ORIGINAL_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0049) +#define PR_ORIGINAL_SUBJECT_A PROP_TAG( PT_STRING8, 0x0049) +#define PR_DISC_VAL PROP_TAG( PT_BOOLEAN, 0x004A) +#define PR_ORIG_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x004B) +#define PR_ORIG_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x004B) +#define PR_ORIG_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x004B) +#define PR_ORIGINAL_AUTHOR_ENTRYID PROP_TAG( PT_BINARY, 0x004C) +#define PR_ORIGINAL_AUTHOR_NAME PROP_TAG( PT_TSTRING, 0x004D) +#define PR_ORIGINAL_AUTHOR_NAME_W PROP_TAG( PT_UNICODE, 0x004D) +#define PR_ORIGINAL_AUTHOR_NAME_A PROP_TAG( PT_STRING8, 0x004D) +//#define PR_ORIGINAL_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x004E) +#define PR_REPLY_RECIPIENT_ENTRIES PROP_TAG( PT_BINARY, 0x004F) +#define PR_REPLY_RECIPIENT_NAMES PROP_TAG( PT_TSTRING, 0x0050) +#define PR_REPLY_RECIPIENT_NAMES_W PROP_TAG( PT_UNICODE, 0x0050) +#define PR_REPLY_RECIPIENT_NAMES_A PROP_TAG( PT_STRING8, 0x0050) + +#define PR_RECEIVED_BY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0051) +#define PR_RCVD_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0052) +#define PR_READ_RECEIPT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0053) +#define PR_REPORT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0054) +#define PR_ORIGINAL_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0055) +#define PR_ORIGINAL_AUTHOR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0056) + +#define PR_MESSAGE_TO_ME PROP_TAG( PT_BOOLEAN, 0x0057) +#define PR_MESSAGE_CC_ME PROP_TAG( PT_BOOLEAN, 0x0058) +#define PR_MESSAGE_RECIP_ME PROP_TAG( PT_BOOLEAN, 0x0059) + +//#define PR_ORIGINAL_SENDER_NAME PROP_TAG( PT_TSTRING, 0x005A) +//#define PR_ORIGINAL_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x005A) +//#define PR_ORIGINAL_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x005A) +//#define PR_ORIGINAL_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x005B) +//#define PR_ORIGINAL_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005C) +//#define PR_ORIGINAL_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x005D) +//#define PR_ORIGINAL_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x005D) +//#define PR_ORIGINAL_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x005D) +//#define PR_ORIGINAL_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x005E) +//#define PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005F) + +#define PR_START_DATE PROP_TAG( PT_SYSTIME, 0x0060) +#define PR_END_DATE PROP_TAG( PT_SYSTIME, 0x0061) +#define PR_OWNER_APPT_ID PROP_TAG( PT_LONG, 0x0062) +#define PR_RESPONSE_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0063) + +#define PR_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0064) +#define PR_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0064) +#define PR_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0064) +#define PR_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0065) +#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0065) +#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0065) + +//#define PR_ORIGINAL_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0066) +//#define PR_ORIGINAL_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0066) +//#define PR_ORIGINAL_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0066) +//#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0067) +//#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0067) +//#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0067) + +//#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0068) +//#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0068) +//#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0068) +//#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0069) +//#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0069) +//#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0069) + +//#define PR_CONVERSATION_TOPIC PROP_TAG( PT_TSTRING, 0x0070) +#define PR_CONVERSATION_TOPIC_W PROP_TAG( PT_UNICODE, 0x0070) +#define PR_CONVERSATION_TOPIC_A PROP_TAG( PT_STRING8, 0x0070) +//#define PR_CONVERSATION_INDEX PROP_TAG( PT_BINARY, 0x0071) + +#define PR_ORIGINAL_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0072) +#define PR_ORIGINAL_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0072) +#define PR_ORIGINAL_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0072) +//#define PR_ORIGINAL_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0073) +//#define PR_ORIGINAL_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0073) +//#define PR_ORIGINAL_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0073) +//#define PR_ORIGINAL_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0074) +//#define PR_ORIGINAL_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0074) +//#define PR_ORIGINAL_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0074) + +#define PR_RECEIVED_BY_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0075) +#define PR_RECEIVED_BY_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0075) +#define PR_RECEIVED_BY_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0075) +#define PR_RECEIVED_BY_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0076) +#define PR_RECEIVED_BY_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0076) +#define PR_RECEIVED_BY_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0076) + +#define PR_RCVD_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0077) +#define PR_RCVD_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0077) +#define PR_RCVD_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0077) +#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0078) +#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0078) +#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0078) + +#define PR_ORIGINAL_AUTHOR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0079) +#define PR_ORIGINAL_AUTHOR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0079) +#define PR_ORIGINAL_AUTHOR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0079) +#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x007A) +#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x007A) +#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x007A) + +#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE PROP_TAG( PT_TSTRING, 0x007B) +#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x007B) +#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x007B) +#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x007C) +#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x007C) +#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x007C) + +#define PR_TRANSPORT_MESSAGE_HEADERS PROP_TAG(PT_TSTRING, 0x007D) +#define PR_TRANSPORT_MESSAGE_HEADERS_W PROP_TAG(PT_UNICODE, 0x007D) +#define PR_TRANSPORT_MESSAGE_HEADERS_A PROP_TAG(PT_STRING8, 0x007D) + +#define PR_DELEGATION PROP_TAG(PT_BINARY, 0x007E) + +#define PR_TNEF_CORRELATION_KEY PROP_TAG(PT_BINARY, 0x007F) + + + +/* + * Message content properties + */ + +//#define PR_BODY PROP_TAG( PT_TSTRING, 0x1000) +//#define PR_BODY_W PROP_TAG( PT_UNICODE, 0x1000) +//#define PR_BODY_A PROP_TAG( PT_STRING8, 0x1000) +#define PR_REPORT_TEXT PROP_TAG( PT_TSTRING, 0x1001) +#define PR_REPORT_TEXT_W PROP_TAG( PT_UNICODE, 0x1001) +#define PR_REPORT_TEXT_A PROP_TAG( PT_STRING8, 0x1001) +#define PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x1002) +#define PR_REPORTING_DL_NAME PROP_TAG( PT_BINARY, 0x1003) +#define PR_REPORTING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x1004) + +/* Removed PR_REPORT_ORIGIN_AUTHENTICATION_CHECK with DCR 3865, use PR_ORIGIN_CHECK */ + +#define PR_RTF_SYNC_BODY_CRC PROP_TAG( PT_LONG, 0x1006) +#define PR_RTF_SYNC_BODY_COUNT PROP_TAG( PT_LONG, 0x1007) +#define PR_RTF_SYNC_BODY_TAG PROP_TAG( PT_TSTRING, 0x1008) +#define PR_RTF_SYNC_BODY_TAG_W PROP_TAG( PT_UNICODE, 0x1008) +#define PR_RTF_SYNC_BODY_TAG_A PROP_TAG( PT_STRING8, 0x1008) +#define PR_RTF_COMPRESSED PROP_TAG( PT_BINARY, 0x1009) +#define PR_RTF_SYNC_PREFIX_COUNT PROP_TAG( PT_LONG, 0x1010) +#define PR_RTF_SYNC_TRAILING_COUNT PROP_TAG( PT_LONG, 0x1011) +#define PR_ORIGINALLY_INTENDED_RECIP_ENTRYID PROP_TAG( PT_BINARY, 0x1012) + +/* + * Reserved 0x1100-0x1200 + */ + + +/* + * Message recipient properties + */ + +#define PR_CONTENT_INTEGRITY_CHECK PROP_TAG( PT_BINARY, 0x0C00) +#define PR_EXPLICIT_CONVERSION PROP_TAG( PT_LONG, 0x0C01) +#define PR_IPM_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C02) +#define PR_MESSAGE_TOKEN PROP_TAG( PT_BINARY, 0x0C03) +#define PR_NDR_REASON_CODE PROP_TAG( PT_LONG, 0x0C04) +#define PR_NDR_DIAG_CODE PROP_TAG( PT_LONG, 0x0C05) +#define PR_NON_RECEIPT_NOTIFICATION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C06) +#define PR_DELIVERY_POINT PROP_TAG( PT_LONG, 0x0C07) + +#define PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C08) +#define PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY, 0x0C09) +#define PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY PROP_TAG( PT_BOOLEAN, 0x0C0A) +#define PR_PHYSICAL_DELIVERY_MODE PROP_TAG( PT_LONG, 0x0C0B) +#define PR_PHYSICAL_DELIVERY_REPORT_REQUEST PROP_TAG( PT_LONG, 0x0C0C) +#define PR_PHYSICAL_FORWARDING_ADDRESS PROP_TAG( PT_BINARY, 0x0C0D) +#define PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C0E) +#define PR_PHYSICAL_FORWARDING_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0C0F) +#define PR_PHYSICAL_RENDITION_ATTRIBUTES PROP_TAG( PT_BINARY, 0x0C10) +#define PR_PROOF_OF_DELIVERY PROP_TAG( PT_BINARY, 0x0C11) +#define PR_PROOF_OF_DELIVERY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C12) +#define PR_RECIPIENT_CERTIFICATE PROP_TAG( PT_BINARY, 0x0C13) +#define PR_RECIPIENT_NUMBER_FOR_ADVICE PROP_TAG( PT_TSTRING, 0x0C14) +#define PR_RECIPIENT_NUMBER_FOR_ADVICE_W PROP_TAG( PT_UNICODE, 0x0C14) +#define PR_RECIPIENT_NUMBER_FOR_ADVICE_A PROP_TAG( PT_STRING8, 0x0C14) +#define PR_RECIPIENT_TYPE PROP_TAG( PT_LONG, 0x0C15) +#define PR_REGISTERED_MAIL_TYPE PROP_TAG( PT_LONG, 0x0C16) +//#define PR_REPLY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C17) +#define PR_REQUESTED_DELIVERY_METHOD PROP_TAG( PT_LONG, 0x0C18) +#define PR_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x0C19) +//#define PR_SENDER_NAME PROP_TAG( PT_TSTRING, 0x0C1A) +//#define PR_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x0C1A) +//#define PR_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x0C1A) +#define PR_SUPPLEMENTARY_INFO PROP_TAG( PT_TSTRING, 0x0C1B) +#define PR_SUPPLEMENTARY_INFO_W PROP_TAG( PT_UNICODE, 0x0C1B) +#define PR_SUPPLEMENTARY_INFO_A PROP_TAG( PT_STRING8, 0x0C1B) +#define PR_TYPE_OF_MTS_USER PROP_TAG( PT_LONG, 0x0C1C) +//#define PR_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0C1D) +#define PR_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0C1E) +#define PR_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0C1E) +#define PR_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0C1E) +#define PR_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0C1F) +#define PR_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0C1F) +#define PR_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0C1F) + +/* + * Message non-transmittable properties + */ + +/* + * The two tags, PR_MESSAGE_RECIPIENTS and PR_MESSAGE_ATTACHMENTS, + * are to be used in the exclude list passed to + * IMessage::CopyTo when the caller wants either the recipients or attachments + * of the message to not get copied. It is also used in the ProblemArray + * return from IMessage::CopyTo when an error is encountered copying them + */ + +#define PR_CURRENT_VERSION PROP_TAG( PT_I8, 0x0E00) +//#define PR_DELETE_AFTER_SUBMIT PROP_TAG( PT_BOOLEAN, 0x0E01) +#define PR_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0E02) +#define PR_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0E02) +#define PR_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0E02) +#define PR_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0E03) +#define PR_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0E03) +#define PR_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0E03) +#define PR_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0E04) +#define PR_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0E04) +#define PR_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0E04) +#define PR_PARENT_DISPLAY PROP_TAG( PT_TSTRING, 0x0E05) +#define PR_PARENT_DISPLAY_W PROP_TAG( PT_UNICODE, 0x0E05) +#define PR_PARENT_DISPLAY_A PROP_TAG( PT_STRING8, 0x0E05) +#define PR_MESSAGE_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0E06) +#define PR_MESSAGE_FLAGS PROP_TAG( PT_LONG, 0x0E07) +#define PR_MESSAGE_SIZE PROP_TAG( PT_LONG, 0x0E08) +#define PR_PARENT_ENTRYID PROP_TAG( PT_BINARY, 0x0E09) +#define PR_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x0E0A) +#define PR_CORRELATE PROP_TAG( PT_BOOLEAN, 0x0E0C) +#define PR_CORRELATE_MTSID PROP_TAG( PT_BINARY, 0x0E0D) +#define PR_DISCRETE_VALUES PROP_TAG( PT_BOOLEAN, 0x0E0E) +#define PR_RESPONSIBILITY PROP_TAG( PT_BOOLEAN, 0x0E0F) +#define PR_SPOOLER_STATUS PROP_TAG( PT_LONG, 0x0E10) +#define PR_TRANSPORT_STATUS PROP_TAG( PT_LONG, 0x0E11) +#define PR_MESSAGE_RECIPIENTS PROP_TAG( PT_OBJECT, 0x0E12) +#define PR_MESSAGE_ATTACHMENTS PROP_TAG( PT_OBJECT, 0x0E13) +#define PR_SUBMIT_FLAGS PROP_TAG( PT_LONG, 0x0E14) +#define PR_RECIPIENT_STATUS PROP_TAG( PT_LONG, 0x0E15) +#define PR_TRANSPORT_KEY PROP_TAG( PT_LONG, 0x0E16) +#define PR_MSG_STATUS PROP_TAG( PT_LONG, 0x0E17) +#define PR_MESSAGE_DOWNLOAD_TIME PROP_TAG( PT_LONG, 0x0E18) +#define PR_CREATION_VERSION PROP_TAG( PT_I8, 0x0E19) +#define PR_MODIFY_VERSION PROP_TAG( PT_I8, 0x0E1A) +#define PR_HASATTACH PROP_TAG( PT_BOOLEAN, 0x0E1B) +#define PR_BODY_CRC PROP_TAG( PT_LONG, 0x0E1C) +//#define PR_NORMALIZED_SUBJECT PROP_TAG( PT_TSTRING, 0x0E1D) +//#define PR_NORMALIZED_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0E1D) +//#define PR_NORMALIZED_SUBJECT_A PROP_TAG( PT_STRING8, 0x0E1D) +#define PR_RTF_IN_SYNC PROP_TAG( PT_BOOLEAN, 0x0E1F) +#define PR_ATTACH_SIZE PROP_TAG( PT_LONG, 0x0E20) +#define PR_ATTACH_NUM PROP_TAG( PT_LONG, 0x0E21) +#define PR_PREPROCESS PROP_TAG( PT_BOOLEAN, 0x0E22) + +/* PR_ORIGINAL_DISPLAY_TO, _CC, and _BCC moved to transmittible range 03/09/95 */ + +#define PR_ORIGINATING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x0E25) +#define PR_PROOF_OF_SUBMISSION PROP_TAG( PT_BINARY, 0x0E26) + + +/* + * The range of non-message and non-recipient property IDs (0x3000 - 0x3FFF) is + * further broken down into ranges to make assigning new property IDs easier. + * + * From To Kind of property + * -------------------------------- + * 3000 32FF MAPI_defined common property + * 3200 33FF MAPI_defined form property + * 3400 35FF MAPI_defined message store property + * 3600 36FF MAPI_defined Folder or AB Container property + * 3700 38FF MAPI_defined attachment property + * 3900 39FF MAPI_defined address book property + * 3A00 3BFF MAPI_defined mailuser property + * 3C00 3CFF MAPI_defined DistList property + * 3D00 3DFF MAPI_defined Profile Section property + * 3E00 3EFF MAPI_defined Status property + * 3F00 3FFF MAPI_defined display table property + */ + +/* + * Properties common to numerous MAPI objects. + * + * Those properties that can appear on messages are in the + * non-transmittable range for messages. They start at the high + * end of that range and work down. + * + * Properties that never appear on messages are defined in the common + * property range (see above). + */ + +/* + * properties that are common to multiple objects (including message objects) + * -- these ids are in the non-transmittable range + */ + +#define PR_ENTRYID PROP_TAG( PT_BINARY, 0x0FFF) +#define PR_OBJECT_TYPE PROP_TAG( PT_LONG, 0x0FFE) +#define PR_ICON PROP_TAG( PT_BINARY, 0x0FFD) +#define PR_MINI_ICON PROP_TAG( PT_BINARY, 0x0FFC) +#define PR_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x0FFB) +#define PR_STORE_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FFA) +#define PR_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FF9) +#define PR_MAPPING_SIGNATURE PROP_TAG( PT_BINARY, 0x0FF8) +#define PR_ACCESS_LEVEL PROP_TAG( PT_LONG, 0x0FF7) +#define PR_INSTANCE_KEY PROP_TAG( PT_BINARY, 0x0FF6) +#define PR_ROW_TYPE PROP_TAG( PT_LONG, 0x0FF5) +#define PR_ACCESS PROP_TAG( PT_LONG, 0x0FF4) + +/* + * properties that are common to multiple objects (usually not including message objects) + * -- these ids are in the transmittable range + */ + +#define PR_ROWID PROP_TAG( PT_LONG, 0x3000) +#define PR_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3001) +#define PR_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3001) +#define PR_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3001) +#define PR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x3002) +#define PR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x3002) +#define PR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x3002) +#define PR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x3003) +#define PR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3003) +#define PR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3003) +#define PR_COMMENT PROP_TAG( PT_TSTRING, 0x3004) +#define PR_COMMENT_W PROP_TAG( PT_UNICODE, 0x3004) +#define PR_COMMENT_A PROP_TAG( PT_STRING8, 0x3004) +#define PR_DEPTH PROP_TAG( PT_LONG, 0x3005) +#define PR_PROVIDER_DISPLAY PROP_TAG( PT_TSTRING, 0x3006) +#define PR_PROVIDER_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3006) +#define PR_PROVIDER_DISPLAY_A PROP_TAG( PT_STRING8, 0x3006) +#define PR_CREATION_TIME PROP_TAG( PT_SYSTIME, 0x3007) +#define PR_LAST_MODIFICATION_TIME PROP_TAG( PT_SYSTIME, 0x3008) +#define PR_RESOURCE_FLAGS PROP_TAG( PT_LONG, 0x3009) +#define PR_PROVIDER_DLL_NAME PROP_TAG( PT_TSTRING, 0x300A) +#define PR_PROVIDER_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x300A) +#define PR_PROVIDER_DLL_NAME_A PROP_TAG( PT_STRING8, 0x300A) +#define PR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x300B) +#define PR_PROVIDER_UID PROP_TAG( PT_BINARY, 0x300C) +#define PR_PROVIDER_ORDINAL PROP_TAG( PT_LONG, 0x300D) + +/* + * MAPI Form properties + */ +#define PR_FORM_VERSION PROP_TAG(PT_TSTRING, 0x3301) +#define PR_FORM_VERSION_W PROP_TAG(PT_UNICODE, 0x3301) +#define PR_FORM_VERSION_A PROP_TAG(PT_STRING8, 0x3301) +#define PR_FORM_CLSID PROP_TAG(PT_CLSID, 0x3302) +#define PR_FORM_CONTACT_NAME PROP_TAG(PT_TSTRING, 0x3303) +#define PR_FORM_CONTACT_NAME_W PROP_TAG(PT_UNICODE, 0x3303) +#define PR_FORM_CONTACT_NAME_A PROP_TAG(PT_STRING8, 0x3303) +#define PR_FORM_CATEGORY PROP_TAG(PT_TSTRING, 0x3304) +#define PR_FORM_CATEGORY_W PROP_TAG(PT_UNICODE, 0x3304) +#define PR_FORM_CATEGORY_A PROP_TAG(PT_STRING8, 0x3304) +#define PR_FORM_CATEGORY_SUB PROP_TAG(PT_TSTRING, 0x3305) +#define PR_FORM_CATEGORY_SUB_W PROP_TAG(PT_UNICODE, 0x3305) +#define PR_FORM_CATEGORY_SUB_A PROP_TAG(PT_STRING8, 0x3305) +#define PR_FORM_HOST_MAP PROP_TAG(PT_MV_LONG, 0x3306) +#define PR_FORM_HIDDEN PROP_TAG(PT_BOOLEAN, 0x3307) +#define PR_FORM_DESIGNER_NAME PROP_TAG(PT_TSTRING, 0x3308) +#define PR_FORM_DESIGNER_NAME_W PROP_TAG(PT_UNICODE, 0x3308) +#define PR_FORM_DESIGNER_NAME_A PROP_TAG(PT_STRING8, 0x3308) +#define PR_FORM_DESIGNER_GUID PROP_TAG(PT_CLSID, 0x3309) +#define PR_FORM_MESSAGE_BEHAVIOR PROP_TAG(PT_LONG, 0x330A) + +/* + * Message store properties + */ + +#define PR_DEFAULT_STORE PROP_TAG( PT_BOOLEAN, 0x3400) +#define PR_STORE_SUPPORT_MASK PROP_TAG( PT_LONG, 0x340D) +#define PR_STORE_STATE PROP_TAG( PT_LONG, 0x340E) + +#define PR_IPM_SUBTREE_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3410) +#define PR_IPM_OUTBOX_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3411) +#define PR_IPM_WASTEBASKET_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3412) +#define PR_IPM_SENTMAIL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3413) +#define PR_MDB_PROVIDER PROP_TAG( PT_BINARY, 0x3414) +#define PR_RECEIVE_FOLDER_SETTINGS PROP_TAG( PT_OBJECT, 0x3415) + +#define PR_VALID_FOLDER_MASK PROP_TAG( PT_LONG, 0x35DF) +#define PR_IPM_SUBTREE_ENTRYID PROP_TAG( PT_BINARY, 0x35E0) + +#define PR_IPM_OUTBOX_ENTRYID PROP_TAG( PT_BINARY, 0x35E2) +#define PR_IPM_WASTEBASKET_ENTRYID PROP_TAG( PT_BINARY, 0x35E3) +#define PR_IPM_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x35E4) +#define PR_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E5) +#define PR_COMMON_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E6) +#define PR_FINDER_ENTRYID PROP_TAG( PT_BINARY, 0x35E7) + +/* Proptags 0x35E8-0x35FF reserved for folders "guaranteed" by PR_VALID_FOLDER_MASK */ + + +/* + * Folder and AB Container properties + */ + +#define PR_CONTAINER_FLAGS PROP_TAG( PT_LONG, 0x3600) +#define PR_FOLDER_TYPE PROP_TAG( PT_LONG, 0x3601) +#define PR_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3602) +#define PR_CONTENT_UNREAD PROP_TAG( PT_LONG, 0x3603) +#define PR_CREATE_TEMPLATES PROP_TAG( PT_OBJECT, 0x3604) +#define PR_DETAILS_TABLE PROP_TAG( PT_OBJECT, 0x3605) +#define PR_SEARCH PROP_TAG( PT_OBJECT, 0x3607) +#define PR_SELECTABLE PROP_TAG( PT_BOOLEAN, 0x3609) +#define PR_SUBFOLDERS PROP_TAG( PT_BOOLEAN, 0x360A) +#define PR_STATUS PROP_TAG( PT_LONG, 0x360B) +#define PR_ANR PROP_TAG( PT_TSTRING, 0x360C) +#define PR_ANR_W PROP_TAG( PT_UNICODE, 0x360C) +#define PR_ANR_A PROP_TAG( PT_STRING8, 0x360C) +#define PR_CONTENTS_SORT_ORDER PROP_TAG( PT_MV_LONG, 0x360D) +#define PR_CONTAINER_HIERARCHY PROP_TAG( PT_OBJECT, 0x360E) +#define PR_CONTAINER_CONTENTS PROP_TAG( PT_OBJECT, 0x360F) +#define PR_FOLDER_ASSOCIATED_CONTENTS PROP_TAG( PT_OBJECT, 0x3610) +#define PR_DEF_CREATE_DL PROP_TAG( PT_BINARY, 0x3611) +#define PR_DEF_CREATE_MAILUSER PROP_TAG( PT_BINARY, 0x3612) +#define PR_CONTAINER_CLASS PROP_TAG( PT_TSTRING, 0x3613) +#define PR_CONTAINER_CLASS_W PROP_TAG( PT_UNICODE, 0x3613) +#define PR_CONTAINER_CLASS_A PROP_TAG( PT_STRING8, 0x3613) +#define PR_CONTAINER_MODIFY_VERSION PROP_TAG( PT_I8, 0x3614) +#define PR_AB_PROVIDER_ID PROP_TAG( PT_BINARY, 0x3615) +#define PR_DEFAULT_VIEW_ENTRYID PROP_TAG( PT_BINARY, 0x3616) +#define PR_ASSOC_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3617) + +/* Reserved 0x36C0-0x36FF */ + +/* + * Attachment properties + */ + +#define PR_ATTACHMENT_X400_PARAMETERS PROP_TAG( PT_BINARY, 0x3700) +#define PR_ATTACH_DATA_OBJ PROP_TAG( PT_OBJECT, 0x3701) +#define PR_ATTACH_DATA_BIN PROP_TAG( PT_BINARY, 0x3701) +#define PR_ATTACH_ENCODING PROP_TAG( PT_BINARY, 0x3702) +#define PR_ATTACH_EXTENSION PROP_TAG( PT_TSTRING, 0x3703) +#define PR_ATTACH_EXTENSION_W PROP_TAG( PT_UNICODE, 0x3703) +#define PR_ATTACH_EXTENSION_A PROP_TAG( PT_STRING8, 0x3703) +#define PR_ATTACH_FILENAME PROP_TAG( PT_TSTRING, 0x3704) +#define PR_ATTACH_FILENAME_W PROP_TAG( PT_UNICODE, 0x3704) +#define PR_ATTACH_FILENAME_A PROP_TAG( PT_STRING8, 0x3704) +#define PR_ATTACH_METHOD PROP_TAG( PT_LONG, 0x3705) +#define PR_ATTACH_LONG_FILENAME PROP_TAG( PT_TSTRING, 0x3707) +#define PR_ATTACH_LONG_FILENAME_W PROP_TAG( PT_UNICODE, 0x3707) +#define PR_ATTACH_LONG_FILENAME_A PROP_TAG( PT_STRING8, 0x3707) +#define PR_ATTACH_PATHNAME PROP_TAG( PT_TSTRING, 0x3708) +#define PR_ATTACH_PATHNAME_W PROP_TAG( PT_UNICODE, 0x3708) +#define PR_ATTACH_PATHNAME_A PROP_TAG( PT_STRING8, 0x3708) +#define PR_ATTACH_RENDERING PROP_TAG( PT_BINARY, 0x3709) +#define PR_ATTACH_TAG PROP_TAG( PT_BINARY, 0x370A) +#define PR_RENDERING_POSITION PROP_TAG( PT_LONG, 0x370B) +#define PR_ATTACH_TRANSPORT_NAME PROP_TAG( PT_TSTRING, 0x370C) +#define PR_ATTACH_TRANSPORT_NAME_W PROP_TAG( PT_UNICODE, 0x370C) +#define PR_ATTACH_TRANSPORT_NAME_A PROP_TAG( PT_STRING8, 0x370C) +#define PR_ATTACH_LONG_PATHNAME PROP_TAG( PT_TSTRING, 0x370D) +#define PR_ATTACH_LONG_PATHNAME_W PROP_TAG( PT_UNICODE, 0x370D) +#define PR_ATTACH_LONG_PATHNAME_A PROP_TAG( PT_STRING8, 0x370D) +#define PR_ATTACH_MIME_TAG PROP_TAG( PT_TSTRING, 0x370E) +#define PR_ATTACH_MIME_TAG_W PROP_TAG( PT_UNICODE, 0x370E) +#define PR_ATTACH_MIME_TAG_A PROP_TAG( PT_STRING8, 0x370E) +#define PR_ATTACH_ADDITIONAL_INFO PROP_TAG( PT_BINARY, 0x370F) + +/* + * AB Object properties + */ + +#define PR_DISPLAY_TYPE PROP_TAG( PT_LONG, 0x3900) +#define PR_TEMPLATEID PROP_TAG( PT_BINARY, 0x3902) +#define PR_PRIMARY_CAPABILITY PROP_TAG( PT_BINARY, 0x3904) + + +/* + * Mail user properties + */ +#define PR_7BIT_DISPLAY_NAME PROP_TAG( PT_STRING8, 0x39FF) +#define PR_ACCOUNT PROP_TAG( PT_TSTRING, 0x3A00) +#define PR_ACCOUNT_W PROP_TAG( PT_UNICODE, 0x3A00) +#define PR_ACCOUNT_A PROP_TAG( PT_STRING8, 0x3A00) +#define PR_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY, 0x3A01) +#define PR_CALLBACK_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A02) +#define PR_CALLBACK_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A02) +#define PR_CALLBACK_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A02) +#define PR_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x3A03) +#define PR_DISCLOSE_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x3A04) +#define PR_GENERATION PROP_TAG( PT_TSTRING, 0x3A05) +#define PR_GENERATION_W PROP_TAG( PT_UNICODE, 0x3A05) +#define PR_GENERATION_A PROP_TAG( PT_STRING8, 0x3A05) +#define PR_GIVEN_NAME PROP_TAG( PT_TSTRING, 0x3A06) +#define PR_GIVEN_NAME_W PROP_TAG( PT_UNICODE, 0x3A06) +#define PR_GIVEN_NAME_A PROP_TAG( PT_STRING8, 0x3A06) +#define PR_GOVERNMENT_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A07) +#define PR_GOVERNMENT_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A07) +#define PR_GOVERNMENT_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A07) +#define PR_BUSINESS_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A08) +#define PR_BUSINESS_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A08) +#define PR_BUSINESS_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A08) +#define PR_OFFICE_TELEPHONE_NUMBER PR_BUSINESS_TELEPHONE_NUMBER +#define PR_OFFICE_TELEPHONE_NUMBER_W PR_BUSINESS_TELEPHONE_NUMBER_W +#define PR_OFFICE_TELEPHONE_NUMBER_A PR_BUSINESS_TELEPHONE_NUMBER_A +#define PR_HOME_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A09) +#define PR_HOME_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A09) +#define PR_HOME_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A09) +#define PR_INITIALS PROP_TAG( PT_TSTRING, 0x3A0A) +#define PR_INITIALS_W PROP_TAG( PT_UNICODE, 0x3A0A) +#define PR_INITIALS_A PROP_TAG( PT_STRING8, 0x3A0A) +#define PR_KEYWORD PROP_TAG( PT_TSTRING, 0x3A0B) +#define PR_KEYWORD_W PROP_TAG( PT_UNICODE, 0x3A0B) +#define PR_KEYWORD_A PROP_TAG( PT_STRING8, 0x3A0B) +#define PR_LANGUAGE PROP_TAG( PT_TSTRING, 0x3A0C) +#define PR_LANGUAGE_W PROP_TAG( PT_UNICODE, 0x3A0C) +#define PR_LANGUAGE_A PROP_TAG( PT_STRING8, 0x3A0C) +#define PR_LOCATION PROP_TAG( PT_TSTRING, 0x3A0D) +#define PR_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A0D) +#define PR_LOCATION_A PROP_TAG( PT_STRING8, 0x3A0D) +#define PR_MAIL_PERMISSION PROP_TAG( PT_BOOLEAN, 0x3A0E) +#define PR_MHS_COMMON_NAME PROP_TAG( PT_TSTRING, 0x3A0F) +#define PR_MHS_COMMON_NAME_W PROP_TAG( PT_UNICODE, 0x3A0F) +#define PR_MHS_COMMON_NAME_A PROP_TAG( PT_STRING8, 0x3A0F) +#define PR_ORGANIZATIONAL_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A10) +#define PR_ORGANIZATIONAL_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A10) +#define PR_ORGANIZATIONAL_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A10) +#define PR_SURNAME PROP_TAG( PT_TSTRING, 0x3A11) +#define PR_SURNAME_W PROP_TAG( PT_UNICODE, 0x3A11) +#define PR_SURNAME_A PROP_TAG( PT_STRING8, 0x3A11) +#define PR_ORIGINAL_ENTRYID PROP_TAG( PT_BINARY, 0x3A12) +#define PR_ORIGINAL_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A13) +#define PR_ORIGINAL_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A13) +#define PR_ORIGINAL_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A13) +#define PR_ORIGINAL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3A14) +#define PR_POSTAL_ADDRESS PROP_TAG( PT_TSTRING, 0x3A15) +#define PR_POSTAL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A15) +#define PR_POSTAL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A15) +#define PR_COMPANY_NAME PROP_TAG( PT_TSTRING, 0x3A16) +#define PR_COMPANY_NAME_W PROP_TAG( PT_UNICODE, 0x3A16) +#define PR_COMPANY_NAME_A PROP_TAG( PT_STRING8, 0x3A16) +#define PR_TITLE PROP_TAG( PT_TSTRING, 0x3A17) +#define PR_TITLE_W PROP_TAG( PT_UNICODE, 0x3A17) +#define PR_TITLE_A PROP_TAG( PT_STRING8, 0x3A17) +#define PR_DEPARTMENT_NAME PROP_TAG( PT_TSTRING, 0x3A18) +#define PR_DEPARTMENT_NAME_W PROP_TAG( PT_UNICODE, 0x3A18) +#define PR_DEPARTMENT_NAME_A PROP_TAG( PT_STRING8, 0x3A18) +#define PR_OFFICE_LOCATION PROP_TAG( PT_TSTRING, 0x3A19) +#define PR_OFFICE_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A19) +#define PR_OFFICE_LOCATION_A PROP_TAG( PT_STRING8, 0x3A19) +#define PR_PRIMARY_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1A) +#define PR_PRIMARY_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1A) +#define PR_PRIMARY_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1A) +#define PR_BUSINESS2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1B) +#define PR_BUSINESS2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1B) +#define PR_BUSINESS2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1B) +#define PR_OFFICE2_TELEPHONE_NUMBER PR_BUSINESS2_TELEPHONE_NUMBER +#define PR_OFFICE2_TELEPHONE_NUMBER_W PR_BUSINESS2_TELEPHONE_NUMBER_W +#define PR_OFFICE2_TELEPHONE_NUMBER_A PR_BUSINESS2_TELEPHONE_NUMBER_A +#define PR_MOBILE_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1C) +#define PR_MOBILE_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1C) +#define PR_MOBILE_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1C) +#define PR_CELLULAR_TELEPHONE_NUMBER PR_MOBILE_TELEPHONE_NUMBER +#define PR_CELLULAR_TELEPHONE_NUMBER_W PR_MOBILE_TELEPHONE_NUMBER_W +#define PR_CELLULAR_TELEPHONE_NUMBER_A PR_MOBILE_TELEPHONE_NUMBER_A +#define PR_RADIO_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1D) +#define PR_RADIO_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1D) +#define PR_RADIO_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1D) +#define PR_CAR_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1E) +#define PR_CAR_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1E) +#define PR_CAR_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1E) +#define PR_OTHER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1F) +#define PR_OTHER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1F) +#define PR_OTHER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1F) +#define PR_TRANSMITABLE_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A20) +#define PR_TRANSMITABLE_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A20) +#define PR_TRANSMITABLE_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A20) +#define PR_PAGER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A21) +#define PR_PAGER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A21) +#define PR_PAGER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A21) +#define PR_BEEPER_TELEPHONE_NUMBER PR_PAGER_TELEPHONE_NUMBER +#define PR_BEEPER_TELEPHONE_NUMBER_W PR_PAGER_TELEPHONE_NUMBER_W +#define PR_BEEPER_TELEPHONE_NUMBER_A PR_PAGER_TELEPHONE_NUMBER_A +#define PR_USER_CERTIFICATE PROP_TAG( PT_BINARY, 0x3A22) +#define PR_PRIMARY_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A23) +#define PR_PRIMARY_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A23) +#define PR_PRIMARY_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A23) +#define PR_BUSINESS_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A24) +#define PR_BUSINESS_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A24) +#define PR_BUSINESS_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A24) +#define PR_HOME_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A25) +#define PR_HOME_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A25) +#define PR_HOME_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A25) +#define PR_COUNTRY PROP_TAG( PT_TSTRING, 0x3A26) +#define PR_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A26) +#define PR_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A26) +#define PR_BUSINESS_ADDRESS_COUNTRY PR_COUNTRY +#define PR_BUSINESS_ADDRESS_COUNTRY_W PR_COUNTRY_W +#define PR_BUSINESS_ADDRESS_COUNTRY_A PR_COUNTRY_A + +#define PR_LOCALITY PROP_TAG( PT_TSTRING, 0x3A27) +#define PR_LOCALITY_W PROP_TAG( PT_UNICODE, 0x3A27) +#define PR_LOCALITY_A PROP_TAG( PT_STRING8, 0x3A27) +#define PR_BUSINESS_ADDRESS_CITY PR_LOCALITY +#define PR_BUSINESS_ADDRESS_CITY_W PR_LOCALITY_W +#define PR_BUSINESS_ADDRESS_CITY_A PR_LOCALITY_A + +#define PR_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A28) +#define PR_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A28) +#define PR_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A28) +#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE PR_STATE_OR_PROVINCE +#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W PR_STATE_OR_PROVINCE_W +#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A PR_STATE_OR_PROVINCE_A + +#define PR_STREET_ADDRESS PROP_TAG( PT_TSTRING, 0x3A29) +#define PR_STREET_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A29) +#define PR_STREET_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A29) +#define PR_BUSINESS_ADDRESS_STREET PR_STREET_ADDRESS +#define PR_BUSINESS_ADDRESS_STREET_W PR_STREET_ADDRESS_W +#define PR_BUSINESS_ADDRESS_STREET_A PR_STREET_ADDRESS_A + +#define PR_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A2A) +#define PR_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A2A) +#define PR_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A2A) +#define PR_BUSINESS_ADDRESS_POSTAL_CODE PR_POSTAL_CODE +#define PR_BUSINESS_ADDRESS_POSTAL_CODE_W PR_POSTAL_CODE_W +#define PR_BUSINESS_ADDRESS_POSTAL_CODE_A PR_POSTAL_CODE_A + + +#define PR_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A2B) +#define PR_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A2B) +#define PR_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A2B) +#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX PR_POST_OFFICE_BOX +#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_W PR_POST_OFFICE_BOX_W +#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_A PR_POST_OFFICE_BOX_A + + +#define PR_TELEX_NUMBER PROP_TAG( PT_TSTRING, 0x3A2C) +#define PR_TELEX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2C) +#define PR_TELEX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2C) +#define PR_ISDN_NUMBER PROP_TAG( PT_TSTRING, 0x3A2D) +#define PR_ISDN_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2D) +#define PR_ISDN_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2D) +#define PR_ASSISTANT_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2E) +#define PR_ASSISTANT_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2E) +#define PR_ASSISTANT_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2E) +#define PR_HOME2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2F) +#define PR_HOME2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2F) +#define PR_HOME2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2F) +#define PR_ASSISTANT PROP_TAG( PT_TSTRING, 0x3A30) +#define PR_ASSISTANT_W PROP_TAG( PT_UNICODE, 0x3A30) +#define PR_ASSISTANT_A PROP_TAG( PT_STRING8, 0x3A30) +#define PR_SEND_RICH_INFO PROP_TAG( PT_BOOLEAN, 0x3A40) + +#define PR_WEDDING_ANNIVERSARY PROP_TAG( PT_SYSTIME, 0x3A41) +#define PR_BIRTHDAY PROP_TAG( PT_SYSTIME, 0x3A42) + + +#define PR_HOBBIES PROP_TAG( PT_TSTRING, 0x3A43) +#define PR_HOBBIES_W PROP_TAG( PT_UNICODE, 0x3A43) +#define PR_HOBBIES_A PROP_TAG( PT_STRING8, 0x3A43) + +#define PR_MIDDLE_NAME PROP_TAG( PT_TSTRING, 0x3A44) +#define PR_MIDDLE_NAME_W PROP_TAG( PT_UNICODE, 0x3A44) +#define PR_MIDDLE_NAME_A PROP_TAG( PT_STRING8, 0x3A44) + +#define PR_DISPLAY_NAME_PREFIX PROP_TAG( PT_TSTRING, 0x3A45) +#define PR_DISPLAY_NAME_PREFIX_W PROP_TAG( PT_UNICODE, 0x3A45) +#define PR_DISPLAY_NAME_PREFIX_A PROP_TAG( PT_STRING8, 0x3A45) + +#define PR_PROFESSION PROP_TAG( PT_TSTRING, 0x3A46) +#define PR_PROFESSION_W PROP_TAG( PT_UNICODE, 0x3A46) +#define PR_PROFESSION_A PROP_TAG( PT_STRING8, 0x3A46) + +#define PR_PREFERRED_BY_NAME PROP_TAG( PT_TSTRING, 0x3A47) +#define PR_PREFERRED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x3A47) +#define PR_PREFERRED_BY_NAME_A PROP_TAG( PT_STRING8, 0x3A47) + +#define PR_SPOUSE_NAME PROP_TAG( PT_TSTRING, 0x3A48) +#define PR_SPOUSE_NAME_W PROP_TAG( PT_UNICODE, 0x3A48) +#define PR_SPOUSE_NAME_A PROP_TAG( PT_STRING8, 0x3A48) + +#define PR_COMPUTER_NETWORK_NAME PROP_TAG( PT_TSTRING, 0x3A49) +#define PR_COMPUTER_NETWORK_NAME_W PROP_TAG( PT_UNICODE, 0x3A49) +#define PR_COMPUTER_NETWORK_NAME_A PROP_TAG( PT_STRING8, 0x3A49) + +#define PR_CUSTOMER_ID PROP_TAG( PT_TSTRING, 0x3A4A) +#define PR_CUSTOMER_ID_W PROP_TAG( PT_UNICODE, 0x3A4A) +#define PR_CUSTOMER_ID_A PROP_TAG( PT_STRING8, 0x3A4A) + +#define PR_TTYTDD_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A4B) +#define PR_TTYTDD_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A4B) +#define PR_TTYTDD_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A4B) + +#define PR_FTP_SITE PROP_TAG( PT_TSTRING, 0x3A4C) +#define PR_FTP_SITE_W PROP_TAG( PT_UNICODE, 0x3A4C) +#define PR_FTP_SITE_A PROP_TAG( PT_STRING8, 0x3A4C) + +#define PR_GENDER PROP_TAG( PT_SHORT, 0x3A4D) + +#define PR_MANAGER_NAME PROP_TAG( PT_TSTRING, 0x3A4E) +#define PR_MANAGER_NAME_W PROP_TAG( PT_UNICODE, 0x3A4E) +#define PR_MANAGER_NAME_A PROP_TAG( PT_STRING8, 0x3A4E) + +#define PR_NICKNAME PROP_TAG( PT_TSTRING, 0x3A4F) +#define PR_NICKNAME_W PROP_TAG( PT_UNICODE, 0x3A4F) +#define PR_NICKNAME_A PROP_TAG( PT_STRING8, 0x3A4F) + +#define PR_PERSONAL_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A50) +#define PR_PERSONAL_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A50) +#define PR_PERSONAL_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A50) + + +#define PR_BUSINESS_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A51) +#define PR_BUSINESS_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A51) +#define PR_BUSINESS_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A51) + +#define PR_CONTACT_VERSION PROP_TAG( PT_CLSID, 0x3A52) +#define PR_CONTACT_ENTRYIDS PROP_TAG( PT_MV_BINARY, 0x3A53) + +#define PR_CONTACT_ADDRTYPES PROP_TAG( PT_MV_TSTRING, 0x3A54) +#define PR_CONTACT_ADDRTYPES_W PROP_TAG( PT_MV_UNICODE, 0x3A54) +#define PR_CONTACT_ADDRTYPES_A PROP_TAG( PT_MV_STRING8, 0x3A54) + +#define PR_CONTACT_DEFAULT_ADDRESS_INDEX PROP_TAG( PT_LONG, 0x3A55) + +#define PR_CONTACT_EMAIL_ADDRESSES PROP_TAG( PT_MV_TSTRING, 0x3A56) +#define PR_CONTACT_EMAIL_ADDRESSES_W PROP_TAG( PT_MV_UNICODE, 0x3A56) +#define PR_CONTACT_EMAIL_ADDRESSES_A PROP_TAG( PT_MV_STRING8, 0x3A56) + + +#define PR_COMPANY_MAIN_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A57) +#define PR_COMPANY_MAIN_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A57) +#define PR_COMPANY_MAIN_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A57) + +#define PR_CHILDRENS_NAMES PROP_TAG( PT_MV_TSTRING, 0x3A58) +#define PR_CHILDRENS_NAMES_W PROP_TAG( PT_MV_UNICODE, 0x3A58) +#define PR_CHILDRENS_NAMES_A PROP_TAG( PT_MV_STRING8, 0x3A58) + + + +#define PR_HOME_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A59) +#define PR_HOME_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A59) +#define PR_HOME_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A59) + +#define PR_HOME_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A5A) +#define PR_HOME_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A5A) +#define PR_HOME_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A5A) + +#define PR_HOME_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A5B) +#define PR_HOME_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A5B) +#define PR_HOME_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A5B) + +#define PR_HOME_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A5C) +#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A5C) +#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A5C) + +#define PR_HOME_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A5D) +#define PR_HOME_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A5D) +#define PR_HOME_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A5D) + +#define PR_HOME_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A5E) +#define PR_HOME_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A5E) +#define PR_HOME_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A5E) + +#define PR_OTHER_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A5F) +#define PR_OTHER_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A5F) +#define PR_OTHER_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A5F) + +#define PR_OTHER_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A60) +#define PR_OTHER_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A60) +#define PR_OTHER_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A60) + +#define PR_OTHER_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A61) +#define PR_OTHER_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A61) +#define PR_OTHER_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A61) + +#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A62) +#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A62) +#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A62) + +#define PR_OTHER_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A63) +#define PR_OTHER_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A63) +#define PR_OTHER_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A63) + +#define PR_OTHER_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A64) +#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A64) +#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A64) + + +/* + * Profile section properties + */ + +#define PR_STORE_PROVIDERS PROP_TAG( PT_BINARY, 0x3D00) +#define PR_AB_PROVIDERS PROP_TAG( PT_BINARY, 0x3D01) +#define PR_TRANSPORT_PROVIDERS PROP_TAG( PT_BINARY, 0x3D02) + +#define PR_DEFAULT_PROFILE PROP_TAG( PT_BOOLEAN, 0x3D04) +#define PR_AB_SEARCH_PATH PROP_TAG( PT_MV_BINARY, 0x3D05) +#define PR_AB_DEFAULT_DIR PROP_TAG( PT_BINARY, 0x3D06) +#define PR_AB_DEFAULT_PAB PROP_TAG( PT_BINARY, 0x3D07) + +#define PR_FILTERING_HOOKS PROP_TAG( PT_BINARY, 0x3D08) +#define PR_SERVICE_NAME PROP_TAG( PT_TSTRING, 0x3D09) +#define PR_SERVICE_NAME_W PROP_TAG( PT_UNICODE, 0x3D09) +#define PR_SERVICE_NAME_A PROP_TAG( PT_STRING8, 0x3D09) +#define PR_SERVICE_DLL_NAME PROP_TAG( PT_TSTRING, 0x3D0A) +#define PR_SERVICE_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x3D0A) +#define PR_SERVICE_DLL_NAME_A PROP_TAG( PT_STRING8, 0x3D0A) +#define PR_SERVICE_ENTRY_NAME PROP_TAG( PT_STRING8, 0x3D0B) +#define PR_SERVICE_UID PROP_TAG( PT_BINARY, 0x3D0C) +#define PR_SERVICE_EXTRA_UIDS PROP_TAG( PT_BINARY, 0x3D0D) +#define PR_SERVICES PROP_TAG( PT_BINARY, 0x3D0E) +#define PR_SERVICE_SUPPORT_FILES PROP_TAG( PT_MV_TSTRING, 0x3D0F) +#define PR_SERVICE_SUPPORT_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D0F) +#define PR_SERVICE_SUPPORT_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D0F) +#define PR_SERVICE_DELETE_FILES PROP_TAG( PT_MV_TSTRING, 0x3D10) +#define PR_SERVICE_DELETE_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D10) +#define PR_SERVICE_DELETE_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D10) +#define PR_AB_SEARCH_PATH_UPDATE PROP_TAG( PT_BINARY, 0x3D11) +#define PR_PROFILE_NAME PROP_TAG( PT_TSTRING, 0x3D12) +#define PR_PROFILE_NAME_A PROP_TAG( PT_STRING8, 0x3D12) +#define PR_PROFILE_NAME_W PROP_TAG( PT_UNICODE, 0x3D12) + +/* + * Status object properties + */ + +#define PR_IDENTITY_DISPLAY PROP_TAG( PT_TSTRING, 0x3E00) +#define PR_IDENTITY_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3E00) +#define PR_IDENTITY_DISPLAY_A PROP_TAG( PT_STRING8, 0x3E00) +#define PR_IDENTITY_ENTRYID PROP_TAG( PT_BINARY, 0x3E01) +#define PR_RESOURCE_METHODS PROP_TAG( PT_LONG, 0x3E02) +#define PR_RESOURCE_TYPE PROP_TAG( PT_LONG, 0x3E03) +#define PR_STATUS_CODE PROP_TAG( PT_LONG, 0x3E04) +#define PR_IDENTITY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3E05) +#define PR_OWN_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x3E06) +#define PR_RESOURCE_PATH PROP_TAG( PT_TSTRING, 0x3E07) +#define PR_RESOURCE_PATH_W PROP_TAG( PT_UNICODE, 0x3E07) +#define PR_RESOURCE_PATH_A PROP_TAG( PT_STRING8, 0x3E07) +#define PR_STATUS_STRING PROP_TAG( PT_TSTRING, 0x3E08) +#define PR_STATUS_STRING_W PROP_TAG( PT_UNICODE, 0x3E08) +#define PR_STATUS_STRING_A PROP_TAG( PT_STRING8, 0x3E08) +#define PR_X400_DEFERRED_DELIVERY_CANCEL PROP_TAG( PT_BOOLEAN, 0x3E09) +#define PR_HEADER_FOLDER_ENTRYID PROP_TAG( PT_BINARY, 0x3E0A) +#define PR_REMOTE_PROGRESS PROP_TAG( PT_LONG, 0x3E0B) +#define PR_REMOTE_PROGRESS_TEXT PROP_TAG( PT_TSTRING, 0x3E0C) +#define PR_REMOTE_PROGRESS_TEXT_W PROP_TAG( PT_UNICODE, 0x3E0C) +#define PR_REMOTE_PROGRESS_TEXT_A PROP_TAG( PT_STRING8, 0x3E0C) +#define PR_REMOTE_VALIDATE_OK PROP_TAG( PT_BOOLEAN, 0x3E0D) + +/* + * Display table properties + */ + +#define PR_CONTROL_FLAGS PROP_TAG( PT_LONG, 0x3F00) +#define PR_CONTROL_STRUCTURE PROP_TAG( PT_BINARY, 0x3F01) +#define PR_CONTROL_TYPE PROP_TAG( PT_LONG, 0x3F02) +#define PR_DELTAX PROP_TAG( PT_LONG, 0x3F03) +#define PR_DELTAY PROP_TAG( PT_LONG, 0x3F04) +#define PR_XPOS PROP_TAG( PT_LONG, 0x3F05) +#define PR_YPOS PROP_TAG( PT_LONG, 0x3F06) +#define PR_CONTROL_ID PROP_TAG( PT_BINARY, 0x3F07) +#define PR_INITIAL_DETAILS_PANE PROP_TAG( PT_LONG, 0x3F08) + +} + +procedure TIdCoderTNEF.DoLog(const AMsg: String; const AAppendSize: Boolean = True); +begin + if AAppendSize then begin + FLog := FLog + IntToStr(FData.Size - FData.Position) + ':' + AMsg + EOL; {Do not localize} + end else begin + FLog := FLog + AMsg + EOL; + end; +end; + +procedure TIdCoderTNEF.DoLogFmt(const AFormat: string; const Args: array of const; AAppendSize: Boolean = True); +begin + DoLog(IndyFormat(AFormat, Args), AAppendSize); +end; + +function TIdCoderTNEF.GetStringForMapiType(AType: Word): string; +begin + case AType of + IdTNEF_PT_UNSPECIFIED: begin + Result := 'Unspecified'; {Do not localize} + end; + IdTNEF_PT_NULL: begin + Result := 'Null'; {Do not localize} + end; + IdTNEF_PT_I2: begin + Result := 'Short'; {Do not localize} + end; + IdTNEF_PT_LONG: begin + Result := 'Long'; {Do not localize} + end; + IdTNEF_PT_R4: begin + Result := 'Float'; {Do not localize} + end; + IdTNEF_PT_DOUBLE: begin + Result := 'Double'; {Do not localize} + end; + IdTNEF_PT_CURRENCY: begin + Result := 'Currency'; {Do not localize} + end; + IdTNEF_PT_APPTIME: begin + Result := 'Application time'; {Do not localize} + end; + IdTNEF_PT_ERROR: begin + Result := 'Error code'; {Do not localize} + end; + IdTNEF_PT_BOOLEAN: begin + Result := 'Boolean'; {Do not localize} + end; + IdTNEF_PT_OBJECT: begin + Result := 'Object'; {Do not localize} + end; + IdTNEF_PT_I8: begin + Result := '64-bit integer'; {Do not localize} + end; + IdTNEF_PT_STRING8: begin + Result := 'String8'; {Do not localize} + end; + IdTNEF_PT_UNICODE: begin + Result := 'Unicode'; {Do not localize} + end; + IdTNEF_PT_SYSTIME: begin + Result := 'SysTime'; {Do not localize} + end; + IdTNEF_PT_CLSID: begin + Result := 'ClsId'; {Do not localize} + end; + IdTNEF_PT_BINARY: begin + Result := 'Binary'; {Do not localize} + end; + else + Result := 'Unknown'; {Do not localize} + end; +end; + +function TIdCoderTNEF.GetStringForType(AType: Word): string; +begin + case AType of + IdTNEFAtpTriples: begin + Result := 'Triples'; {Do not localize} + end; + IdTNEFAtpString: begin + Result := 'String'; {Do not localize} + end; + IdTNEFAtpText: begin + Result := 'Text'; {Do not localize} + end; + IdTNEFAtpDate: begin + Result := 'Date'; {Do not localize} + end; + IdTNEFAtpShort: begin + Result := 'Short'; {Do not localize} + end; + IdTNEFAtpLong: begin + Result := 'Long'; {Do not localize} + end; + IdTNEFAtpByte: begin + Result := 'Byte'; {Do not localize} + end; + IdTNEFAtpWord: begin + Result := 'Word'; {Do not localize} + end; + IdTNEFAtpDWord: begin + Result := 'DWord'; {Do not localize} + end; + IdTNEFAtpMax: begin + Result := 'Max'; {Do not localize} + end; + else + Result := 'Unknown'; {Do not localize} + end; +end; + +function TIdCoderTNEF.GetStringForAttribute(AAttribute: Word): string; +begin + case AAttribute of + IdTNEFattNull: begin + Result := 'Null'; {Do not localize} + end; + IdTNEFattFrom: begin + Result := 'From'; {Do not localize} + end; + IdTNEFattSubject: begin + Result := 'Subject'; {Do not localize} + end; + IdTNEFattDateSent: begin + Result := 'DateSent'; {Do not localize} + end; + IdTNEFattDateRecd: begin + Result := 'DateRecd'; {Do not localize} + end; + IdTNEFattMessageStatus: begin + Result := 'MessageStatus'; {Do not localize} + end; + IdTNEFattMessageClass: begin + Result := 'MessageClass'; {Do not localize} + end; + IdTNEFattMessageID: begin + Result := 'MessageID'; {Do not localize} + end; + IdTNEFattParentID: begin + Result := 'ParentID'; {Do not localize} + end; + IdTNEFattConversationID: begin + Result := 'ConversationID'; {Do not localize} + end; + IdTNEFattPriority: begin + Result := 'Priority'; {Do not localize} + end; + IdTNEFattAttachData: begin + Result := 'AttachData'; {Do not localize} + end; + IdTNEFattAttachTitle: begin + Result := 'AttachTitle'; {Do not localize} + end; + IdTNEFattAttachMetaFile: begin + Result := 'AttachMetaFile'; {Do not localize} + end; + IdTNEFattAttachCreateDate: begin + Result := 'AttachCreateDate'; {Do not localize} + end; + IdTNEFattAttachModifyDate: begin + Result := 'AttachModifyDate'; {Do not localize} + end; + IdTNEFattDateModified: begin + Result := 'DateModified'; {Do not localize} + end; + IdTNEFattAttachTransportFilename: begin + Result := 'AttachTransportFilename'; {Do not localize} + end; + IdTNEFattAttachRenddata: begin + Result := 'AttachRenddata'; {Do not localize} + end; + IdTNEFattMAPIProps: begin + Result := 'MAPIProps'; {Do not localize} + end; + IdTNEFattRecipTable: begin + Result := 'RecipTable'; {Do not localize} + end; + IdTNEFattAttachment: begin + Result := 'Null'; {Do not localize} + end; + IdTNEFattTnefVersion: begin + Result := 'TnefVersion'; {Do not localize} + end; + IdTNEFattOemCodepage: begin + Result := 'OemCodepage'; {Do not localize} + end; + IdTNEFattOriginalMessageClass: begin + Result := 'OriginalMessageClass'; {Do not localize} + end; + //IdTNEFattOwner: begin + // Result := 'Owner'; {Do not localize} + //end; + IdTNEFattSentFor: begin + Result := 'SentFor'; {Do not localize} + end; + IdTNEFattDelegate: begin + Result := 'Delegate'; {Do not localize} + end; + //IdTNEFattDateStart: begin + // Result := 'DateStart'; {Do not localize} + //end; + IdTNEFattDateEnd: begin + Result := 'DateEnd'; {Do not localize} + end; + IdTNEFattAidOwner: begin + Result := 'OwnerAID'; {Do not localize} + end; + IdTNEFattRequestRes: begin + Result := 'ResponseRequested'; {Do not localize} + end; + else + Result := 'Unknown'; {Do not localize} + end; +end; + +class function TIdCoderTNEF.IsFilenameTnef(const AFilename: string): Boolean; +begin + Result := IdCoderTNEF.IsFilenameTnef(AFilename); +end; + +function IsFilenameTnef(const AFilename: string): Boolean; +begin + if TextIsSame(AFilename, 'winmail.dat') then begin + Result := True; + end + else if TextStartsWith(AFilename, 'att') and TextEndsWith(AFilename, '.dat') then begin + Result := IndyStrToInt(Copy(AFilename, 4, Length(AFilename)-7), -1) > -1; + end else begin + Result := False; + end; +end; + +function TIdCoderTNEF.GetMultipleUnicodeOrString8String(AType: Word): TIdUnicodeString; +var + LIndex, LCount: LongWord; +begin + //Usually this will only contain one string, but if there are more, return + //them as a single string concatenated with semicolons. + LCount := GetLongWord; + if FDoLogging then begin + DoLogFmt(' Found %d %s String(s):', [LCount, GetStringForMapiType(AType)]); {Do not localize} + end; + if LCount = 0 then begin //Very unlikely, just paranoia + Result := ''; + Exit; + end; + Result := GetUnicodeOrString8String(AType); + for LIndex := 2 to LCount do begin + Result := ';' + GetUnicodeOrString8String(AType); {Do not localize} + end; +end; + +function TIdCoderTNEF.GetUnicodeOrString8String(AType: Word): TIdUnicodeString; +var + LLength: LongWord; + LsTemp: AnsiString; + LBuf: TIdBytes; +begin + Result := ''; + LLength := GetLongWord; + if LLength = 0 then begin + Exit; + end; + //Note the length count includes a terminating null. + case AType of + IdTNEF_PT_UNICODE: begin + LBuf := GetBytes(LLength); + SetString(Result, PWideChar(LBuf), (LLength div SizeOf(TIdWideChar))-1); + end; + IdTNEF_PT_STRING8: begin + LBuf := GetBytes(LLength); + // TODO: use the value from the attOemCodepage attribute to decode the data: + // Result := IndyTextEncoding(attOemCodepage).GetString(LBuf, 0, Length(LBuf)-1); + SetString(LsTemp, PAnsiChar(LBuf), LLength-1); + Result := TIdUnicodeString(LsTemp); + end; + else begin + Skip(LLength); + end; + end; + if FDoLogging then begin + DoLog(' Found string value: ' + Result); {Do not localize} + end; + //Note the strings are padded to 4-byte boundaries... + if (LLength mod 4) > 0 then begin + Skip(4 - (LLength mod 4)); + end; +end; + +function TIdCoderTNEF.PadWithZeroes(const AStr: string; ACount: integer): string; +begin + if Length(AStr) < ACount then begin + Result := StringOfChar('0', ACount-Length(AStr)) + AStr; + end else begin + Result := AStr; + end; +end; + +function TIdCoderTNEF.GetByteAsChar(AByte: Byte): Char; +begin + //Return a displayable char or '.' if not displayable. + if (Ord(AByte) > 31) and (Ord(AByte) < 127) then begin + Result := Chr(AByte); + end else begin + Result := '.'; {Do not localize} + end; +end; + +function TIdCoderTNEF.GetByteAsHexString(AByte: Byte): string; +var + LsTemp: string; +begin + LsTemp := IndyFormat('%x', [AByte]); {Do not localize} + Result := PadWithZeroes(LsTemp, 2); +end; + +function TIdCoderTNEF.GetByteAsHexString: string; +var + LnTemp: Byte; + LsTemp: string; +begin + LnTemp := GetByte; + LsTemp := IndyFormat('%x', [LnTemp]); {Do not localize} + Result := PadWithZeroes(LsTemp, 2); +end; + +function TIdCoderTNEF.GetBytesAsHexString(ACount: integer): string; +var + i: integer; +begin + Result := ''; + for i := 0 to ACount-1 do begin + Result := Result + GetByteAsHexString + ' '; {Do not localize} + end; +end; + +function TIdCoderTNEF.GetByte: Byte; +var + LTemp: TIdBytes; +begin + LTemp := GetBytes(SizeOf(Byte)); + Result := LTemp[0]; +end; + +function TIdCoderTNEF.GetBytes(ALength: Integer; APeek: Boolean = False): TIdBytes; +var + LPos: TIdStreamSize; +begin + Result := nil; + CheckForEof(ALength); + LPos := FData.Position; + try + //Note the length count includes a terminating null. + ReadTIdBytesFromStream(FData, Result, ALength); + finally + if APeek then begin + FData.Position := LPos; + end; + end; +end; + +function TIdCoderTNEF.GetWord: Word; +var + LTemp: TIdBytes; +begin + LTemp := GetBytes(SizeOf(Word)); + Result := PWord(LTemp)^; +end; + +function TIdCoderTNEF.GetLongWord: LongWord; +var + LTemp: TIdBytes; +begin + LTemp := GetBytes(SizeOf(LongWord)); + Result := PLongWord(LTemp)^; +end; + +function TIdCoderTNEF.GetInt64: Int64; +var + LTemp: TIdBytes; +begin + LTemp := GetBytes(SizeOf(Int64)); + Result := PInt64(@LTemp[0])^; +end; + +function TIdCoderTNEF.GetString(ALength: Word): string; +begin + if ALength > 0 then begin + Result := ReadStringFromStream(FData, ALength-1, IndyTextEncoding_8Bit{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); + Skip(1) //Skip terminating null + end else begin + Result := ''; + end; +end; + +function TIdCoderTNEF.GetDate(ALength: Word): TDateTime; +var + LYear, LMonth, LDay, LHour, LMinute, LSecond: Word; +begin + LYear := GetWord; + LMonth := GetWord; + LDay := GetWord; + LHour := GetWord; + LMinute := GetWord; + LSecond := GetWord; + Skip(SizeOf(Word)); //Day-of-week + Result := EncodeDateTime(LYear, LMonth, LDay, LHour, LMinute, LSecond, 0); +end; + +procedure TIdCoderTNEF.Skip(ACount: integer); +begin + CheckForEof(ACount); + FData.Seek(ACount, soFromCurrent); +end; + +procedure TIdCoderTNEF.Checksum(ANumBytesToCheck: integer); +var + LChecksum: Word; + i: integer; + LBytes: TIdBytes; + LTNEFChecksum: Word; +begin + //Do a checksum on ANumBytesToCheck bytes from the current position. + //Compare to the recorded TNEF value in the word after these bytes. + //DONT move our stream pointer forward. + LBytes := GetBytes(ANumBytesToCheck + SizeOf(Word), True); + LChecksum := 0; + for i := 0 to ANumBytesToCheck-1 do begin + Inc(LChecksum, LBytes[i]); + end; + LTNEFChecksum := PWord(@LBytes[ANumBytesToCheck])^; + if LChecksum <> LTNEFChecksum then begin + raise EIdTnefChecksumFailure.Create('Checksum failure - TNEF is corrupt or truncated'); {Do not localize} + end; +end; + +procedure TIdCoderTNEF.CheckForEof(ANumBytesRequested: integer); +begin + //See if you have enough bytes left to satisfy the request for nNumBytesRequested... + if (FData.Size - FData.Position) < TIdStreamSize(ANumBytesRequested) then begin + raise EIdTnefRanOutOfBytes.Create('Hit end of file prematurely - TNEF is corrupt or truncated'); {Do not localize} + end; +end; + +procedure TIdCoderTNEF.Parse(const AIn: string; AMsg: TIdMessage; ALog: Boolean = False); +var + LIn: TMemoryStream; +begin + LIn := TMemoryStream.Create; + try + WriteStringToStream(LIn, AIn, IndyTextEncoding_8Bit{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); + LIn.Position := 0; + Parse(LIn, AMsg, ALog); + finally + LIn.Free; + end; +end; + +procedure TIdCoderTNEF.Parse(const AIn: TIdAttachment; AMsg: TIdMessage; ALog: Boolean = False); +var + LTempStream: TStream; +begin + LTempStream := AIn.OpenLoadStream; + try + Parse(LTempStream, AMsg, ALog); + finally + AIn.CloseLoadStream; + end; +end; + +procedure TIdCoderTNEF.Parse(const AIn: TIdBytes; AMsg: TIdMessage; ALog: Boolean = False); +var + LIn: TMemoryStream; +begin + LIn := TMemoryStream.Create; + try + WriteTIdBytesToStream(LIn, AIn); + LIn.Position := 0; + Parse(LIn, AMsg, ALog); + finally + LIn.Free; + end; +end; + +procedure TIdCoderTNEF.ParseMessageBlock; +var + LType, LAttribute: Word; +begin + LAttribute := GetWord; + LType := GetWord; + ParseAttribute(LAttribute, LType); +end; + +procedure TIdCoderTNEF.IsCurrentAttachmentValid; +begin + if FCurrentAttachment = nil then begin + raise EIdTnefCurrentAttachmentInvalid.Create('Attempt to access invalid attachment - invalid TNEF missing attAttachRenddata attribute at start of attachment?'); {Do not localize} + end; +end; + +function TIdCoderTNEF.GetAttributeString(const AAttributeName: string; AType: Word): string; +var + LLength: LongWord; +begin + LLength := GetLongWord; + if FDoLogging then begin + DoLogFmt(' ParseAttachmentBlock found %s type, length: %d', [AAttributeName, LLength]); {Do not localize} + end; + if AType <> IdTNEFAtpString then begin + raise EIdTnefAttributeUnexpectedType.Create(AAttributeName + ' not a String'); {Do not localize} + end; + Checksum(LLength); + Result := GetString(LLength); + Skip(2); //Checksum +end; + +procedure TIdCoderTNEF.ParseAttachmentBlock; +var + LType, LAttribute: Word; + LLength: LongWord; + LDestStream: TStream; +begin + LAttribute := GetWord; + LType := GetWord; + if FDoLogging then begin + DoLogFmt(' ParseAttachmentBlock passed a %s type %s', [GetStringForAttribute(LAttribute), GetStringForType(LType)]); {Do not localize} + end; + case LAttribute of + IdTNEFattAttachRenddata: begin + //Per Microsoft, you get this first, at the start of every attachment, + //create a new attachment when you encounter this. + LLength := GetLongWord; + if FDoLogging then begin + DoLog(' ParseAttachmentBlock found IdTNETattAttachRenddata type, length: ' + IntToStr(LLength)); {Do not localize} + end; + Checksum(LLength); + Skip(LLength); + Skip(2); //Checksum + if FDoLogging then begin + DoLog(' Adding attachment to decoded message.'); {Do not localize} + end; + FMsg.DoCreateAttachment(nil, FCurrentAttachment); + FCurrentAttachment.ParentPart := -1; + end; + IdTNEFattAttachTitle: begin + //This is the filename of the attachment, set the already-created attachment's + //filename to this. + IsCurrentAttachmentValid; + FCurrentAttachment.FileName := GetAttributeString('IdTNEFattAttachTitle', LType); {Do not localize} + if FDoLogging then begin + DoLog(' ParseAttachmentBlock parsed attachment filename: ' + FCurrentAttachment.FileName); {Do not localize} + end; + end; + IdTNEFattAttachData: begin + //This is the attachment file contents, set it for the already-created + //attachment. + LLength := GetLongWord; + if FDoLogging then begin + DoLog(' ParseAttachmentBlock found IdTNEFattAttachData type, length: ' + IntToStr(LLength)); {Do not localize} + end; + if LType <> IdTNEFAtpByte then begin + raise EIdTnefAttributeUnexpectedType.Create('TNEF AttachmentData not a Byte'); {Do not localize} + end; + Checksum(LLength); + if LLength > 0 then begin + IsCurrentAttachmentValid; + LDestStream := FCurrentAttachment.PrepareTempStream; + try + LDestStream.CopyFrom(FData, LLength); + finally + FCurrentAttachment.FinishTempStream; + end; + end; + if FDoLogging then begin + DoLogFmt(' ParseAttachmentBlock copied %d bytes to attachment.', [LLength]); {Do not localize} + end; + Skip(2); //Checksum + end; + else + if FDoLogging then begin + DoLogFmt(' ParseAttachmentBlock found unknown attribute: %d, type: %d, passing to ParseAttribute.', [LAttribute, LType]); {Do not localize} + end; + ParseAttribute(LAttribute, LType); + end; +end; + +function TIdCoderTNEF.GetMapiBoolean(AType: Word; const AText: string): Smallint; +begin + if AType <> IdTNEF_PT_BOOLEAN then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected Boolean for %s', [AText]); {Do not localize} + end; + Result := GetWord; + Skip(SizeOf(Word)); //Skip next two bytes (padded to 4 bytes) + if FDoLogging then begin + DoLogFmt(' ParseMapiProp found %s Boolean, value: %d', [AText, Result]); {Do not localize} + end; +end; + +function TIdCoderTNEF.GetMapiLong(AType: Word; const AText: string): Longint; +begin + if AType <> IdTNEF_PT_LONG then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected Long for %s', [AText]); {Do not localize} + end; + Result := GetLongWord; + if FDoLogging then begin + DoLogFmt(' ParseMapiProp found %s Long, value: %d', [AText, Result]); {Do not localize} + end; +end; + +function TIdCoderTNEF.GetMapiSysTime(AType: Word; const AText: string): TDateTime; +var + LHour, LMinute, LSecond, LMilliSecond: Word; + LVal: Int64; + LTime: Double; +begin + //MAPI's SysTime is a 64-bit integer holding the number of 100ns intervals + //since 1st Jan 1601. + if AType <> IdTNEF_PT_SYSTIME then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected SysTime for %s', [AText]); {Do not localize} + end; + LVal := GetInt64; + //I am sure there is a better way of doing the following... + LVal := LVal div 10; //Ditch the 100ns + LVal := LVal div 1000; //Ditch the ms + LMilliSecond := LVal mod 1000; + LVal := LVal div 1000; + LSecond := LVal mod 60; + LVal := LVal div 60; + LMinute := LVal mod 60; + LVal := LVal div 60; + LHour := LVal mod 24; + LVal := LVal div 24; + //LVal is now the days since 1/1/1601. Subtract Delphi's 300-year offset... + Result := LVal; + Result := Result + EncodeDate(1601, 1, 1); {Do not localize} + //Is the hour out by 1 or is it WET vs GMT time? Or is it GetDate that is the hour out? + LTime := ((((((LHour*60)+LMinute)*60)+LSecond)*1000)+LMilliSecond)/(24*60*60*1000); + Result := Result + LTime; + if FDoLogging then begin + DoLogFmt(' ParseMapiProp found %s SysTime, value: %s', [AText, DateTimeToStr(Result)]); {Do not localize} + end; +end; + +function TIdCoderTNEF.GetMapiStrings(AType: Word; const AText: string): string; +begin + //May be PT_UNICODE or PT_STRING8 (PT_TSTRING will be aliased to one of these)... + if (AType <> IdTNEF_PT_UNICODE) and (AType <> IdTNEF_PT_STRING8) then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected Unicode or String8 for %s', [AText]); {Do not localize} + end; + Result := GetMultipleUnicodeOrString8String(AType); + if FDoLogging then begin + DoLogFmt(' ParseMapiProp found %s String, value: %s', [AText, Result]); {Do not localize} + end; +end; + +{ GetMapiObject was previously needed, may be needed later in development... +function TIdCoderTNEF.GetMapiObject(AType: Word; const AText: string): TIdBytes; +begin + if AType <> IdTNEF_PT_OBJECT then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected Object for %s', [AText]); {Do not localize} +{ end; + Result := GetMapiItemAsBytes(AType, AText); +end; +} + +function TIdCoderTNEF.GetMapiBinaryAsString(AType: Word; const AText: string): string; +var + LBinary: TIdBytes; + LStrLen: integer; + LIndex: integer; +begin + //You MUST know that the binary data is really a null-terminated string. + LBinary := GetMapiBinary(AType, AText); + LStrLen := Length(LBinary)-1; + Result := ''; + for LIndex := 0 to LStrLen-1 do begin + Result := Result + Chr(LBinary[LIndex]); + end; +end; + +function TIdCoderTNEF.GetMapiBinaryAsEmailName(AType: Word; const AText: string): string; +begin + Result := GetMapiBinaryAsString(AType, AText); + //If it starts SMTP: then remove SMTP:, but leave anything else (e.g. FAX:) + if TextStartsWith(Result, 'SMTP:') then begin {Do not localize} + Result := Trim(Copy(Result, 6, MaxInt)); + end; + Result := LowerCase(Result); +end; + +function TIdCoderTNEF.GetMapiBinary(AType: Word; const AText: string): TIdBytes; +begin + if AType <> IdTNEF_PT_BINARY then begin + raise EIdTnefUnexpectedType.CreateFmt('Expected Binary for %s', [AText]); {Do not localize} + end; + Result := GetMapiItemAsBytesPossiblyCompressed(AType, AText); +end; + +function TIdCoderTNEF.GetMapiItemAsBytesPossiblyCompressed(AType: Word; const AText: string): TIdBytes; +var + LCount, LLength: LongWord; + LMagicNumber: LongWord; + LCompressedSize, LUncompressedSize: LongWord; + LPos: TIdStreamSize; +begin + SetLength(Result, 0); + LCount := GetLongWord; + if FDoLogging then begin + DoLogFmt(' Found %d %s:', [LCount, GetStringForMapiType(AType)]); {Do not localize} + end; + if LCount = 0 then begin //Very unlikely, just paranoia + Exit; + end; + if LCount <> 1 then begin + raise EIdTnefNotSupported.Create('Binary/Object not supported with a count > 1'); {Do not localize} + end; + LLength := GetLongWord; + if LLength >= 12 then begin + //Peek ahead to see if it has an optional magic number, indicating that it + //has another header here. + //If it has a valid magic number, then the next long is the + //uncompressed size, then the compressed size, next is the magic (long) number, + //next is a CRC. + //We initially only want to see if it has a magic number... + LPos := FData.Position; + try + Skip(8); + LMagicNumber := GetLongWord; + finally + FData.Position := LPos; + end; + if LMagicNumber = $414C454D then begin + //It has a header, but this magic number means it is NOT compressed. + //Note: I have never seen this option existing in reality. + LCompressedSize := GetLongWord; + LUncompressedSize := GetLongWord; + Skip(SizeOf(LongWord)); //Magic word + Skip(SizeOf(LongWord)); //Checksum, ignore this, this block was crc-checked already + if FDoLogging then begin + DoLogFmt(' Is uncompressed, uncompressed size %d, compressed size %d', {Do not localize} + [LUncompressedSize, LCompressedSize]); + end; + Result := InternalGetMapiItemAsBytes(LCount, LLength-16, AType, AText); + Exit; + end + else if LMagicNumber = $75465A4C then begin + //It is compressed. Decompress it. + Result := DecompressRtf(LCount, LLength, AType, AText); + Exit; + end; + end; + //Not compressed (or not compressed in a format we recognise)... + Result := InternalGetMapiItemAsBytes(LCount, LLength, AType, AText); +end; + +function TIdCoderTNEF.DecompressRtf(ACount, ALength: LongWord; AType: Word; const AText: string): TIdBytes; +var + LCompressedSize, LUncompressedSize: LongWord; + LData: TIdBytes; + LInIndex, LOutIndex: LongWord; + LFlags: Byte; + LShifts: integer; + LFlag: Byte; + LCodePosition: integer; + LCodeLength: integer; + LDecodeString: string; + LDecodeStringLength: LongWord; + LTemp: Integer; + LOutBufferSize: LongWord; +begin + //Read header... + LCompressedSize := GetLongWord; //Length AFTER this field (LLength - 4) + LUncompressedSize := GetLongWord; + GetLongWord; //Magic number + GetLongWord; //Checksum, ignore this, this block was crc-checked already + if FDoLogging then begin + DoLogFmt(' Is compressed, uncompressed size %d, compressed size %d', {Do not localize} + [LUncompressedSize, LCompressedSize]); + end; + //Get the compressed bytes... + LData := InternalGetMapiItemAsBytes(ACount, ALength-16, AType, AText); + LDecodeStringLength := Length(IdTNEF_decode_string); + LOutBufferSize := LDecodeStringLength + LUncompressedSize; + SetLength(Result, LOutBufferSize); + //Copy the preload decode string into the output... + LDecodeString := IdTNEF_decode_string; + for LTemp := 0 to LDecodeStringLength-1 do begin + Result[LTemp] := Byte(LDecodeString[LTemp+1]); + end; + LInIndex := 0; + LOutIndex := LDecodeStringLength; + Dec(ALength, 16); //Adjust for the header. + LShifts := 8; //Force an initial load of the first flag byte + LFlags := 0; //Stop warning + while LInIndex < ALength do begin + //This scheme blocks that contain a starting byte of eight flags followed by + //eight 1 or 2-byte entries. If the flag is 0, its entry is a single + //literal byte, if 1 then its entry is a two-byte compression code. + if LShifts = 8 then begin + LFlags := LData[LInIndex]; + LShifts := 0; + Inc(LInIndex); + end; + LFlag := LFlags and 1; + LFlags := LFlags shr 1; + Inc(LShifts); + if LFlag = 0 then begin + //A single literal byte... + Result[LOutIndex] := LData[LInIndex]; + Inc(LInIndex); + Inc(LOutIndex); + end else begin + //A two-byte code telling us that the bytes we want to output are + //a copy of bytes that were previously outputted. The position of + //the previous bytes is the first three nibbles, the number of + //bytes to copy is the last nibble. + LCodePosition := LData[LInIndex]; + Inc(LInIndex); + LCodeLength := LData[LInIndex]; + Inc(LInIndex); + LCodePosition := LCodePosition shl 4; + LCodePosition := LCodePosition or (LCodeLength shr 4); + LCodeLength := LCodeLength and $F; //The low nibble + //Since repetitions of 0 or 1 byte would be a waste of time, the + //length runs from 2 to 17 instead of 0 to 15... + LCodeLength := LCodeLength + 2; + //LCodePosition points to the byte sequence we are to copy from the + //previously-decoded output data. The output data is viewed as a + //4096-byte circular buffer into which LCodePosition points. It is + //further complicated by the fact that the buffer is preloaded with + //the string IdTNEF_decode_string. Rather than using a real buffer, + //we just calculate the corresponding position in the output data. + LCodePosition := ((Integer(LOutIndex) div 4096) * 4096) + LCodePosition; + //The flag byte always has 8 bits, but at the end of the data, the + //last bits may be padding. In this case, both LOutPosition and + //LOutIndex will equal LOutBufferSize (1 byte past our output). + //If we don't filter them out, we will get an access violation. + if LOutIndex >= LOutBufferSize then begin + if FDoLogging then begin + DoLogFmt(' Ignoring EOD padding: %d bytes from offset %d to %d destlen %d', {Do not localize} + [LCodeLength, LCodePosition, LOutIndex, LOutBufferSize], + False); + end; + end else begin + if LCodePosition >= Integer(LOutIndex) then begin + //The buffer is supposed to be a circular buffer. Since we + //made it a linear buffer, we need to wrap around... + LCodePosition := LCodePosition - 4096; + end; + if LCodePosition < 0 then begin + //This should never happen, would cause an AV... + raise EIdTnefCorruptData.Create('Corrupt compressed rtf: negative code position'); {Do not localize} + end; + if FDoLogging then begin + DoLogFmt(' Copying %d bytes from offset %d to %d destlen %d', {Do not localize} + [LCodeLength, LCodePosition, LOutIndex, LOutBufferSize], False); + end; + for LTemp := 0 to LCodeLength-1 do begin + Result[LOutIndex] := Result[LCodePosition+LTemp]; //GPFs here + Inc(LOutIndex); + end; + end; + end; + end; + //Remove the decode string from the output... + for LOutIndex := 0 to LUncompressedSize-1 do begin + Result[LOutIndex] := Result[LOutIndex+LDecodeStringLength]; + end; + SetLength(Result, LUncompressedSize); + if FDoLogging then begin + DoLog(' Uncompressed bytes:'); {Do not localize} + DumpBytes(Result); + end; +end; + +function TIdCoderTNEF.GetMapiItemAsBytes(AType: Word; const AText: string): TIdBytes; +var + LCount, LLength: LongWord; +begin + SetLength(Result, 0); + LCount := GetLongWord; + if FDoLogging then begin + DoLogFmt(' Found %d %s:', [LCount, GetStringForMapiType(AType)]); {Do not localize} + end; + if LCount = 0 then begin //Very unlikely, just paranoia + Exit; + end; + if LCount <> 1 then begin + raise EIdTnefNotSupported.Create('Binary/Object not supported with a count > 1'); {Do not localize} + end; + LLength := GetLongWord; + Result := InternalGetMapiItemAsBytes(LCount, LLength, AType, AText); +end; + +function TIdCoderTNEF.InternalGetMapiItemAsBytes(ACount, ALength: LongWord; AType: Word; const AText: string): TIdBytes; +var + LPos: TIdStreamSize; +begin + if FDoLogging then begin + DoLogFmt(' Item had %d bytes.', [ALength]); {Do not localize} + end; + LPos := FData.Position; + Result := GetBytes(ALength); + if FDoLogging then begin + DumpBytes(Result); + end; + //Note the bytes are padded to 4-byte boundaries... + if (ALength mod 4) > 0 then begin + ALength := ((ALength div 4) + 1) * 4; + end; + FData.Position := LPos + ALength; + if FDoLogging then begin + DoLogFmt(' ParseMapiProp found %s Bytes, count: %d', [AText, ACount]); {Do not localize} + end; +end; + +procedure TIdCoderTNEF.DumpBytes(const ABytes: TIdBytes); +var + LIndex: integer; + LLHS, LRHS: string; +begin + LLHS := ''; + LRHS := ''; + for LIndex := 0 to Length(ABytes)-1 do begin + LLHS := LLHS + GetByteAsHexString(ABytes[LIndex])+' '; {Do not localize} + LRHS := LRHS + GetByteAsChar(ABytes[LIndex]); + if ((LIndex+1) mod 16) = 0 then begin + DoLog(' ' + LLHS + ' ' + LRHS, False); {Do not localize} + LLHS := ''; + LRHS := ''; + end; + end; + if LLHS <> '' then begin + while Length(LLHS) < 48 do begin + LLHS := LLHS + ' '; {Do not localize} + end; + DoLog(' ' + LLHS + ' ' + LRHS, False); {Do not localize} + end; +end; + +procedure TIdCoderTNEF.ParseMapiProp; +var + LType, LAttribute: Word; + LLength, LCount, I: LongWord; + //LGUIDType: LongWord; + LShort: Smallint; + LStr: string; + LGUID: string; + LTextPart: TIdText; + LDate: TDateTime; +begin + LType := GetWord; + LAttribute := GetWord; + //Initially, just parse out the common attributes and the ones we are interested in. + if LAttribute >= $8000 then begin + //A named property: this has a GUID and some other optional stuff... + LGUID := GetBytesAsHexString(16); + if FDoLogging then begin + DoLog(' MAPI item has a named property, GUID: ' + LGUID); {Do not localize} + end; + LLength := GetLongWord; + if LLength = 0 then begin + //In this case, the named property uses an identifier... + //TODO: What is the LGUIDType below? + {LGUIDType :=} GetLongWord; + end else begin + //In this case, the named property uses a string... + //TODO: Following code not tested... + //Skip strings for now + LCount := LLength; + for I := 1 to LCount do begin + LLength := GetLongWord; + if LLength = 0 then begin + Continue; + end; + Skip(LLength); + //Note the strings are padded to 4-byte boundaries... + if (LLength mod 4) > 0 then begin + Skip(4 - (LLength mod 4)); + end; + end; + end; + end; + case LAttribute of + IdTNEF_PR_ALTERNATE_RECIPIENT_ALLOWED: begin + {LShort := } GetMapiBoolean(LType, 'ALTERNATE_RECIPIENT_ALLOWED'); {Do not localize} + end; + IdTNEF_PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED: begin + //A delivery receipt, not supported by most systems, implement as a read receipt + LShort := GetMapiBoolean(LType, 'ORIGINATOR_DELIVERY_REPORT_REQUESTED'); {Do not localize} + if LShort > 0 then begin + //Have we already parsed the sender? + if FMsg.From.Address <> '' then begin + FMsg.ReceiptRecipient.Address := FMsg.From.Address; + end else begin + FReceiptRequested := True; + end; + if FDoLogging then begin + DoLog(' Delivery receipt requested.'); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' Delivery receipt not requested.'); {Do not localize} + end; + end; + end; + IdTNEF_PR_PRIORITY: begin + {LLong := } GetMapiLong(LType, 'PRIORITY'); {Do not localize} + end; + IdTNEF_PR_READ_RECEIPT_REQUESTED: begin + LShort := GetMapiBoolean(LType, 'READ_RECEIPT_REQUESTED'); {Do not localize} + if LShort > 0 then begin + //Have we already parsed the sender? + if FMsg.From.Address <> '' then begin + FMsg.ReceiptRecipient.Address := FMsg.From.Address; + end else begin + FReceiptRequested := True; + end; + if FDoLogging then begin + DoLog(' Read receipt requested.'); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' Read receipt not requested.'); {Do not localize} + end; + end; + end; + IdTNEF_PR_ORIGINAL_SENSITIVITY: begin + {LLong := } GetMapiLong(LType, 'ORIGINAL_SENSITIVITY'); {Do not localize} + end; + IdTNEF_PR_SENSITIVITY: begin + {LLong := } GetMapiLong(LType, 'SENSITIVITY'); {Do not localize} + end; + IdTNEF_PR_SUBJECT_PREFIX: begin + {LStr := } GetMapiStrings(LType, 'SUBJECT_PREFIX'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_AUTHOR_NAME: begin + {LStr := } GetMapiStrings(LType, 'ORIGINAL_AUTHOR_NAME'); {Do not localize} + end; + IdTNEF_PR_CONVERSATION_TOPIC: begin + LStr := GetMapiStrings(LType, 'CONVERSATION_TOPIC'); {Do not localize} + if FMsg.Subject = '' then begin //Only use this as subject if did not find subject at a higher level + FMsg.Subject := LStr; + if FDoLogging then begin + DoLog(' Message has subject (from CONVERSATION_TOPIC): ' + LStr); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' CONVERSATION_TOPIC ignored, already have subject. CONVERSATION_TOPIC: ' + LStr); {Do not localize} + end; + end; + end; + IdTNEF_PR_CLIENT_SUBMIT_TIME: begin + LDate := GetMapiSysTime(LType, 'CLIENT_SUBMIT_TIME'); {Do not localize} + if FMsg.Date = 0 then begin //Only use this as date if did not find date at a higher level + FMsg.Date := LDate; + if FDoLogging then begin + DoLog(' Message has date (from CLIENT_SUBMIT_TIME): ' + DateTimeToStr(LDate)); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' CLIENT_SUBMIT_TIME ignored, already have date. CLIENT_SUBMIT_TIME: ' + DateTimeToStr(LDate)); {Do not localize} + end; + end; + end; + IdTNEF_PR_CONVERSATION_INDEX: begin + GetMapiBinary(LType, 'CONVERSATION_INDEX'); {Do not localize} + end; + IdTNEF_PR_MESSAGE_SUBMISSION_ID: begin + GetMapiBinary(LType, 'MESSAGE_SUBMISSION_ID'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SUBJECT: begin + {LStr :=} GetMapiStrings(LType, 'ORIGINAL_SUBJECT'); {Do not localize} + end; + IdTNEF_PR_REPLY_REQUESTED: begin + {LShort :=} GetMapiBoolean(LType, 'REPLY_REQUESTED'); {Do not localize} + end; + IdTNEF_PR_SENDER_SEARCH_KEY: begin + LStr := GetMapiBinaryAsEmailName(LType, 'SENDER_SEARCH_KEY'); {Do not localize} + FMsg.From.Address := LStr; + if FReceiptRequested = True then begin + FMsg.ReceiptRecipient.Address := FMsg.From.Address; + end; + if FDoLogging then begin + DoLog(' Message sender: ' + LStr); {Do not localize} + end; + end; + IdTNEF_PR_DELETE_AFTER_SUBMIT: begin + GetMapiBoolean(LType, 'DELETE_AFTER_SUBMIT'); {Do not localize} + end; + IdTNEF_PR_MESSAGE_DELIVERY_TIME: begin + GetMapiSysTime(LType, 'MESSAGE_DELIVERY_TIME'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SUBMIT_TIME: begin + GetMapiSysTime(LType, 'ORIGINAL_SUBMIT_TIME'); {Do not localize} + end; + IdTNEF_PR_SENTMAIL_ENTRYID: begin + GetMapiBinary(LType, 'SENTMAIL_ENTRYID'); {Do not localize} + end; + IdTNEF_PR_RTF_IN_SYNC: begin + GetMapiBoolean(LType, 'RTF_IN_SYNC'); {Do not localize} + end; + IdTNEF_PR_MAPPING_SIGNATURE: begin + GetMapiBinary(LType, 'MAPPING_SIGNATURE'); {Do not localize} + end; + IdTNEF_PR_STORE_RECORD_KEY: begin + GetMapiBinary(LType, 'STORE_RECORD_KEY'); {Do not localize} + end; + IdTNEF_PR_STORE_ENTRYID: begin + GetMapiBinary(LType, 'STORE_ENTRYID'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENDER_NAME: begin + LStr := GetMapiStrings(LType, 'ORIGINAL_SENDER_NAME'); {Do not localize} + if FMsg.From.Address = '' then begin + FMsg.From.Address := LStr; + if FDoLogging then begin + DoLog(' Message has From (from ORIGINAL_SENDER_NAME): ' + LStr); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' ORIGINAL_SENDER_NAME ignored, already have From. ORIGINAL_SENDER_NAME: ' + LStr); {Do not localize} + end; + end; + if FDoLogging then begin + DoLog(' Message From: ' + FMsg.From.Address); {Do not localize} + end; + end; + IdTNEF_PR_ORIGINAL_SENDER_ENTRYID: begin + GetMapiBinary(LType, 'ORIGINAL_SENDER_ENTRYID'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENDER_SEARCH_KEY: begin + GetMapiBinary(LType, 'ORIGINAL_SENDER_SEARCH_KEY'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_NAME: begin + GetMapiStrings(LType, 'ORIGINAL_SENT_REPRESENTING_NAME'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_ENTRYID: begin + GetMapiBinary(LType, 'ORIGINAL_SENT_REPRESENTING_ENTRYID'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY: begin + GetMapiBinary(LType, 'ORIGINAL_SENT_REPRESENTING_SEARCH_KEY'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENDER_ADDRTYPE: begin + GetMapiStrings(LType, 'ORIGINAL_SENDER_ADDRTYPE'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENDER_EMAIL_ADDRESS: begin + GetMapiStrings(LType, 'ORIGINAL_SENDER_EMAIL_ADDRESS'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE: begin + GetMapiStrings(LType, 'ORIGINAL_SENT_REPRESENTING_ADDRTYPE'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS: begin + GetMapiStrings(LType, 'ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS'); {Do not localize} + end; + IdTNEF_PR_SENDER_NAME: begin + GetMapiStrings(LType, 'SENDER_NAME'); {Do not localize} + end; + IdTNEF_PR_NORMALIZED_SUBJECT: begin + GetMapiStrings(LType, 'NORMALIZED_SUBJECT'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_DISPLAY_CC: begin + GetMapiStrings(LType, 'ORIGINAL_DISPLAY_CC'); {Do not localize} + end; + IdTNEF_PR_ORIGINAL_DISPLAY_TO: begin + GetMapiStrings(LType, 'ORIGINAL_DISPLAY_TO'); {Do not localize} + end; + IdTNEF_PR_OBJECT_TYPE: begin + {LLong := } GetMapiLong(LType, 'OBJECT_TYPE'); {Do not localize} + end; + IdTNEF_PR_STORE_SUPPORT_MASK: begin + {LLong := } GetMapiLong(LType, 'STORE_SUPPORT_MASK'); {Do not localize} + end; + IdTNEF_PR_TNEF_CORRELATION_KEY: begin + LStr := GetMapiBinaryAsString(LType, 'CORRELATION_KEY'); {Do not localize} + if FMsg.MsgId = '' then begin + FMsg.MsgId := LStr; + if FDoLogging then begin + DoLog(' Message has message ID (from CORRELATION_KEY): ' + LStr); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' CORRELATION_KEY ignored, already have message ID. CORRELATION_KEY: ' + LStr); {Do not localize} + end; + end; + if FDoLogging then begin + DoLog(' Message ID: ' + FMsg.MsgId); {Do not localize} + end; + end; + IdTNEF_PR_RTF_SYNC_BODY_CRC: begin + {LLong := } GetMapiLong(LType, 'RTF_SYNC_BODY_CRC'); {Do not localize} + end; + IdTNEF_PR_RTF_SYNC_BODY_COUNT: begin + {LLong := } GetMapiLong(LType, 'RTF_SYNC_BODY_COUNT'); {Do not localize} + end; + IdTNEF_PR_RTF_SYNC_BODY_TAG: begin + {LStr :=} GetMapiStrings(LType, 'RTF_SYNC_BODY_TAG'); {Do not localize} + end; + IdTNEF_PR_BODY: begin + LStr := GetMapiStrings(LType, 'BODY'); {Do not localize} + FMsg.Body.Text := LStr; + end; + IdTNEF_PR_RTF_COMPRESSED: begin + LStr := GetMapiBinaryAsString(LType, 'RTF_COMPRESSED'); {Do not localize} + //Add this as a TIdText part of type text/rtf... + LTextPart := TIdText.Create(FMsg.MessageParts); + LTextPart.ContentType := 'text/rtf'; {Do not localize} + LTextPart.Body.Text := LStr; + end; + IdTNEF_PR_RTF_SYNC_PREFIX_COUNT: begin + {LLong := } GetMapiLong(LType, 'RTF_SYNC_PREFIX_COUNT'); {Do not localize} + end; + IdTNEF_PR_RTF_SYNC_TRAILING_COUNT: begin + {LLong := } GetMapiLong(LType, 'RTF_SYNC_TRAILING_COUNT'); {Do not localize} + end; + IdTNEF_PR_ORIGINALLY_INTENDED_RECIP_ENTRYID: begin + GetMapiBinary(LType, 'ORIGINALLY_INTENDED_RECIP_ENTRYID'); {Do not localize} + end; + else + //For the types we are not interested in, skip past them... + case LType of + IdTNEF_PT_BOOLEAN, + IdTNEF_PT_LONG, + IdTNEF_PT_I2, + IdTNEF_PT_R4, + IdTNEF_PT_ERROR, + IdTNEF_PT_APPTIME: begin + if FDoLogging then begin + DoLogFmt(' Skipping MAPI attribute 0x%x of type %s', [LAttribute, GetStringForMapiType(LType)]); {Do not localize} + end; + Skip(4); //Only 2 bytes used, but padded to 4 + end; + IdTNEF_PT_BINARY, + IdTNEF_PT_OBJECT: begin + if FDoLogging then begin + DoLogFmt(' Skipping MAPI attribute 0x%x of type %s', [LAttribute, GetStringForMapiType(LType)]); {Do not localize} + end; + GetMapiItemAsBytes(LType, 'ignored data'); + end; + IdTNEF_PT_UNICODE, + IdTNEF_PT_STRING8: begin + if FDoLogging then begin + DoLogFmt(' Skipping MAPI attribute 0x%x of type %s', [LAttribute, GetStringForMapiType(LType)]); {Do not localize} + end; + GetMapiStrings(LType, 'ignored data'); + end; + IdTNEF_PT_SYSTIME, + IdTNEF_PT_DOUBLE, + IdTNEF_PT_I8, + IdTNEF_PT_CURRENCY: begin + if FDoLogging then begin + DoLogFmt(' Skipping MAPI attribute 0x%x of type %s', [LAttribute, GetStringForMapiType(LType)]); {Do not localize} + end; + Skip(8); + end; + else + raise EIdTnefUnknownMapiType.CreateFmt('Encountered unknown MAPI type: %d, attribute: %d', [LType, LAttribute]); {Do not localize} + end; + end; +end; + +procedure TIdCoderTNEF.ParseMapiProps(ALength: LongWord); +var + LNumEntries: LongWord; + LIndex: LongWord; +begin + if FDoLogging then begin + DoLogFmt(' Parsing MAPI block, %d bytes.', [ALength]); {Do not localize} + end; + LNumEntries := GetLongWord; + if FDoLogging then begin + DoLogFmt(' Contains %d entries.', [LNumEntries]); {Do not localize} + end; + if LNumEntries > 0 then begin + for LIndex := 0 to LNumEntries-1 do begin + if FDoLogging then begin + DoLogFmt(' Entry %d:', [LIndex]); {Do not localize} + end; + ParseMapiProp; + end; + end; +end; + +procedure TIdCoderTNEF.ParseAttribute(AAttribute, AType: Word); +var + LLength: LongWord; + LMajor, LMinor: Word; + LShort: Smallint; +begin + LLength := GetLongWord; + Checksum(LLength); + case AAttribute of + IdTNEFattTnefVersion: begin + if AType <> IdTNEFAtpDWord then begin + raise EIdTnefUnexpectedType.Create('Expected DWord for TnefVersion'); {Do not localize} + end; + LMinor := GetWord; + LMajor := GetWord; + if FDoLogging then begin + DoLogFmt(' ParseAttribute found TNef Version DWord. Major version: %d Minor version: %d', [LMajor, LMinor]); {Do not localize} + end; + if (LMajor <> 1) and (LMinor <> 0) then begin + if FDoLogging then begin + DoLog(' Expected a version with Major = 1, Minor = 0. Some elements may not parse correctly.'); {Do not localize} + end; + end else begin + if FDoLogging then begin + DoLog(' This is the expected version.'); {Do not localize} + end; + end; + end; + IdTNEFattSubject: begin + if AType <> IdTNEFAtpString then begin + raise EIdTnefUnexpectedType.Create('Expected String for TnefSubject'); {Do not localize} + end; + FMsg.Subject := GetString(LLength); + if FDoLogging then begin + DoLog(' ParseAttribute found TNef Subject String: ' + FMsg.Subject); {Do not localize} + DoLog(' Message has subject: ' + FMsg.Subject); {Do not localize} + end; + end; + IdTNEFattDateSent: begin + if AType <> IdTNEFAtpDate then begin + raise EIdTnefUnexpectedType.Create('Expected Date for TnefDateSent'); {Do not localize} + end; + FMsg.Date := GetDate(LLength); + if FDoLogging then begin + DoLog(' ParseAttribute found TNef Date Sent.'); {Do not localize} + DoLog(' Message has date: ' + DateTimeToStr(FMsg.Date)); {Do not localize} + end; + end; + IdTNEFattMessageID: begin + if AType <> IdTNEFAtpString then begin + raise EIdTnefUnexpectedType.Create('Expected String for TnefMessageID'); {Do not localize} + end; + FMsg.MsgId := GetString(LLength); + if FDoLogging then begin + DoLog(' ParseAttribute found TNef Message ID.'); {Do not localize} + DoLog(' Message has ID: ' + FMsg.MsgId); {Do not localize} + end; + end; + IdTNEFattPriority: begin + if AType <> IdTNEFAtpShort then begin + raise EIdTnefUnexpectedType.Create('Expected Short for TnefPriority'); {Do not localize} + end; + LShort := GetWord; + if FDoLogging then begin + DoLog(' ParseAttribute found Priority Short.'); {Do not localize} + end; + case LShort of + IdTNEFprioLow: begin + FMsg.Priority := mpLow; + if FDoLogging then begin + DoLog(' Message has low priority.'); {Do not localize} + end; + end; + IdTNEFprioNorm: begin + FMsg.Priority := mpNormal; + if FDoLogging then begin + DoLog(' Message has normal priority.'); {Do not localize} + end; + end; + IdTNEFprioHigh: begin + FMsg.Priority := mpHigh; + if FDoLogging then begin + DoLog(' Message has high priority.'); {Do not localize} + end; + end; + else + raise EIdTnefUnexpectedValue.Create('Unexpected value for priority.'); {Do not localize} + end; + end; + IdTNEFattMAPIProps: begin + ParseMapiProps(LLength); + end; + else + case AType of + IdTNEFAtpTriples: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpTriples type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpString: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpString type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpText: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpText type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpDate: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpDate type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpShort: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpShort type, %s, length: %d', [GetStringForAttribute(AAttribute), LLength]); {Do not localize} + end; + Skip(LLength); + end; + IdTNEFAtpLong: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpLong type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpByte: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpByte type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpWord: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpWord type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpDWord: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpDWord type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + IdTNEFAtpMax: begin + if FDoLogging then begin + DoLogFmt(' ParseAttribute found AtpMax type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + else + if FDoLogging then begin + DoLogFmt(' ParseAttribute found unknown type, %s, length: %d', {Do not localize} + [GetStringForAttribute(AAttribute), LLength]); + end; + Skip(LLength); + end; + end; + Skip(2); //Checksum +end; + +procedure TIdCoderTNEF.Parse(const AIn: TStream; AMsg: TIdMessage; ALog: Boolean = False); +var + LdwTemp: LongWord; + LBlockType: Byte; +begin + FLog := ''; + FDoLogging := ALog; + FMsg := AMsg; + FMsg.Clear; + FMsg.ContentType := 'multipart/mixed'; //Default: improve on this at a later stage. + FReceiptRequested := False; + FCurrentAttachment := nil; + FData := AIn; + if FDoLogging then begin + DoLogFmt('Bytes in TNEF: %d', [FData.Size - FData.Position], False); {Do not localize} + end; + //Check for a valid TNEF signature... + LdwTemp := GetLongWord; + if LdwTemp <> IdTNEFSignature then begin + if FDoLogging then begin + DoLog('Invalid TNEF signature', False); {Do not localize} + end; + raise EIdTnefInvalidTNEFSignature.Create('Invalid TNEF signature'); {Do not localize} + end; + FKey := GetWord; + if FDoLogging then begin + DoLogFmt('Key: %d' + EOL + 'Bytes left plus message:', [FKey], False); {Do not localize} + end; + repeat + LBlockType := GetByte; + case LBlockType of + IdTNEFLvlMessage: begin + if FDoLogging then begin + DoLog(' Calling ParseMessageBlock:'); {Do not localize} + end; + ParseMessageBlock; + end; + IdTNEFLvlAttachment: begin + if FDoLogging then begin + DoLog(' Calling ParseAttachmentBlock:'); {Do not localize} + end; + ParseAttachmentBlock; + end; + else + begin + if FDoLogging then begin + DoLogFmt(' Hit unknown block type: %d', [LBlockType]); {Do not localize} + end; + raise EIdTnefUnknownBlockType.Create('Hit unknown block type in TNEF - corrupt TNEF?'); {Do not localize} + end; + end; + until FData.Position >= FData.Size; + if FDoLogging then begin + DoLog(' Finished processing TNEF.'); {Do not localize} + end; +end; + +end. + diff --git a/indy/Protocols/IdCoderUUE.pas b/indy/Protocols/IdCoderUUE.pas new file mode 100644 index 0000000..8c0e062 --- /dev/null +++ b/indy/Protocols/IdCoderUUE.pas @@ -0,0 +1,116 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 1/21/2004 1:44:16 PM JPMugaas + InitComponent + + Rev 1.5 10/16/2003 11:11:18 PM DSiders + Added localization comments. + + Rev 1.4 2003.06.13 6:57:12 PM czhower + Speed improvement + + Rev 1.2 6/13/2003 07:58:48 AM JPMugaas + Should now compile with new decoder design. + + Rev 1.1 2003.06.13 3:41:20 PM czhower + Optimizaitions. + + Rev 1.0 11/14/2002 02:15:06 PM JPMugaas +} + +unit IdCoderUUE; + +{$i IdCompilerDefines.inc} + +interface + +uses + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + Classes, + {$ENDIF} + IdCoder00E, IdCoder3to4; + +type + TIdDecoderUUE = class(TIdDecoder00E) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + + TIdEncoderUUE = class(TIdEncoder00E) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + +const + // Note the embedded ' + GUUECodeTable: string = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_'; {do not localize} + +var + GUUEDecodeTable: TIdDecodeTable; + +implementation + +uses + IdGlobal; + +{ TIdEncoderUUE } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdEncoderUUE.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdEncoderUUE.InitComponent; +begin + inherited InitComponent; + FCodingTable := ToBytes(GUUECodeTable); + FFillChar := GUUECodeTable[1]; +end; + +{ TIdDecoderUUE } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDecoderUUE.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDecoderUUE.InitComponent; +begin + inherited InitComponent; + FDecodeTable := GUUEDecodeTable; + FFillChar := GUUECodeTable[1]; +end; + +initialization + TIdDecoder00E.ConstructDecodeTable(GUUECodeTable, GUUEDecodeTable); + // Older UUEncoders use space instead of `. This way we account for both. + GUUEDecodeTable[Ord(' ')] := GUUEDecodeTable[Ord('`')]; +end. diff --git a/indy/Protocols/IdCoderXXE.pas b/indy/Protocols/IdCoderXXE.pas new file mode 100644 index 0000000..0d72f4f --- /dev/null +++ b/indy/Protocols/IdCoderXXE.pas @@ -0,0 +1,111 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 1/21/2004 1:44:18 PM JPMugaas + InitComponent + + Rev 1.4 10/16/2003 11:11:34 PM DSiders + Added localization comments. + + Rev 1.3 2003.06.13 6:57:12 PM czhower + Speed improvement + + Rev 1.1 6/13/2003 08:14:38 AM JPMugaas + Removed some extra line feeds causing formatting problems. + + Rev 1.0 11/14/2002 02:15:22 PM JPMugaas +} + +unit IdCoderXXE; + +interface + +{$i IdCompilerDefines.inc} + +uses + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + Classes, + {$ENDIF} + IdCoder00E, IdCoder3to4; + +type + TIdDecoderXXE = class(TIdDecoder00E) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + + TIdEncoderXXE = class(TIdEncoder00E) + protected + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + end; + +const + GXXECodeTable: string = '+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; {do not localize} + +var + GXXEDecodeTable: TIdDecodeTable; + +implementation + +uses + IdGlobal; + +{ TIdEncoderXXE } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdEncoderXXE.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdEncoderXXE.InitComponent; +begin + inherited InitComponent; + FCodingTable := ToBytes(GXXECodeTable); + FFillChar := GXXECodeTable[1]; +end; + +{ TIdDecoderXXE } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDecoderXXE.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDecoderXXE.InitComponent; +begin + inherited InitComponent; + FDecodeTable := GXXEDecodeTable; + FFillChar := GXXECodeTable[1]; +end; + +initialization + TIdDecoder00E.ConstructDecodeTable(GXXECodeTable, GXXEDecodeTable); +end. + diff --git a/indy/Protocols/IdCompilerDefines.inc b/indy/Protocols/IdCompilerDefines.inc new file mode 100644 index 0000000..fc64532 --- /dev/null +++ b/indy/Protocols/IdCompilerDefines.inc @@ -0,0 +1,1687 @@ +{$IFDEF CONDITIONALEXPRESSIONS} + // Must be at the top... + {$IF CompilerVersion >= 24.0} + {$LEGACYIFEND ON} + {$IFEND} +{$ENDIF} + +// General + +// Make this $DEFINE to use the 16 color icons required by Borland +// or DEFINE to use the 256 color Indy versions +{.$DEFINE Borland} + +// S.G. 4/9/2002: IPv4/IPv6 general switch (for defaults only) +{$DEFINE IdIPv4} + +{$DEFINE INDY100} +{$DEFINE 10_6_2} //so developers can IFDEF for this specific version + +// When invoking DCC on the command-line, use the -DBCB +// parameter when generating C++Builder output files! +{$IFDEF BCB} + {$DEFINE CBUILDER} +{$ELSE} + {$DEFINE DELPHI} +{$ENDIF} + +{$UNDEF USE_OPENSSL} +{$UNDEF STATICLOAD_OPENSSL} + +{$UNDEF USE_ZLIB_UNIT} +{$UNDEF USE_SSPI} + +// $DEFINE the following if the global objects in the IdStack and IdThread +// units should be freed on finalization +{.$DEFINE FREE_ON_FINAL} +{$UNDEF FREE_ON_FINAL} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. This works in conjunction with the +// FREE_ON_FINAL define above. +{$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + +// FastMM is natively available in BDS 2006 and higher. $DEFINE the +// following if FastMM has been installed manually in earlier versions +{.$DEFINE USE_FASTMM4} +{$UNDEF USE_FASTMM4} + +// $DEFINE the following if MadExcept has been installed manually in +// BDS 2005 or earlier (System.RegisterExpectedMemoryLeak() was introduced +// in BDS 2006) +{.$DEFINE USE_MADEXCEPT} +{$UNDEF USE_MADEXCEPT} + +// Make sure the following are $DEFINE'd only for Delphi/C++Builder 2009 onwards +// as specified further below. The VCL is fully Unicode, where the 'String' +// type maps to System.UnicodeString, not System.AnsiString anymore +{$UNDEF STRING_IS_UNICODE} +{$UNDEF STRING_IS_ANSI} +{$UNDEF STRING_UNICODE_MISMATCH} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. Delphi/C++Builder Mobile/NextGen compilers +// do not support Ansi data types anymore, and is moving away from raw +// pointers as well. +{$DEFINE HAS_AnsiString} +{$DEFINE HAS_AnsiChar} +{$DEFINE HAS_PAnsiChar} +{$UNDEF HAS_PPAnsiChar} +{$UNDEF NO_ANSI_TYPES} +{$UNDEF USE_MARSHALLED_PTRS} +{$UNDEF HAS_MarshaledAString} +{$UNDEF USE_OBJECT_ARC} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF STRING_IS_IMMUTABLE} +{$UNDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF HAS_TEncoding} +{$UNDEF HAS_TEncoding_GetEncoding_ByEncodingName} +{$UNDEF HAS_Exception_RaiseOuterException} +{$UNDEF HAS_System_ReturnAddress} +{$UNDEF HAS_TCharacter} +{$UNDEF HAS_TInterlocked} +{$UNDEF HAS_TNetEncoding} + +// Make sure that this is defined only for environments where we are using +// the iconv library to charactor conversions. +{.$UNDEF USE_ICONV} + +//Define for Delphi cross-compiler targetting Posix +{$UNDEF USE_VCL_POSIX} +{$UNDEF HAS_ComponentPlatformsAttribute} +{$UNDEF HAS_ComponentPlatformsAttribute_Win32} +{$UNDEF HAS_ComponentPlatformsAttribute_Win64} +{$UNDEF HAS_ComponentPlatformsAttribute_OSX32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Simulator} +{$UNDEF HAS_ComponentPlatformsAttribute_Android} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device32} +{$UNDEF HAS_ComponentPlatformsAttribute_Linux64} +{$UNDEF HAS_ComponentPlatformsAttribute_WinNX32} +{$UNDEF HAS_ComponentPlatformsAttribute_WinIoT32} +{$UNDEF HAS_ComponentPlatformsAttribute_iOS_Device64} +{$UNDEF HAS_DIRECTIVE_WARN_DEFAULT} + +// Define for Delphi to auto-generate platform-appropriate '#pragma link' statements in HPP files +{$UNDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + +// detect compiler versions + +// TODO: to detect features in Delphi/C++Builder v6 and later, use CompilerVersion +// and RTLVersion constants instead of VERXXX defines. We still support v5, which +// does not have such constants. + +// Delphi 4 +{$IFDEF VER120} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE DELPHI_4} +{$ENDIF} + +// C++Builder 4 +{$IFDEF VER125} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE CBUILDER_4} +{$ENDIF} + +// Delphi & C++Builder 5 +{$IFDEF VER130} + {$DEFINE DCC} + {$DEFINE VCL_50} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_5} + {$ELSE} + {$DEFINE DELPHI_5} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 6 +{$IFDEF VER140} + {$DEFINE DCC} + {$DEFINE VCL_60} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_6} + {$ELSE} + {$DEFINE DELPHI_6} + {$ENDIF} +{$ENDIF} + +//Delphi 7 +{$IFDEF VER150} + {$DEFINE DCC} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} // there was no C++ Builder 7 +{$ENDIF} + +//Delphi 8 +{$IFDEF VER160} + {$DEFINE DCC} + {$DEFINE VCL_80} + {$DEFINE DELPHI_8} // there was no C++ Builder 8 +{$ENDIF} + +//Delphi 2005 +{$IFDEF VER170} + {$DEFINE DCC} + {$DEFINE VCL_2005} + {$DEFINE DELPHI_2005} // there was no C++Builder 2005 +{$ENDIF} + +// NOTE: CodeGear decided to make Highlander be a non-breaking release +// (no interface changes, thus fully backwards compatible without any +// end user code changes), so VER180 applies to both BDS 2006 and +// Highlander prior to the release of RAD Studio 2007. Use VER185 to +// identify Highlanger specifically. + +//Delphi & C++Builder 2006 +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER180} + {$DEFINE DCC} + {$DEFINE VCL_2006} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2006} + {$ELSE} + {$DEFINE DELPHI_2006} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER185} + {$DEFINE DCC} + {$UNDEF VCL_2006} + {$DEFINE VCL_2007} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_2006} + {$DEFINE CBUILDER_2007} + {$ELSE} + {$UNDEF DELPHI_2006} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +// BDS 2007 NET personality uses VER190 instead of 185. +//Delphi .NET 2007 +{$IFDEF VER190} + {$DEFINE DCC} + {$IFDEF CIL} + //Delphi 2007 + {$DEFINE VCL_2007} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2009 (Tiburon) +{$IFDEF VER200} + {$DEFINE DCC} + {$DEFINE VCL_2009} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2009} + {$ELSE} + {$DEFINE DELPHI_2009} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2010 (Weaver) +{$IFDEF VER210} + {$DEFINE DCC} + {$DEFINE VCL_2010} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2010} + {$ELSE} + {$DEFINE DELPHI_2010} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder XE (Fulcrum) +{$IFDEF VER220} +//REMOVE DCC DEFINE after the next Fulcrum beta. +//It will be defined there. + {$IFNDEF DCC} + {$DEFINE DCC} + {$ENDIF} + {$DEFINE VCL_XE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE} + {$ELSE} + {$DEFINE DELPHI_XE} + {$ENDIF} +{$ENDIF} + +// DCC is now defined by the Delphi compiler starting in XE2 + +//Delphi & CBuilder XE2 (Pulsar) +{$IFDEF VER230} + {$DEFINE VCL_XE2} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE2} + {$ELSE} + {$DEFINE DELPHI_XE2} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE3 (Waterdragon) +//Delphi & CBuilder XE3.5 (Quintessence - early betas only) +{$IFDEF VER240} + {$DEFINE VCL_XE3} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE3} + {$ELSE} + {$DEFINE DELPHI_XE3} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE4 (Quintessence) +{$IFDEF VER250} + {$UNDEF VCL_XE3} + {$DEFINE VCL_XE4} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_XE3} + {$DEFINE CBUILDER_XE4} + {$ELSE} + {$UNDEF DELPHI_XE3} + {$DEFINE DELPHI_XE4} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE5 (Zephyr) +{$IFDEF VER260} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder AppMethod +//AppMethod is just XE5 for mobile only, VCL is removed +{$IFDEF VER265} + {$DEFINE VCL_XE5} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE5} + {$ELSE} + {$DEFINE DELPHI_XE5} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE6 (Proteus) +{$IFDEF VER270} + {$DEFINE VCL_XE6} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE6} + {$ELSE} + {$DEFINE DELPHI_XE6} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE7 (Carpathia) +{$IFDEF VER280} + {$DEFINE VCL_XE7} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE7} + {$ELSE} + {$DEFINE DELPHI_XE7} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE8 (Elbrus) +{$IFDEF VER290} + {$DEFINE VCL_XE8} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE8} + {$ELSE} + {$DEFINE DELPHI_XE8} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder 10.0 Seattle (Aitana) +{$IFDEF VER300} + {$DEFINE VCL_SEATTLE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_SEATTLE} + {$ELSE} + {$DEFINE DELPHI_SEATTLE} + {$ENDIF} +{$ENDIF} + +// Delphi.NET +// Covers D8+ +{$IFDEF CIL} + // Platform specific conditional. Used for platform specific code. + {$DEFINE DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE STRING_IS_IMMUTABLE} + {.$DEFINE HAS_Int8} + {.$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + {$DEFINE HAS_UInt64} +{$ENDIF} + +// Kylix +// +//Important: Don't use CompilerVersion here as IF's are evaluated before +//IFDEF's and Kylix 1 does not have CompilerVersion defined at all. +{$IFNDEF FPC} + {$IFDEF LINUX} + {$DEFINE UNIX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF (RTLVersion >= 14.0) and (RTLVersion <= 14.5) } + {$DEFINE KYLIX} + {$IF RTLVersion = 14.5} + {$DEFINE KYLIX_3} + {$ELSEIF RTLVersion >= 14.2} + {$DEFINE KYLIX_2} + {$ELSE} + {$DEFINE KYLIX_1} + {$IFEND} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF KYLIX} + {$DEFINE VCL_60} + {$DEFINE INT_THREAD_PRIORITY} + {$DEFINE CPUI386} + {$UNDEF USE_BASEUNIX} + + {$IFDEF KYLIX_3} + {$DEFINE KYLIX_3_OR_ABOVE} + {$ENDIF} + + {$IFDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_2} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_1} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFNDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIXCOMPAT} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE USE_ZLIB_UNIT} + {$ENDIF} +{$ENDIF} + +// FPC (2+) + +{$IFDEF FPC} + // TODO: In FreePascal 4.2.0+, a Delphi-like UnicodeString type is supported. + // However, String/(P)Char do not map to UnicodeString/(P)WideChar unless + // either {$MODE DelphiUnicode} or {$MODESWITCH UnicodeStrings} is used. + // We should consider enabling one of them so Indy uses the same Unicode logic + // in Delphi 2009+ and FreePascal 4.2.0+ and reduces IFDEFs (in particular, + // STRING_UNICODE_MISMATCH, see further below). However, FreePascal's RTL + // is largely not UnicodeString-enabled yet... + {$MODE Delphi} + //note that we may need further defines for widget types depending on + //what we do and what platforms we support in FPC. + //I'll let Marco think about that one. + {$IFDEF UNIX} + {$DEFINE USE_BASEUNIX} + {$IFDEF LINUX} + //In Linux for I386, you can choose between a Kylix-libc API or + //the standard RTL Unix API. Just pass -dKYLIXCOMPAT to the FPC compiler. + //I will see what I can do about the Makefile. + {$IFDEF KYLIXCOMPAT} + {$IFDEF CPUI386} + {$UNDEF USE_BASEUNIX} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF USE_BASEUNIX} + {$UNDEF KYLIXCOMPAT} + {$ENDIF} + {$ENDIF} + + // FPC_FULLVERSION was added in FPC 2.2.4 + // Have to use Defined() or else Delphi compiler chokes, since it + // evaluates $IF statements before $IFDEF statements... + + {$MACRO ON} // must be on in order to use versioning macros + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20701)} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20604)} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20602)} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20600)} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20404)} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20402)} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20400)} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20204)} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20202)} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$IFEND} + {$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 20105)} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$IFEND} + + // just in case + {$IFDEF FPC_2_7_1} + {$DEFINE FPC_2_7_1_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_2} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + + {$IFDEF FPC_2_7_1_OR_ABOVE} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_4} + {$DEFINE FPC_2_6_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_4_OR_ABOVE} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_2} + {$DEFINE FPC_2_6_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_2_OR_ABOVE} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_6_0} + {$DEFINE FPC_2_6_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_4} + {$DEFINE FPC_2_4_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_2} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_2_OR_ABOVE} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_4_0} + {$DEFINE FPC_2_4_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_4} + {$DEFINE FPC_2_2_4_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_4_OR_ABOVE} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_2_2} + {$DEFINE FPC_2_2_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ELSE} + {$IFDEF VER2_2} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ELSE} + {$IFDEF FPC_2_1_5} + {$DEFINE FPC_2_1_5_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {.$IFDEF FPC_2_7_1_OR_ABOVE} + // support for RawByteString and UnicodeString + {.$DEFINE VCL_2009} + {.$DEFINE DELPHI_2009} + {.$ELSE} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} + {.$ENDIF} +{$ENDIF} + +// end FPC + +{$IFDEF VCL_SEATTLE} + {$DEFINE VCL_SEATTLE_OR_ABOVE} +{$ENDIF} + +{$IFDEF VCL_SEATTLE_OR_ABOVE} + {$DEFINE VCL_XE8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE8} + {$DEFINE VCL_XE8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE VCL_XE7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE7} + {$DEFINE VCL_XE7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE VCL_XE6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE6} + {$DEFINE VCL_XE6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE6_OR_ABOVE} + {$DEFINE VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE5} + {$DEFINE VCL_XE5_OR_ABOVE} + // TODO: figure out how to detect this version + {.$DEFINE VCL_XE5_UPDATE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE VCL_XE4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE4} + {$DEFINE VCL_XE4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE VCL_XE3_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE3} + {$DEFINE VCL_XE3_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE VCL_XE2_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE2} + {$DEFINE VCL_XE2_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE VCL_XE_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE} + {$DEFINE VCL_XE_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE VCL_2010_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2010} + {$DEFINE VCL_2010_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE VCL_2009_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2009} + {$DEFINE VCL_2009_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$DEFINE VCL_2007_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2007} + {$DEFINE VCL_2007_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE VCL_2006_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2006} + {$DEFINE VCL_2006_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE VCL_2005_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2005} + {$DEFINE VCL_2005_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2005_OR_ABOVE} + {$DEFINE VCL_8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_80} + {$DEFINE VCL_8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_8_OR_ABOVE} + {$DEFINE VCL_7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_70} + {$DEFINE VCL_7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$DEFINE VCL_6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_60} + {$DEFINE VCL_6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE VCL_5_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_50} + {$DEFINE VCL_5_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$DEFINE VCL_4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_40} + {$DEFINE VCL_4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +// Normalize Delphi compiler defines to match FPC for consistency: +// +// CPU32 - any 32-bit CPU +// CPU64 - any 64-bit CPU +// WINDOWS - any Windows platform (32-bit, 64-bit, CE) +// WIN32 - Windows 32-bit +// WIN64 - Windows 64-bit +// WINCE - Windows CE +// +// Consult the "Free Pascal Programmer's Guide", Appendix G for the complete +// list of defines that are used. Do not work on this unless you understand +// what the FreePascal developers are doing. Not only do you have to +// descriminate with operating systems, but also with chip architectures +// are well. +// +// DCC Pulsar+ define the following values: +// ASSEMBLER +// DCC +// CONDITIONALEXPRESSIONS +// NATIVECODE +// UNICODE +// MACOS +// MACOS32 +// MACOS64 +// MSWINDOWS +// WIN32 +// WIN64 +// LINUX +// POSIX +// POSIX32 +// CPU386 +// CPUX86 +// CPUX64 +// +// Kylix defines the following values: +// LINUX +// (others??) +// + +{$IFNDEF FPC} + // TODO: We need to use ENDIAN_BIG for big endian chip architectures, + // such as 680x0, PowerPC, Sparc, and MIPS, once DCC supports them, + // provided it does not already define its own ENDIAN values by then... + {$DEFINE ENDIAN_LITTLE} + {$IFNDEF VCL_6_OR_ABOVE} + {$DEFINE MSWINDOWS} + {$ENDIF} + {$IFDEF MSWINDOWS} + {$DEFINE WINDOWS} + {$ENDIF} + // TODO: map Pulsar's non-Windows platform defines... + {$IFDEF VCL_XE2_OR_ABOVE} + {$IFDEF CPU386} + //any 32-bit CPU + {$DEFINE CPU32} + //Intel 386 compatible chip architecture + {$DEFINE CPUI386} + {$ENDIF} + {$IFDEF CPUX86} + {$DEFINE CPU32} + {$ENDIF} + {$IFDEF CPUX64} + //any 64-bit CPU + {$DEFINE CPU64} + //AMD64 compatible chip architecture + {$DEFINE CPUX86_64} //historical name for AMD64 + {$DEFINE CPUAMD64} + {$ENDIF} + {$ELSE} + {$IFNDEF DOTNET} + {$IFNDEF KYLIX} + {$DEFINE I386} + {$ENDIF} + {$ENDIF} + {$DEFINE CPU32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + //differences in DotNET Framework versions. + {$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE DOTNET_2} + {$DEFINE DOTNET_2_OR_ABOVE} + {$ELSE} + {$DEFINE DOTNET_1_1} + {$ENDIF} + {$DEFINE DOTNET_1_1_OR_ABOVE} + // Extra include used in D7 for testing. Remove later when all comps are + // ported. Used to selectively exclude non ported parts. Allowed in places + // IFDEFs are otherwise not permitted. + {$DEFINE DOTNET_EXCLUDE} +{$ENDIF} + +// Check for available features + +{$IFDEF CBUILDER} + // When generating a C++ HPP file, if a class has no explicit constructor + // defined and contains compiler-managed members (xxxString, TDateTime, + // Variant, DelphiInterface, etc), the HPP will contain a forwarding + // inline constructor that implicitally initializes those managed members, + // which will overwrite any non-default initializations performed inside + // of InitComponent() overrides! In this situation, the workaround is to + // define an explicit constructor that forwards to the base class constructor + // manually. + {$DEFINE WORKAROUND_INLINE_CONSTRUCTORS} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$IFNDEF FPC} + {$IFNDEF KYLIX} + {$DEFINE HAS_RemoveFreeNotification} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_GetObjectProp} + {$DEFINE HAS_TObjectList} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE HAS_PCardinal} + {$DEFINE HAS_PByte} + {$DEFINE HAS_PWord} + {$DEFINE HAS_PPointer} + {$DEFINE HAS_TList_Assign} + {$DEFINE HAS_sLineBreak} + {$DEFINE HAS_RaiseLastOSError} + {$DEFINE HAS_SysUtils_IncludeExcludeTrailingPathDelimiter} + {$DEFINE HAS_SysUtils_DirectoryExists} + {$DEFINE HAS_UNIT_DateUtils} + {$DEFINE HAS_UNIT_StrUtils} + {$DEFINE HAS_UNIT_Types} + {$DEFINE HAS_TryStrToInt} + {$DEFINE HAS_TryStrToInt64} + {$DEFINE HAS_TryEncodeDate} + {$DEFINE HAS_TryEncodeTime} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFNDEF FPC} + {$DEFINE HAS_IInterface} + {$DEFINE HAS_TSelectionEditor} + {$DEFINE HAS_TStringList_CaseSensitive} + {$IFNDEF KYLIX} + {$DEFINE HAS_DEPRECATED} + {$DEFINE HAS_SYMBOL_PLATFORM} + {$DEFINE HAS_UNIT_PLATFORM} + {$IFNDEF VCL_8_OR_ABOVE} + // Delphi 6 and 7 have an annoying bug that if a class method is declared as + // deprecated, the compiler will emit a "symbol is deprecated" warning + // on the method's implementation! So we will have to wrap implementations + // of deprecated methods with {$WARN SYMBOL_DEPRECATED OFF} directives + // to disable that warning. + {$DEFINE DEPRECATED_IMPL_BUG} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFNDEF DOTNET} + //Widget defines are omitted in .NET + {$DEFINE VCL_60_PLUS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$IFNDEF FPC} + {$DEFINE HAS_UInt64} + {$DEFINE HAS_NAMED_THREADS} + {$DEFINE HAS_TStrings_ValueFromIndex} + {$ENDIF} + {$DEFINE HAS_TFormatSettings} + {$DEFINE HAS_PosEx} + {$IFNDEF VCL_70} + // not implemented in D7 + {$DEFINE HAS_STATIC_TThread_Queue} + {$ENDIF} + {$IFNDEF CIL} + {$IFNDEF VCL_80} + // not implemented in D8 or .NET + {$DEFINE HAS_STATIC_TThread_Synchronize} + {$ENDIF} + {$ENDIF} +{$ELSE} + {$IFDEF CBUILDER_6} + {$DEFINE HAS_NAMED_THREADS} + {$ENDIF} +{$ENDIF} + +{$IFNDEF VCL_2005_OR_ABOVE} + {$IFDEF DCC} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE USE_INLINE} + {$DEFINE HAS_2PARAM_FileAge} + {$DEFINE HAS_System_RegisterExpectedMemoryLeak} + {$IFNDEF FREE_ON_FINAL} + {$IFNDEF DOTNET} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} + // UInt64 is emitted as signed __int64 instead of unsigned __int64 in HPP files + {$IFDEF CBUILDER} + {$DEFINE BROKEN_UINT64_HPPEMIT} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$IFNDEF CBUILDER_2007} + // class properties are broken in C++Builder 2007, causing AVs at compile-time + {$DEFINE HAS_CLASSPROPERTIES} + {$ENDIF} + // Native(U)Int exist but are buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_StrToInt64Def} + {$DEFINE HAS_DWORD_PTR} + {$DEFINE HAS_ULONG_PTR} + {$DEFINE HAS_ULONGLONG} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_CurrentYear} + {$IFNDEF DOTNET} + {$DEFINE HAS_TIMEUNITS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$IFNDEF DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE HAS_UnicodeString} + {$DEFINE HAS_TEncoding} + {$DEFINE HAS_TCharacter} + {$DEFINE HAS_InterlockedCompareExchangePointer} + {$DEFINE HAS_WIDE_TCharArray} + {$DEFINE HAS_UNIT_AnsiStrings} + {$DEFINE HAS_PUInt64} + {$IFDEF VCL_2009} + // TODO: need to differentiate between RTM and Update 1 + // FmtStr() is broken in RTM but was fixed in Update 1 + {$DEFINE BROKEN_FmtStr} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_CLASSVARS} + {$DEFINE HAS_DEPRECATED_MSG} + {$DEFINE HAS_TBytes} + // Native(U)Int are still buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_Int8} + {$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + // UInt64 is now emitted as unsigned __int64 in HPP files + {$IFDEF CBUILDER} + {$UNDEF BROKEN_UINT64_HPPEMIT} + {$ENDIF} + {$IFDEF DCC} + {$IFDEF WINDOWS} + // Exception.RaiseOuterException() is only available on Windows at this time + {$DEFINE HAS_Exception_RaiseOuterException} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE HAS_CLASSCONSTRUCTOR} + {$DEFINE HAS_CLASSDESTRUCTOR} + {$DEFINE HAS_DELAYLOAD} + {$DEFINE HAS_TThread_NameThreadForDebugging} + {$DEFINE DEPRECATED_TThread_SuspendResume} + // Native(U)Int are finally ok to use now + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$DEFINE HAS_USHORT} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE HAS_TFormatSettings_Object} + {$DEFINE HAS_LocaleCharsFromUnicode} + {$DEFINE HAS_UnicodeFromLocaleChars} + {$DEFINE HAS_PVOID} + {$DEFINE HAS_ULONG64} + {$DEFINE HAS_TEncoding_GetEncoding_ByEncodingName} + {$IFDEF DCC} + // Exception.RaiseOuterException() is now available on all platforms + {$DEFINE HAS_Exception_RaiseOuterException} + {$ENDIF} + {$IFNDEF DOTNET} + {$DEFINE HAS_TInterlocked} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$DEFINE HAS_LONG} + {$DEFINE HAS_ComponentPlatformsAttribute} + {$DEFINE HAS_ComponentPlatformsAttribute_Win32} + {$DEFINE HAS_ComponentPlatformsAttribute_Win64} + {$DEFINE HAS_ComponentPlatformsAttribute_OSX32} + {$DEFINE HAS_System_ReturnAddress} + {$DEFINE HAS_DIRECTIVE_WARN_DEFAULT} +{$ENDIF} + +{$IFDEF VCL_XE3_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$DEFINE HAS_SysUtils_TStringHelper} + {$IFDEF NEXTGEN} + {$DEFINE DCC_NEXTGEN} + {$DEFINE HAS_MarshaledAString} + {$DEFINE USE_MARSHALLED_PTRS} + {$IFDEF AUTOREFCOUNT} + {$DEFINE USE_OBJECT_ARC} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE4_OR_ABOVE} + {$DEFINE HAS_AnsiStrings_StrPLCopy} + {$DEFINE HAS_AnsiStrings_StrLen} + {$DEFINE HAS_Character_TCharHelper} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Simulator} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device} + // technically, these are present in XE4, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Android} + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux32} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinNX32} +{$ENDIF} + +{$IFDEF VCL_XE5_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_Android} +{$ENDIF} + +{$IFDEF VCL_XE5_UPDATE2_OR_ABOVE} + {$DEFINE HAS_DIRECTIVE_HPPEMIT_LINKUNIT} +{$ENDIF} + +{$IFDEF VCL_XE7_OR_ABOVE} + {$DEFINE HAS_TNetEncoding} +{$ENDIF} + +{$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device32} + {$DEFINE HAS_ComponentPlatformsAttribute_iOS_Device64} + // technically, these are present in XE8, but they are not used yet + {.$DEFINE HAS_ComponentPlatformsAttribute_Linux64} + {.$DEFINE HAS_ComponentPlatformsAttribute_WinIoT32} +{$ENDIF} + +// Delphi XE+ cross-compiling +{$IFNDEF FPC} + {$IFDEF POSIX} + {$IF RTLVersion >= 22.0} + {$DEFINE UNIX} + {$UNDEF USE_BASEUNIX} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$IFDEF LINUX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF RTLVersion >= 22.0} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_CROSS_COMPILE} + {$UNDEF KYLIXCOMPAT} +{$ELSE} + {$IFDEF KYLIXCOMPAT} + {$linklib c} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC} + {$DEFINE USE_INLINE} + {$DEFINE USE_CLASSINLINE} + {$DEFINE USE_TBitBtn} //use Bit Buttons instead of Buttons + {$DEFINE FPC_REINTRODUCE_BUG} + {$DEFINE FPC_CIRCULAR_BUG} + {$DEFINE NO_REDECLARE} + {$DEFINE BYTE_COMPARE_SETS} + {$DEFINE HAS_QWord} // TODO: when was QWord introduced? + {$DEFINE HAS_PQWord} // TODO: when was PQWord introduced? + {$IFDEF FPC_2_1_5_OR_ABOVE} + {$DEFINE HAS_UInt64} + {.$DEFINE HAS_PUInt64} // TODO: is this defined? + {$ENDIF} + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} + {$IFDEF FPC_2_2_2_OR_ABOVE} + {$DEFINE HAS_SharedPrefix} + {$ENDIF} + {$IFDEF FPC_2_2_4_OR_ABOVE} + // size_t and psize_t are only available on Unix systems (FreeBSD, Linux, etc) + {$IFDEF UNIX} + {$DEFINE HAS_SIZE_T} + {$DEFINE HAS_PSIZE_T} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_PtrInt} + {$DEFINE HAS_PtrUInt} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_LPGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFDEF WINDOWS} + {$DEFINE HAS_ULONG_PTR} + {.$DEFINE HAS_ULONGLONG} // TODO: is this defined? + {$ENDIF} + {$DEFINE HAS_UNIT_ctypes} + {$DEFINE HAS_sLineBreak} + {$IFDEF FPC_HAS_UNICODESTRING} + {$DEFINE HAS_UnicodeString} + {$ELSE} + {$IFDEF FPC_2_4_0_OR_ABOVE} + {$DEFINE HAS_UnicodeString} + {$ENDIF} + {$ENDIF} + {$IFDEF FPC_2_4_4_OR_ABOVE} + {$DEFINE DEPRECATED_TThread_SuspendResume} + {$DEFINE HAS_DEPRECATED} // TODO: when was deprecated introduced? + {$DEFINE HAS_DEPRECATED_MSG} + {$ENDIF} + {$IFDEF FPC_2_6_0_OR_ABOVE} + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} + {$ENDIF} + {$IFDEF FPC_2_6_2_OR_ABOVE} + {$DEFINE HAS_Int8} + {$DEFINE HAS_UInt8} + {$DEFINE HAS_Int16} + {$DEFINE HAS_UInt16} + {$DEFINE HAS_Int32} + {$DEFINE HAS_UInt32} + {$ENDIF} + {$IFDEF FPC_2_6_4_OR_ABOVE} + {$DEFINE HAS_PInt8} + {$DEFINE HAS_PUInt8} + {$DEFINE HAS_PInt16} + {$DEFINE HAS_PUInt16} + {$DEFINE HAS_PInt32} + {$DEFINE HAS_PUInt32} + {$ENDIF} + {$IFDEF FPC_UNICODESTRINGS} + {$DEFINE STRING_IS_UNICODE} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE WIDGET_WINFORMS} +{$ELSE} + {$DEFINE WIDGET_VCL_LIKE} // LCL included. + {$DEFINE WIDGET_VCL_LIKE_OR_KYLIX} + {$IFDEF FPC} + {$DEFINE WIDGET_LCL} + {$ELSE} + {$IFDEF KYLIX} + {$DEFINE WIDGET_KYLIX} + {$ELSE} + {$DEFINE WIDGET_VCL} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// .NET and Delphi 2009+ support UNICODE strings natively! +// +// FreePascal 2.4.0+ supports UnicodeString, but does not map its +// native String type to UnicodeString except when {$MODE DelphiUnicode} +// or {$MODESWITCH UnicodeStrings} is enabled. However, UNICODE is not +// defined in that mode yet until its RTL has been updated to support +// UnicodeString. STRING_UNICODE_MISMATCH is defined when the native +// String/Char types do not map to the same types that APIs are expecting +// based on whether UNICODE is defined or not. +// +// NOTE: Do not define UNICODE here. The compiler defines +// the symbol automatically. +{$IFDEF STRING_IS_UNICODE} + {$IFNDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ELSE} + {$DEFINE STRING_IS_ANSI} + {$IFDEF UNICODE} + {$DEFINE STRING_UNICODE_MISMATCH} + {$ENDIF} +{$ENDIF} + +{$IFDEF DCC_NEXTGEN} + {$DEFINE NO_ANSI_TYPES} + {.$DEFINE STRING_IS_IMMUTABLE} // Strings are NOT immutable in NEXTGEN yet + {$IFDEF USE_OBJECT_ARC} + // TODO: move these to an appropriate section. Not doing this yet because + // it is a major interface change to switch to Generics and we should + // maintain backwards compatibility with earlier compilers for the time + // being. Defining them only here for now because the non-Generic versions + // of these classes have become deprecated by ARC and so we need to start + // taking advantage of the Generics versions... + {$DEFINE HAS_UNIT_Generics_Collections} + {$DEFINE HAS_UNIT_Generics_Defaults} + {$DEFINE HAS_GENERICS_TDictionary} + {$DEFINE HAS_GENERICS_TList} + {$DEFINE HAS_GENERICS_TObjectList} + {$DEFINE HAS_GENERICS_TThreadList} + // TArray.Copy() was introduced in XE7 but was buggy. It was fixed in XE8: + // + // RSP-9763 TArray.Copy copies from destination to source for unmanaged types + // https://quality.embarcadero.com/browse/RSP-9763 + // + {$IFDEF VCL_XE8_OR_ABOVE} + {$DEFINE HAS_GENERICS_TArray_Copy} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF NO_ANSI_TYPES} + {$UNDEF HAS_AnsiString} + {$UNDEF HAS_AnsiChar} + {$UNDEF HAS_PAnsiChar} + {$UNDEF HAS_PPAnsiChar} + {$UNDEF HAS_UNIT_AnsiStrings} + {$UNDEF HAS_AnsiStrings_StrPLCopy} +{$ENDIF} + +{$IFDEF WIN32} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} +{$IFDEF WIN64} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} + +{$IFDEF WIN32_OR_WIN64} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} + {$IFNDEF DCC_NEXTGEN} + {$DEFINE USE_SSPI} + {$IFDEF STRING_IS_UNICODE} + {$DEFINE SSPI_UNICODE} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// High-performance counters are not reliable on multi-core systems, and have +// been known to cause problems with TIdIOHandler.ReadLn() timeouts in Windows +// XP SP3, both 32-bit and 64-bit. Refer to these discussions for more info: +// +// http://www.virtualdub.org/blog/pivot/entry.php?id=106 +// http://blogs.msdn.com/oldnewthing/archive/2008/09/08/8931563.aspx +// +// Do not enable thus unless you know it will work correctly on your systems! +{$IFDEF WINDOWS} + {.$DEFINE USE_HI_PERF_COUNTER_FOR_TICKS} +{$ENDIF} + +{$IFDEF UNIX} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} +{$ENDIF} + +{$IFDEF MACOS} + {$DEFINE HAS_getifaddrs} +{$ENDIF} + +{$IFDEF IOS} + {$DEFINE HAS_getifaddrs} + {$DEFINE USE_OPENSSL} + {$IFDEF CPUARM} + // RLebeau: For iOS devices, OpenSSL cannot be used as an external library, + // it must be statically linked into the app. For the iOS simulator, this + // is not true. Users who want to use OpenSSL in iOS device apps will need + // to add the static OpenSSL library to the project and then include the + // IdSSLOpenSSLHeaders_static unit in their uses clause. It hooks up the + // statically linked functions for the IdSSLOpenSSLHeaders unit to use... + {$DEFINE STATICLOAD_OPENSSL} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT} + {$DEFINE REQUIRES_PROPER_ALIGNMENT} +{$ENDIF} + +// +//iconv defines section. +{$DEFINE USE_ICONV_UNIT} +{$DEFINE USE_ICONV_ENC} +{$IFDEF UNIX} + {$DEFINE USE_ICONV} + {$IFDEF USE_BASEUNIX} + {$IFDEF FPC} + {$UNDEF USE_ICONV_UNIT} + {$ELSE} + {$UNDEF USE_ICONV_ENC} + {$ENDIF} + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + //important!! Iconv functions are defined in the libc.pas Kylix compatible unit. + {$UNDEF USE_ICONV_ENC} + {$UNDEF USE_ICONV_UNIT} + {$ENDIF} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE USE_ICONV} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONV_UNIT + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +{$UNDEF USE_SAFELOADLIBRARY} +{$IFDEF WINDOWS} + {$UNDEF USE_ICONV_ENC} + {$DEFINE USE_SAFELOADLIBRARY} +{$ENDIF} + +{$UNDEF USE_INVALIDATE_MOD_CACHE} +{$UNDEF USE_SAFELOADLIBRARY} +//This must come after the iconv defines because this compiler targets a Unix-like +//operating system. One key difference is that it does have a TEncoding class. +//If this comes before the ICONV defines, it creates problems. +//This also must go before the THandle size calculations. +{$IFDEF VCL_CROSS_COMPILE} + {$IFDEF POSIX} + {$DEFINE BSD} + {$DEFINE USE_SAFELOADLIBRARY} + {$DEFINE USE_INVALIDATE_MOD_CACHE} + {$ENDIF} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONVUNIT + {$UNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} + {$DEFINE INT_THREAD_PRIORITY} +{$ENDIF} + +{$IFNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} + +//IMPORTANT!!!! +// +//Do not remove this!!! This is to work around a conflict. In DCC, MACOS +//will mean OS X. In FreePascal, the DEFINE MACOS means MacIntosh System OS Classic. +{$IFDEF DCC} + // DCC defines MACOS for both iOS and OS X platforms, need to differentiate + {$IFDEF MACOS} + {$IFNDEF IOS} + {$DEFINE DARWIN} + {$ENDIF} + {$ENDIF} +{$ENDIF} +{$IFDEF FPC} + {$IFDEF MACOS} + {$DEFINE MACOS_CLASSIC} + {$ENDIF} +{$ENDIF} + +{ +BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit +word to an 8 bit byte and an 8 bit byte field named sa_len was added. +} +//Place this only after DARWIN has been defined for Delphi MACOS +{$IFDEF FREEBSD} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF DARWIN} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} + +// Do NOT remove these IFDEF's. They are here because InterlockedExchange +// only handles 32bit values. Some Operating Systems may have 64bit +// THandles. This is not always tied to the platform architecture. + +{$IFDEF AMIGA} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF ATARI} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BEOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BSD} + //I think BSD might handle FreeBSD, NetBSD, OpenBSD, and Darwin + {$IFDEF IOS} + {$IFDEF CPUARM32} + {$DEFINE CPU32} + {$DEFINE THANDLE_32} + {$ELSE} + {$IFDEF CPUARM64} + {$DEFINE CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} + {$ENDIF} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} +{$IFDEF EMBEDDED} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF EMX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GBA} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GO32} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF MACOS_CLASSIC} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NATIVENT} //Native NT for kernel level drivers + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NDS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF PALMOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SYMBIAN} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WII} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WATCOM} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} + +// end platform specific stuff for THandle size + +{$IFDEF THANDLE_CPUBITS} + {$IFDEF CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} +{$IFDEF USE_ICONV} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} + +{$UNDEF STREAM_SIZE_64} +{$IFDEF FPC} + {$DEFINE STREAM_SIZE_64} +{$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE STREAM_SIZE_64} + {$ENDIF} +{$ENDIF} + +{$IFNDEF FREE_ON_FINAL} + {$IFNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF USE_FASTMM4} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF USE_MADEXCEPT} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF DOTNET} + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF VCL_CROSS_COMPILE} + // RLebeau: should this be enabled for Windows, at least? + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} +{$ENDIF} + +{ +We must determine what the SocketType parameter is for the Socket function. +In DotNET, it's SocketType. In Kylix and the libc.pas Kylix-compatibility +library, it's a __socket_type. In BaseUnix, it's a C-type Integer. In Windows, +it's a LongInt. + +} +{$UNDEF SOCKETTYPE_IS_SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_CINT} +{$UNDEF SOCKETTYPE_IS___SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_LONGINT} +{$UNDEF SOCKETTYPE_IS_NUMERIC} +{$UNDEF SOCKET_LEN_IS_socklen_t} +{$IFDEF DOTNET} + {$DEFINE SOCKETTYPE_IS_SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_BASEUNIX} + {$DEFINE SOCKETTYPE_IS_CINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF KYLIXCOMPAT} + {$DEFINE SOCKETTYPE_IS___SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$DEFINE SOCKETTYPE_IS_NUMERIC} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKET_LEN_IS_socklen_t} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} + +{Take advantage of some TCP features specific to some stacks. +They work somewhat similarly but there's a key difference. +In Linux, TCP_CORK is turned on to send fixed packet sizes and +when turned-off (uncorked), any remaining data is sent. With +TCP_NOPUSH, this might not happen and remaining data is only sent +before disconnect. TCP_KEEPIDLE and TCP_KEEPINTVL so the IFDEF LINUX and IFDEF +SOLARIS instead of IFDEF UNIX is not an error, it's deliberate.} +{$UNDEF HAS_TCP_NOPUSH} +{$UNDEF HAS_TCP_CORK} +{$UNDEF HAS_TCP_KEEPIDLE} +{$UNDEF HAS_TCP_KEEPINTVL} +{$UNDEF HAS_SOCKET_NOSIGPIPE} +{$IFDEF BSD} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE HAS_TCP_CORK} +{$ENDIF} +{$IFDEF NETBSD} + {$DEFINE HAS_TCP_CORK} + {$DEFINE HAS_TCP_KEEPIDLE} + {$DEFINE HAS_TCP_KEEPINTVL} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$IFNDEF ANDROID} + {$DEFINE HAS_SOCKET_NOSIGPIPE} + {$ENDIF} +{$ENDIF} +{end Unix OS specific stuff} +{$IFDEF DEBUG} + {$UNDEF USE_INLINE} +{$ENDIF} + +// RLebeau 5/24/2015: In C++Builder 2006 and 2007, UInt64 is emitted as +// signed __int64 in HPP files instead of as unsigned __int64. This causes +// conflicts in overloaded routines that have (U)Int64 parameters. This +// was fixed in C++Builder 2009. For compilers that do not have a native +// UInt64 type, or for C++Builder 2006/2007, let's define a record type +// that can hold UInt64 values... +{$IFDEF HAS_UInt64} + {$IFDEF BROKEN_UINT64_HPPEMIT} + {$DEFINE TIdUInt64_IS_NOT_NATIVE} + {$ENDIF} +{$ELSE} + {$IFNDEF HAS_QWord} + {$DEFINE TIdUInt64_IS_NOT_NATIVE} + {$ENDIF} +{$ENDIF} + +// RLebeau 9/5/2013: it would take a lot of work to re-write Indy to support +// both 0-based and 1-based string indexing, so we'll just turn off 0-based +// indexing for now... +{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS} + {$ZEROBASEDSTRINGS OFF} +{$ENDIF} \ No newline at end of file diff --git a/indy/Protocols/IdCompressionIntercept.pas b/indy/Protocols/IdCompressionIntercept.pas new file mode 100644 index 0000000..43b7bae --- /dev/null +++ b/indy/Protocols/IdCompressionIntercept.pas @@ -0,0 +1,339 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.10 2/22/2004 12:04:00 AM JPMugaas + Updated for file rename. + + + Rev 1.9 2/12/2004 11:28:04 PM JPMugaas + Modified compression intercept to use the ZLibEx unit. + + + Rev 1.8 2004.02.09 9:56:00 PM czhower + Fixed for lib changes. + + + Rev 1.7 5/12/2003 12:31:00 AM GGrieve + Get compiling again with DotNet Changes + + + Rev 1.6 10/12/2003 1:49:26 PM BGooijen + Changed comment of last checkin + + + Rev 1.5 10/12/2003 1:43:24 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + + Rev 1.3 6/27/2003 2:38:04 PM BGooijen + Fixed bug where last part was not compressed/send + + + Rev 1.2 4/10/2003 4:12:42 PM BGooijen + Added TIdServerCompressionIntercept + + + Rev 1.1 4/3/2003 2:55:48 PM BGooijen + Now calls DeinitCompressors on disconnect + + + Rev 1.0 11/14/2002 02:15:50 PM JPMugaas +} +unit IdCompressionIntercept; + +{ This file implements an Indy intercept component that compresses a data + stream using the open-source zlib compression library. In order for this + file to compile on Windows, the follow .obj files *must* be provided as + delivered with this file: + + deflate.obj + inflate.obj + inftrees.obj + trees.obj + adler32.obj + infblock.obj + infcodes.obj + infutil.obj + inffast.obj + + On Linux, the shared-object file libz.so.1 *must* be available on the + system. Most modern Linux distributions include this file. + + Simply set the CompressionLevel property to a value between 1 and 9 to + enable compressing of the data stream. A setting of 0(zero) disables + compression and the component is dormant. The sender *and* received must + have compression enabled in order to properly decompress the data stream. + They do *not* have to use the same CompressionLevel as long as they are + both set to a value between 1 and 9. + + Original Author: Allen Bauer + + This source file is submitted to the Indy project on behalf of Borland + Sofware Corporation. No warranties, express or implied are given with + this source file. +} +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdGlobal, + IdGlobalProtocols, + IdIntercept, + IdTCPClient, + IdTCPConnection, + IdZLibHeaders, + IdZLib; + +type + EIdCompressionException = class(EIdException); + EIdCompressorInitFailure = class(EIdCompressionException); + EIdDecompressorInitFailure = class(EIdCompressionException); + EIdCompressionError = class(EIdCompressionException); + EIdDecompressionError = class(EIdCompressionException); + + TIdCompressionLevel = 0..9; + + TIdCompressionIntercept = class(TIdConnectionIntercept) + protected + FCompressionLevel: TIdCompressionLevel; + FCompressRec: TZStreamRec; + FDecompressRec: TZStreamRec; + FRecvBuf: TIdBytes; + FRecvCount, FRecvSize: UInt32; + FSendBuf: TIdBytes; + FSendCount, FSendSize: UInt32; + procedure SetCompressionLevel(Value: TIdCompressionLevel); + procedure InitCompressors; + procedure DeinitCompressors; + public + destructor Destroy; override; + procedure Disconnect; override; + procedure Receive(var VBuffer: TIdBytes); override; + procedure Send(var VBuffer: TIdBytes); override; + published + property CompressionLevel: TIdCompressionLevel read FCompressionLevel write SetCompressionLevel; + end; + + TIdServerCompressionIntercept = class(TIdServerIntercept) + protected + FCompressionLevel: TIdCompressionLevel; + public + procedure Init; override; + function Accept(AConnection: TComponent): TIdConnectionIntercept; override; + published + property CompressionLevel: TIdCompressionLevel read FCompressionLevel write FCompressionLevel; + end; + + +implementation + +uses + IdResourceStringsProtocols, IdExceptionCore; + +{ TIdCompressionIntercept } + +procedure TIdCompressionIntercept.DeinitCompressors; +begin + if Assigned(FCompressRec.zalloc) then begin + deflateEnd(FCompressRec); + FillChar(FCompressRec, SizeOf(FCompressRec), 0); + end; + if Assigned(FDecompressRec.zalloc) then + begin + inflateEnd(FDecompressRec); + FillChar(FDecompressRec, SizeOf(FDecompressRec), 0); + end; +end; + +destructor TIdCompressionIntercept.Destroy; +begin + DeinitCompressors; + SetLength(FRecvBuf, 0); + SetLength(FSendBuf, 0); + inherited Destroy; +end; + +procedure TIdCompressionIntercept.Disconnect; +begin + inherited Disconnect; + DeinitCompressors; +end; + +procedure TIdCompressionIntercept.InitCompressors; +begin + if not Assigned(FCompressRec.zalloc) then + begin + FCompressRec.zalloc := IdZLibHeaders.zlibAllocMem; + FCompressRec.zfree := IdZLibHeaders.zlibFreeMem; + if deflateInit_(FCompressRec, FCompressionLevel, zlib_Version, SizeOf(FCompressRec)) <> Z_OK then + begin + raise EIdCompressorInitFailure.Create(RSZLCompressorInitializeFailure); + end; + end; + if not Assigned(FDecompressRec.zalloc) then + begin + FDecompressRec.zalloc := IdZLibHeaders.zlibAllocMem; + FDecompressRec.zfree := IdZLibHeaders.zlibFreeMem; + if inflateInit_(FDecompressRec, zlib_Version, SizeOf(FDecompressRec)) <> Z_OK then + begin + raise EIdDecompressorInitFailure.Create(RSZLDecompressorInitializeFailure); + end; + end; +end; + +procedure TIdCompressionIntercept.Receive(var VBuffer: TIdBytes); +var + LBuffer: TIdBytes; + LPos : integer; + nChars, C : UInt32; + StreamEnd: Boolean; +begin + // let the next Intercept in the chain decode its data first + inherited Receive(VBuffer); + + SetLength(LBuffer, 2048); + if FCompressionLevel in [1..9] then + begin + InitCompressors; + StreamEnd := False; + LPos := 0; + repeat + nChars := IndyMin(Length(VBuffer) - LPos, Length(LBuffer)); + if nChars = 0 then begin + Break; + end; + CopyTIdBytes(VBuffer, LPos, LBuffer, 0, nChars); + Inc(LPos, nChars); + FDecompressRec.next_in := PIdAnsiChar(@LBuffer[0]); + FDecompressRec.avail_in := nChars; + FDecompressRec.total_in := 0; + while FDecompressRec.avail_in > 0 do + begin + if FRecvCount = FRecvSize then begin + if FRecvSize = 0 then begin + FRecvSize := 2048; + end else begin + Inc(FRecvSize, 1024); + end; + SetLength(FRecvBuf, FRecvSize); + end; + FDecompressRec.next_out := PIdAnsiChar(@FRecvBuf[FRecvCount]); + C := FRecvSize - FRecvCount; + FDecompressRec.avail_out := C; + FDecompressRec.total_out := 0; + case inflate(FDecompressRec, Z_NO_FLUSH) of + Z_STREAM_END: + StreamEnd := True; + Z_STREAM_ERROR, + Z_DATA_ERROR, + Z_MEM_ERROR: + raise EIdDecompressionError.Create(RSZLDecompressionError); + end; + Inc(FRecvCount, C - FDecompressRec.avail_out); + end; + until StreamEnd; + SetLength(VBuffer, FRecvCount); + CopyTIdBytes(FRecvBuf, 0, VBuffer, 0, FRecvCount); + FRecvCount := 0; + end; +end; + +procedure TIdCompressionIntercept.Send(var VBuffer: TIdBytes); +var + LBuffer: TIdBytes; + LLen, LSize: UInt32; +begin + LBuffer := nil; + if FCompressionLevel in [1..9] then + begin + InitCompressors; + // Make sure the Send buffer is large enough to hold the input data + LSize := Length(VBuffer); + if LSize > FSendSize then + begin + if LSize > 2048 then begin + FSendSize := LSize + (LSize + 1023) mod 1024; + end else begin + FSendSize := 2048; + end; + SetLength(FSendBuf, FSendSize); + end; + + // Get the data from the input and save it off + // TODO: get rid of FSendBuf and use ABuffer directly + FSendCount := LSize; + CopyTIdBytes(VBuffer, 0, FSendBuf, 0, FSendCount); + FCompressRec.next_in := PIdAnsiChar(@FSendBuf[0]); + FCompressRec.avail_in := FSendCount; + FCompressRec.avail_out := 0; + + // clear the output stream in preparation for compression + SetLength(VBuffer, 0); + SetLength(LBuffer, 1024); + + // As long as data is being outputted, keep compressing + while FCompressRec.avail_out = 0 do + begin + FCompressRec.next_out := PIdAnsiChar(@LBuffer[0]); + FCompressRec.avail_out := Length(LBuffer); + case deflate(FCompressRec, Z_SYNC_FLUSH) of + Z_STREAM_ERROR, + Z_DATA_ERROR, + Z_MEM_ERROR: raise EIdCompressionError.Create(RSZLCompressionError); + end; + // Place the compressed data into the output stream + LLen := Length(VBuffer); + SetLength(VBuffer, LLen + UInt32(Length(LBuffer)) - FCompressRec.avail_out); + CopyTIdBytes(LBuffer, 0, VBuffer, LLen, UInt32(Length(LBuffer)) - FCompressRec.avail_out); + end; + end; + + // let the next Intercept in the chain encode its data next + inherited Send(VBuffer); +end; + +procedure TIdCompressionIntercept.SetCompressionLevel(Value: TIdCompressionLevel); +begin + if Value < 0 then begin + Value := 0; + end else if Value > 9 then begin + Value := 9; + end; + if Value <> FCompressionLevel then begin + DeinitCompressors; + FCompressionLevel := Value; + end; +end; + +{ TIdServerCompressionIntercept } + +procedure TIdServerCompressionIntercept.Init; +begin +end; + +function TIdServerCompressionIntercept.Accept(AConnection: TComponent): TIdConnectionIntercept; +begin + Result := TIdCompressionIntercept.Create(nil); + TIdCompressionIntercept(Result).CompressionLevel := CompressionLevel; +end; + +end. + diff --git a/indy/Protocols/IdCompressorZLib.pas b/indy/Protocols/IdCompressorZLib.pas new file mode 100644 index 0000000..1426fd9 --- /dev/null +++ b/indy/Protocols/IdCompressorZLib.pas @@ -0,0 +1,332 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 3/5/2005 3:33:54 PM JPMugaas + Fix for some compiler warnings having to do with TStream.Read being platform + specific. This was fixed by changing the Compressor API to use TIdStreamVCL + instead of TStream. I also made appropriate adjustments to other units for + this. + + Rev 1.8 10/24/2004 2:40:28 PM JPMugaas + Made a better fix for the problem with SmartFTP. It turns out that we may + not be able to avoid a Z_BUF_ERROR in some cases. + + Rev 1.7 10/24/2004 11:17:08 AM JPMugaas + Reimplemented ZLIB Decompression in FTP better. It now should work properly + at ftp://ftp.smartftp.com. + + Rev 1.6 9/16/2004 3:24:04 AM JPMugaas + TIdFTP now compresses to the IOHandler and decompresses from the IOHandler. + + Noted some that the ZLib code is based was taken from ZLibEx. + + Rev 1.4 9/11/2004 10:58:04 AM JPMugaas + FTP now decompresses output directly to the IOHandler. + + Rev 1.3 6/21/2004 12:10:52 PM JPMugaas + Attempt to expand the ZLib support for Int64 support. + + Rev 1.2 2/21/2004 3:32:58 PM JPMugaas + Foxed for Unit rename. + + Rev 1.1 2/14/2004 9:59:50 PM JPMugaas + Reworked the API. There is now a separate API for the Inflate_ and + InflateInit2_ functions as well as separate functions for DeflateInit_ and + DeflateInit2_. This was required for FTP. The API also includes an optional + output stream for the servers. + + Rev 1.0 2/12/2004 11:27:22 PM JPMugaas + New compressor based on ZLibEx. +} + +unit IdCompressorZLib; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdIOHandler, + IdZLibCompressorBase, + IdZLibHeaders; + +type + TIdCompressorZLib = class(TIdZLibCompressorBase) + protected + function GetIsReady : Boolean; override; + procedure InternalDecompressStream(LZstream: TZStreamRec; AIOHandler : TIdIOHandler; + AOutStream: TStream); + public + + procedure DeflateStream(AInStream, AOutStream : TStream; + const ALevel : TIdCompressionLevel=0); override; + procedure InflateStream(AInStream, AOutStream : TStream); override; + + procedure CompressStream(AInStream, AOutStream : TStream; const ALevel : TIdCompressionLevel; const AWindowBits, AMemLevel, + AStrategy: Integer); override; + procedure DecompressStream(AInStream, AOutStream : TStream; const AWindowBits : Integer); override; + procedure CompressFTPToIO(AInStream : TStream; AIOHandler : TIdIOHandler; + const ALevel, AWindowBits, AMemLevel, AStrategy: Integer); override; + procedure DecompressFTPFromIO(AIOHandler : TIdIOHandler; AOutputStream : TStream; + const AWindowBits : Integer); override; + end; + + EIdCompressionException = class(EIdException); + EIdCompressorInitFailure = class(EIdCompressionException); + EIdDecompressorInitFailure = class(EIdCompressionException); + EIdCompressionError = class(EIdCompressionException); + EIdDecompressionError = class(EIdCompressionException); + +implementation + +uses + IdAntiFreezeBase, IdComponent, IdResourceStringsProtocols, IdGlobal, + IdGlobalProtocols, IdZLib, SysUtils; + +const + bufferSize = 32768; + +{ TIdCompressorZLib } + +procedure TIdCompressorZLib.InternalDecompressStream( + LZstream: TZStreamRec; AIOHandler: TIdIOHandler; AOutStream: TStream); +{Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler} +var + zresult : Integer; + outBuffer: Array [0..bufferSize-1] of TIdAnsiChar; + inSize : Integer; + outSize : Integer; + LBuf : TIdBytes; + + function RawReadFromIOHandler(ABuffer : TIdBytes; AOIHandler : TIdIOHandler; AMax : Integer) : Integer; + begin + //We don't use the IOHandler.ReadBytes because that will check + // for disconnect and raise an exception that we don't want. + + // RLebeau 3/26/09: we need to raise exceptions here! The socket component + // that is performing the IO needs to know what is happening on the socket... + + { + repeat + AIOHandler.CheckForDataOnSource(1); + Result := IndyMin(AIOHandler.InputBuffer.Size, AMax); + if Result > 0 then begin + AIOHandler.InputBuffer.ExtractToBytes(ABuffer, Result, False); + Break; + end; + until not AIOHandler.Connected; + } + + // copied from TIdIOHandler.ReadStream() and trimmed down... + try + AIOHandler.ReadBytes(ABuffer, AMax, False); + except + on E: Exception do begin + // RLebeau - ReadFromSource() inside of ReadBytes() + // could have filled the InputBuffer with more bytes + // than actually requested, so don't extract too + // many bytes here... + AMax := IndyMin(AMax, AIOHandler.InputBuffer.Size); + AIOHandler.InputBuffer.ExtractToBytes(ABuffer, AMax, False); + if not (E is EIdConnClosedGracefully) then begin + raise; + end; + end; + end; + TIdAntiFreezeBase.DoProcess; + Result := AMax; + end; + +begin + SetLength(LBuf, bufferSize); + repeat + inSize := RawReadFromIOHandler(LBuf, AIOHandler, bufferSize); + if inSize < 1 then begin + Break; + end; + + LZstream.next_in := PIdAnsiChar(@LBuf[0]); + LZstream.avail_in := inSize; + + repeat + LZstream.next_out := @outBuffer[0]; + LZstream.avail_out := bufferSize; + DCheck(inflate(LZstream,Z_NO_FLUSH)); + outSize := bufferSize - LZstream.avail_out; + AOutStream.Write(outBuffer, outSize); + until (LZstream.avail_in = 0) and (LZstream.avail_out > 0); + until False; + { From the ZLIB FAQ at http://www.gzip.org/zlib/FAQ.txt + + 5. deflate() or inflate() returns Z_BUF_ERROR + + Before making the call, make sure that avail_in and avail_out are not + zero. When setting the parameter flush equal to Z_FINISH, also make sure + that avail_out is big enough to allow processing all pending input. + Note that a Z_BUF_ERROR is not fatal--another call to deflate() or + inflate() can be made with more input or output space. A Z_BUF_ERROR + may in fact be unavoidable depending on how the functions are used, since + it is not possible to tell whether or not there is more output pending + when strm.avail_out returns with zero. +} + repeat + LZstream.next_out := @outBuffer[0]; + LZstream.avail_out := bufferSize; + + zresult := inflate(LZstream, Z_FINISH); + if zresult <> Z_BUF_ERROR then + begin + zresult := DCheck(zresult); + end; + outSize := bufferSize - LZstream.avail_out; + AOutStream.Write(outBuffer, outSize); + + until ((zresult = Z_STREAM_END) and (LZstream.avail_out > 0)) or (zresult = Z_BUF_ERROR); + + DCheck(inflateEnd(LZstream)); +end; + +procedure TIdCompressorZLib.DecompressFTPFromIO(AIOHandler : TIdIOHandler; AOutputStream : TStream; + const AWindowBits : Integer); +{Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler} +var + Lzstream: TZStreamRec; + LWinBits : Integer; +begin + AIOHandler.BeginWork(wmRead); + try + FillChar(Lzstream,SizeOf(TZStreamRec),0); + { + This is a workaround for some clients and servers that do not send decompression + headers. The reason is that there's an inconsistancy in Internet Drafts for ZLIB + compression. One says to include the headers while an older one says do not + include the headers. + + If you add 32 to the Window Bits parameter, + } + LWinBits := AWindowBits; + if LWinBits > 0 then + begin + LWinBits := Abs( LWinBits) + 32; + end; + LZstream.zalloc := zlibAllocMem; + LZstream.zfree := zlibFreeMem; + DCheck(inflateInit2_(Lzstream,LWinBits,ZLIB_VERSION,SizeOf(TZStreamRec))); + + InternalDecompressStream(Lzstream,AIOHandler,AOutputStream); + finally + AIOHandler.EndWork(wmRead); + end; +end; + +procedure TIdCompressorZLib.CompressFTPToIO(AInStream : TStream; + AIOHandler : TIdIOHandler; + const ALevel, AWindowBits, AMemLevel, AStrategy: Integer); +{Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler} +var + LCompressRec : TZStreamRec; + + zresult : Integer; + inBuffer : Array [0..bufferSize-1] of TIdAnsiChar; + outBuffer: Array [0..bufferSize-1] of TIdAnsiChar; + inSize : Integer; + outSize : Integer; +begin + AIOHandler.BeginWork(wmWrite, AInStream.Size); + try + FillChar(LCompressRec, SizeOf(TZStreamRec), 0); + CCheck( deflateInit2_(LCompressRec, ALevel, Z_DEFLATED, AWindowBits, AMemLevel, + AStrategy, ZLIB_VERSION, SizeOf(LCompressRec))); + + inSize := AInStream.Read(inBuffer, bufferSize); + + while inSize > 0 do + begin + LCompressRec.next_in := @inBuffer[0]; + LCompressRec.avail_in := inSize; + + repeat + LCompressRec.next_out := @outBuffer[0]; + LCompressRec.avail_out := bufferSize; + + CCheck(deflate(LCompressRec,Z_NO_FLUSH)); + + // outSize := zstream.next_out - outBuffer; + outSize := bufferSize - LCompressRec.avail_out; + if outsize <> 0 then + begin + AIOHandler.Write(RawToBytes(outBuffer, outSize)); + end; + until (LCompressRec.avail_in = 0) and (LCompressRec.avail_out > 0); + + inSize := AInStream.Read(inBuffer, bufferSize); + end; + + repeat + LCompressRec.next_out := @outBuffer[0]; + LCompressRec.avail_out := bufferSize; + + zresult := CCheck(deflate(LCompressRec,Z_FINISH)); + + // outSize := zstream.next_out - outBuffer; + outSize := bufferSize - LCompressRec.avail_out; + + // outStream.Write(outBuffer,outSize); + if outSize <> 0 then + begin + AIOHandler.Write(RawToBytes(outBuffer, outSize)); + end; + until (zresult = Z_STREAM_END) and (LCompressRec.avail_out > 0); + + CCheck(deflateEnd(LCompressRec)); + + finally + AIOHandler.EndWork(wmWrite); + end; +end; + +procedure TIdCompressorZLib.CompressStream(AInStream,AOutStream : TStream; + const ALevel : TIdCompressionLevel; + const AWindowBits, AMemLevel, AStrategy: Integer); + +begin + IdZLib.IndyCompressStream(AInStream,AOutStream,ALevel,AWindowBits,AMemLevel,AStrategy); +end; + +procedure TIdCompressorZLib.DecompressStream(AInStream, AOutStream : TStream; const AWindowBits : Integer); +begin + IdZLib.IndyDeCompressStream(AInStream,AOutStream, AWindowBits); +end; + +procedure TIdCompressorZLib.DeflateStream(AInStream, AOutStream : TStream; const ALevel : TIdCompressionLevel=0); +begin + IdZLib.IndyCompressStream(AInStream,AOutStream,ALevel); +end; + +function TIdCompressorZLib.GetIsReady: Boolean; +begin + Result := IdZLibHeaders.Loaded; +end; + +procedure TIdCompressorZLib.InflateStream(AInStream, AOutStream : TStream); +begin + IdZlib.DeCompressStream(AInStream,AOutStream); +end; + +end. diff --git a/indy/Protocols/IdConnectThroughHttpProxy.pas b/indy/Protocols/IdConnectThroughHttpProxy.pas new file mode 100644 index 0000000..bc9d960 --- /dev/null +++ b/indy/Protocols/IdConnectThroughHttpProxy.pas @@ -0,0 +1,153 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 11/12/2004 11:31:06 AM JPMugaas + IPv6 expansions. + + Rev 1.5 2004.02.03 5:45:00 PM czhower + Name changes + + Rev 1.4 10/19/2003 11:48:12 AM DSiders + Added localization comments. + + Rev 1.3 4/5/2003 7:27:48 PM BGooijen + Checks for errors, added authorisation + + Rev 1.2 4/1/2003 4:14:22 PM BGooijen + Fixed + cleaned up + + Rev 1.1 2/24/2003 08:20:46 PM JPMugaas + Now should compile with new code. + + Rev 1.0 11/14/2002 02:16:10 PM JPMugaas +} + +unit IdConnectThroughHttpProxy; + +{ + implements: + http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdCustomTransparentProxy, IdGlobal, IdIOHandler; + +type + TIdConnectThroughHttpProxy = class(TIdCustomTransparentProxy) + private + FAuthorizationRequired: Boolean; + protected + FEnabled: Boolean; + function GetEnabled: Boolean; override; + procedure SetEnabled(AValue: Boolean); override; + procedure MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override; + procedure DoMakeConnection(AIOHandler: TIdIOHandler; const AHost: string; + const APort: TIdPort; const ALogin:boolean; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);virtual; + public + published + property Enabled; + property Host; + property Port; + property ChainedProxy; + property Username; + property Password; + end; + +implementation + +uses + IdCoderMIME, IdExceptionCore, IdHeaderList, IdGlobalProtocols, SysUtils; + +{ TIdConnectThroughHttpProxy } + +function TIdConnectThroughHttpProxy.GetEnabled: Boolean; +begin + Result := FEnabled; +end; + +procedure TIdConnectThroughHttpProxy.DoMakeConnection(AIOHandler: TIdIOHandler; + const AHost: string; const APort: TIdPort; const ALogin: Boolean; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +var + LStatus: string; + LResponseCode: Integer; + LHeaders: TIdHeaderList; + LContentLength: Int64; + LEncoder: TIdEncoderMIME; +begin + LHeaders := TIdHeaderList.Create(QuoteHTTP); + try + AIOHandler.WriteLn(IndyFormat('CONNECT %s:%d HTTP/1.0', [AHost,APort])); {do not localize} + if ALogin then begin + LEncoder := TIdEncoderMIME.Create; + try + AIOHandler.WriteLn('Proxy-Authorization: Basic ' + LEncoder.Encode(Username + ':' + Password)); {do not localize} + finally + LEncoder.Free; + end; + end; + AIOHandler.WriteLn; + LStatus := AIOHandler.ReadLn; + if LStatus <> '' then begin // if empty response then we assume it succeeded + AIOHandler.Capture(LHeaders, '', False); + // TODO: support chunked replies... + LContentLength := IndyStrToInt64(LHeaders.Values['Content-Length'], -1); {do not localize} + if LContentLength > 0 then begin + AIOHandler.Discard(LContentLength); + end; + Fetch(LStatus);// to remove the http/1.0 or http/1.1 + LResponseCode := IndyStrToInt(Fetch(LStatus, ' ', False), 200); // if invalid response then we assume it succeeded + if (LResponseCode = 407) and (not ALogin) and ((Length(Username) > 0) or (Length(Password) > 0)) then begin // authorization required + if TextIsSame(LHeaders.Values['Proxy-Connection'], 'close') or {do not localize} + TextIsSame(LHeaders.Values['Connection'], 'close') then begin {do not localize} + // need to reconnect before trying again with login + AIOHandler.Close; + FAuthorizationRequired := True; + try + AIOHandler.Open; + finally + FAuthorizationRequired := False; + end; + end else begin + // still connected so try again with login + DoMakeConnection(AIOHandler, AHost, APort, True); + end; + end + else if not (LResponseCode in [200]) then begin // maybe more responsecodes to add + raise EIdHttpProxyError.Create(LStatus);//BGO: TODO: maybe split into more exceptions? + end; + end; + finally + LHeaders.Free; + end; +end; + +procedure TIdConnectThroughHttpProxy.MakeConnection(AIOHandler: TIdIOHandler; + const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); +begin + DoMakeConnection(AIOHandler, AHost, APort, FAuthorizationRequired); +end; + +procedure TIdConnectThroughHttpProxy.SetEnabled(AValue: Boolean); +begin + FEnabled := AValue; +end; + +end. diff --git a/indy/Protocols/IdContainers.pas b/indy/Protocols/IdContainers.pas new file mode 100644 index 0000000..57f5928 --- /dev/null +++ b/indy/Protocols/IdContainers.pas @@ -0,0 +1,281 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 10/26/2004 11:08:10 PM JPMugaas + Updated refs. + + Rev 1.6 28.09.2004 21:35:28 Andreas Hausladen + Added TIdObjectList.Assign method for missing Delphi 5 TList.Assign + + Rev 1.5 1/4/2004 12:09:00 AM BGooijen + Commented out Notify, this doesn't exist in DotNet, and doesn't do anything + anyways + + Rev 1.4 3/13/2003 11:10:52 AM JPMugaas + Fixed warning message. + + Rev 1.3 2/8/2003 04:33:34 AM JPMugaas + Commented out a free statement in the TIdObjectList.Notify method because it + was causing instability in some new IdFTPList code I was working on. + Added a TStringList descendent object that implements a buble sort. That + should require less memory than a QuickSort. This also replaces the + TStrings.CustomSort because that is not supported in D4. + + Rev 1.2 2/7/2003 10:33:48 AM JPMugaas + Added BoubleSort to TIdObjectList to facilitate some work. + + Rev 1.1 12/2/2002 04:32:30 AM JPMugaas + Fixed minor compile errors. + + Rev 1.0 11/14/2002 02:16:14 PM JPMugaas + + Revision 1.0 2001-02-20 02:02:09-05 dsiders + Initial revision +} + +{********************************************************************} +{* IdContainers.pas *} +{* *} +{* Provides compatibility with the Contnr.pas unit from *} +{* Delphi 5 not found in Delphi 4. *} +{* *} +{* Based on ideas from the Borland VCL Contnr.pas interface. *} +{* *} +{********************************************************************} + +unit IdContainers; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes + {$IFDEF HAS_UNIT_Generics_Collections} + , System.Generics.Collections + {$ELSE} + {$IFDEF HAS_TObjectList} + , Contnrs + {$ENDIF} + {$ENDIF} + ; + +type + {$IFDEF HAS_GENERICS_TObjectList} + TIdSortCompare = function(AItem1, AItem2 : T): Integer; + {$ELSE} + TIdSortCompare = function(AItem1, AItem2 : TObject): Integer; + {$ENDIF} + + {TIdObjectList} + + {$IFDEF HAS_GENERICS_TObjectList} + TIdObjectList = class(TObjectList) + public + procedure BubbleSort(ACompare : TIdSortCompare); + procedure Assign(Source: TIdObjectList); + end; + {$ELSE} + {$IFDEF HAS_TObjectList} + TIdObjectList = class(TObjectList) + public + procedure BubbleSort(ACompare : TIdSortCompare); + // Delphi 5 does not have TList.Assign. + // This is a simplyfied Assign method that does only support the copy operation. + procedure Assign(Source: TIdObjectList); {$IFDEF VCL_6_OR_ABOVE}reintroduce;{$ENDIF} + end; + {$ELSE} + TIdObjectList = class(TList) + private + FOwnsObjects: Boolean; + protected + function GetItem(AIndex: Integer): TObject; + procedure SetItem(AIndex: Integer; AObject: TObject); + {$IFNDEF DOTNET} + procedure Notify(AItemPtr: Pointer; AAction: TListNotification); override; + {$ENDIF} + public + constructor Create; overload; + constructor Create(AOwnsObjects: Boolean); overload; + procedure BubbleSort(ACompare : TIdSortCompare); + function Add(AObject: TObject): Integer; + function FindInstanceOf(AClassRef: TClass; AMatchExact: Boolean = True; AStartPos: Integer = 0): Integer; + function IndexOf(AObject: TObject): Integer; + function Remove(AObject: TObject): Integer; + procedure Insert(AIndex: Integer; AObject: TObject); + procedure Assign(Source: TIdObjectList); + property Items[AIndex: Integer]: TObject read GetItem write SetItem; default; + property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects; + end; + {$ENDIF} + {$ENDIF} + + TIdStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer; + + TIdBubbleSortStringList = class(TStringList) + public + procedure BubbleSort(ACompare: TIdStringListSortCompare); virtual; + end; + +implementation + +{$IFDEF VCL_XE3_OR_ABOVE} +uses + System.Types; +{$ENDIF} + +{ TIdObjectList } + +{$IFNDEF HAS_GENERICS_TObjectList} + {$IFNDEF HAS_TObjectList} + +constructor TIdObjectList.Create; +begin + inherited Create; + FOwnsObjects := True; +end; + +constructor TIdObjectList.Create(AOwnsObjects: Boolean); +begin + inherited Create; + FOwnsObjects := AOwnsObjects; +end; + +function TIdObjectList.Add(AObject: TObject): Integer; +begin + Result := inherited Add(AObject); +end; + +function TIdObjectList.FindInstanceOf(AClassRef: TClass; + AMatchExact: Boolean = True; AStartPos: Integer = 0): Integer; +var + iPos: Integer; + bIsAMatch: Boolean; +begin + Result := -1; // indicates item is not in object list + + for iPos := AStartPos to Count - 1 do + begin + bIsAMatch := + ((not AMatchExact) and Items[iPos].InheritsFrom(AClassRef)) or + (AMatchExact and (Items[iPos].ClassType = AClassRef)); + + if bIsAMatch then + begin + Result := iPos; + Break; + end; + end; +end; + +function TIdObjectList.GetItem(AIndex: Integer): TObject; +begin + Result := inherited Items[AIndex]; +end; + +function TIdObjectList.IndexOf(AObject: TObject): Integer; +begin + Result := inherited IndexOf(AObject); +end; + +procedure TIdObjectList.Insert(AIndex: Integer; AObject: TObject); +begin + inherited Insert(AIndex, AObject); +end; + +{$IFNDEF DOTNET} +procedure TIdObjectList.Notify(AItemPtr: Pointer; AAction: TListNotification); +begin + if OwnsObjects and (AAction = lnDeleted) then begin + TObject(AItemPtr).Free; + end; + inherited Notify(AItemPtr, AAction); +end; +{$ENDIF} + +function TIdObjectList.Remove(AObject: TObject): Integer; +begin + Result := inherited Remove(AObject); +end; + +procedure TIdObjectList.SetItem(AIndex: Integer; AObject: TObject); +begin + inherited Items[AIndex] := AObject; +end; + + {$ENDIF} +{$ENDIF} + +{$IFDEF HAS_GENERICS_TObjectList} +procedure TIdObjectList.BubbleSort(ACompare: TIdSortCompare); +{$ELSE} +procedure TIdObjectList.BubbleSort(ACompare: TIdSortCompare); +{$ENDIF} +var + i, n, newn : Integer; +begin + n := Count; + repeat + newn := 0; + for i := 1 to n-1 do begin + if ACompare(Items[i-1], Items[i]) > 0 then begin + Exchange(i-1, i); + newn := i; + end; + end; + n := newn; + until n = 0; +end; + +{$IFDEF HAS_GENERICS_TObjectList} +procedure TIdObjectList.Assign(Source: TIdObjectList); +{$ELSE} +procedure TIdObjectList.Assign(Source: TIdObjectList); +{$ENDIF} +var + I: Integer; +begin + // Delphi 5 does not have TList.Assign. + // This is a simplyfied Assign method that does only support the copy operation. + Clear; + Capacity := Source.Capacity; + for I := 0 to Source.Count - 1 do begin + Add(Source[I]); + end; +end; + +{ TIdBubbleSortStringList } + +procedure TIdBubbleSortStringList.BubbleSort(ACompare: TIdStringListSortCompare); +var + i, n, newn : Integer; +begin + n := Count; + repeat + newn := 0; + for i := 1 to n-1 do begin + if ACompare(Self, i-1, i) > 0 then begin + Exchange(i-1, i); + newn := i; + end; + end; + n := newn; + until n = 0; +end; + +end. diff --git a/indy/Protocols/IdCookie.pas b/indy/Protocols/IdCookie.pas new file mode 100644 index 0000000..021c18e --- /dev/null +++ b/indy/Protocols/IdCookie.pas @@ -0,0 +1,1176 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 2004.10.27 9:17:46 AM czhower + For TIdStrings + + Rev 1.5 10/26/2004 11:08:08 PM JPMugaas + Updated refs. + + Rev 1.4 13.04.2004 12:56:44 ARybin + M$ IE behavior + + Rev 1.3 2004.02.03 5:45:00 PM czhower + Name changes + + Rev 1.2 2004.01.22 6:09:02 PM czhower + IdCriticalSection + + Rev 1.1 1/22/2004 7:09:58 AM JPMugaas + Tried to fix AnsiSameText depreciation. + + Rev 1.0 11/14/2002 02:16:20 PM JPMugaas + + Mar-31-2001 Doychin Bondzhev + - Changes in the class heirarchy to implement Netscape specification[Netscape], + RFC 2109[RFC2109] & 2965[RFC2965] + + Feb-2001 Doychin Bondzhev + - Initial release +} + +unit IdCookie; + +{ + Implementation of the HTTP State Management Mechanism as specified in RFC 6265. + Author: Remy Lebeau (remy@lebeausoftware.org) + Copyright: (c) Chad Z. Hower and The Indy Team. + + TIdCookie - The base code used in all cookies. + +REFERENCES +------------------- + [RFC6265] Barth, A, "HTTP State Management Mechanism", + RFC 6265, April 2011. + + [DRAFT-ORIGIN-01] Pettersen, Y, "Identifying origin server of HTTP Cookies", + Internet-Draft, March 07, 2010. + http://www.ietf.org/id/draft-pettersen-cookie-origin-01.txt + + [DRAFT-COOKIEv2-05] Pettersen, Y, "HTTP State Management Mechanism v2", + Internet-Draft, March 07, 2010. + http://www.ietf.org/id/draft-pettersen-cookie-v2-05.txt +} + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdGlobal, IdException, IdGlobalProtocols, IdURI, + SysUtils; + +type + { Base Cookie class as described in [RFC6265] } + TIdCookie = class(TCollectionItem) + protected + FDomain: String; + FExpires: TDateTime; + FHttpOnly: Boolean; + FName: String; + FPath: String; + FSecure: Boolean; + FValue: String; + FCreatedAt: TDateTime; + FHostOnly: Boolean; + FLastAccessed: TDateTime; + FPersistent: Boolean; + + function GetIsExpired: Boolean; + + function GetServerCookie: String; virtual; + function GetClientCookie: String; virtual; + + function GetMaxAge: Int64; + + public + constructor Create(ACollection: TCollection); override; + destructor Destroy; override; + + procedure Assign(Source: TPersistent); override; + + function IsAllowed(AURI: TIdURI; SecureOnly: Boolean): Boolean; virtual; + + function ParseClientCookie(const ACookieText: String): Boolean; virtual; + function ParseServerCookie(const ACookieText: String; AURI: TIdURI): Boolean; virtual; + + property ClientCookie: String read GetClientCookie; + property CookieName: String read FName write FName; + property CookieText: String read GetServerCookie; // {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPECATED_MSG} 'Use ServerCookie property instead'{$ENDIF};{$ENDIF} + property Domain: String read FDomain write FDomain; + property Expires: TDateTime read FExpires write FExpires; + property HttpOnly: Boolean read FHttpOnly write FHttpOnly; + property Path: String read FPath write FPath; + property Secure: Boolean read FSecure write FSecure; + property ServerCookie: String read GetServerCookie; + property Value: String read FValue write FValue; + + property MaxAge: Int64 read GetMaxAge; + + property CreatedAt: TDateTime read FCreatedAt write FCreatedAt; + property IsExpired: Boolean read GetIsExpired; + property HostOnly: Boolean read FHostOnly write FHostOnly; + property LastAccessed: TDateTime read FLastAccessed write FLastAccessed; + property Persistent: Boolean read FPersistent write FPersistent; + end; + + TIdCookieClass = class of TIdCookie; + + { The Cookie collection } + + {$IFDEF HAS_GENERICS_TList} + TIdCookieList = TList; + {$ELSE} + TIdCookieList = class(TList) + protected + function GetCookie(Index: Integer): TIdCookie; + procedure SetCookie(Index: Integer; AValue: TIdCookie); + public + function IndexOfCookie(ACookie: TIdCookie): Integer; + property Cookies[Index: Integer]: TIdCookie read GetCookie write SetCookie; default; + end; + {$ENDIF} + + TIdCookieAccess = (caRead, caReadWrite); + + TIdCookies = class(TOwnedCollection) + protected + FCookieList: TIdCookieList; + FRWLock: TMultiReadExclusiveWriteSynchronizer; + + function GetCookieByNameAndDomain(const AName, ADomain: string): TIdCookie; + function GetCookie(Index: Integer): TIdCookie; + procedure SetCookie(Index: Integer; const Value: TIdCookie); + + public + constructor Create(AOwner: TPersistent); + destructor Destroy; override; + + function Add: TIdCookie; reintroduce; + function AddCookie(ACookie: TIdCookie; AURI: TIdURI; AReplaceOld: Boolean = True): Boolean; + + function AddClientCookie(const ACookie: string): TIdCookie; + procedure AddClientCookies(const ACookie: string); overload; + procedure AddClientCookies(const ACookies: TStrings); overload; + + function AddServerCookie(const ACookie: string; AURI: TIdURI): TIdCookie; + procedure AddServerCookies(const ACookies: TStrings; AURI: TIdURI); + + procedure AddCookies(ASource: TIdCookies); + + procedure Assign(ASource: TPersistent); override; + procedure Clear; reintroduce; + + function GetCookieIndex(const AName: string; FirstIndex: Integer = 0): Integer; overload; + function GetCookieIndex(const AName, ADomain: string; FirstIndex: integer = 0): Integer; overload; + + function LockCookieList(AAccessType: TIdCookieAccess): TIdCookieList; + procedure UnlockCookieList(AAccessType: TIdCookieAccess); + + property Cookie[const AName, ADomain: string]: TIdCookie read GetCookieByNameAndDomain; + property Cookies[Index: Integer]: TIdCookie read GetCookie write SetCookie; Default; + end; + + EIdCookieError = class(EIdException); + +function IsDomainMatch(const AUriHost, ACookieDomain: String): Boolean; +function IsPathMatch(const AUriPath, ACookiePath: String): Boolean; + +function CanonicalizeHostName(const AHost: String): String; + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.Types, + {$ENDIF} + IdAssignedNumbers, IdResourceStringsProtocols; + +function GetDefaultPath(const AURL: TIdURI): String; +var + Idx: Integer; +begin + { + Per RFC 6265, Section 5.1.4: + + The user agent MUST use an algorithm equivalent to the following + algorithm to compute the default-path of a cookie: + + 1. Let uri-path be the path portion of the request-uri if such a + portion exists (and empty otherwise). For example, if the + request-uri contains just a path (and optional query string), + then the uri-path is that path (without the %x3F ("?") character + or query string), and if the request-uri contains a full + absoluteURI, the uri-path is the path component of that URI. + + 2. If the uri-path is empty or if the first character of the uri- + path is not a %x2F ("/") character, output %x2F ("/") and skip + the remaining steps. + + 3. If the uri-path contains no more than one %x2F ("/") character, + output %x2F ("/") and skip the remaining steps. + + 4. Output the characters of the uri-path from the first character up + to, but not including, the right-most %x2F ("/"). + } + + if TextStartsWith(AURL.Path, '/') then begin {do not localize} + Idx := RPos('/', AURL.Path); {do not localize} + if Idx > 1 then begin + Result := Copy(AURL.Path, 1, Idx-1); + Exit; + end; + end; + Result := '/'; {do not localize} +end; + +function CanonicalizeHostName(const AHost: String): String; +begin + // TODO: implement this + { + Per RFC 6265 Section 5.1.2: + + A canonicalized host name is the string generated by the following + algorithm: + + 1. Convert the host name to a sequence of individual domain name + labels. + + 2. Convert each label that is not a Non-Reserved LDH (NR_LDH) label, + to an A-label (see Section 2.3.2.1 of [RFC5890] for the fomer + and latter), or to a "punycode label" (a label resulting from the + "ToASCII" conversion in Section 4 of [RFC3490]), as appropriate + (see Section 6.3 of this specification). + + 3. Concatentate the resulting labels, separated by a %x2E (".") + character. + } + Result := AHost; +end; + +function IsDomainMatch(const AUriHost, ACookieDomain: String): Boolean; +var + LHost, LDomain: String; +begin + { + Per RFC 6265 Section 5.1.3: + + A string domain-matches a given domain string if at least one of the + following conditions hold: + + o The domain string and the string are identical. (Note that both + the domain string and the string will have been canonicalized to + lower case at this point.) + + o All of the following conditions hold: + + * The domain string is a suffix of the string. + + * The last character of the string that is not included in the + domain string is a %x2E (".") character. + + * The string is a host name (i.e., not an IP address). + } + + Result := False; + LHost := CanonicalizeHostName(AUriHost); + LDomain := CanonicalizeHostName(ACookieDomain); + if (LHost <> '') and (LDomain <> '') then begin + if TextIsSame(LHost, LDomain) then begin + Result := True; + end + else if TextEndsWith(LHost, LDomain) then + begin + if TextEndsWith(Copy(LHost, 1, Length(LHost)-Length(LDomain)), '.') then begin + Result := IsHostName(LHost); + end; + end; + end; +end; + +function IsPathMatch(const AUriPath, ACookiePath: String): Boolean; +begin + { + Per RFC 6265 Section 5.1.4: + + A request-path path-matches a given cookie-path if at least one of + the following conditions hold: + + o The cookie-path and the request-path are identical. + + o The cookie-path is a prefix of the request-path and the last + character of the cookie-path is %x2F ("/"). + + o The cookie-path is a prefix of the request-path and the first + character of the request-path that is not included in the cookie- + path is a %x2F ("/") character. + } + Result := TextIsSame(AUriPath, ACookiePath) or + ( + TextStartsWith(AUriPath, ACookiePath) and + ( + TextEndsWith(ACookiePath, '/') or + CharEquals(AUriPath, Length(ACookiePath)+1, '/') + ) + ); +end; + +function IsHTTP(const AProtocol: String): Boolean; +begin + Result := PosInStrArray(AProtocol, ['http', 'https'], False) <> -1; {do not localize} +end; + +{ base functions used for construction of Cookie text } + +procedure AddCookieProperty(var VCookie: String; + const AProperty, AValue: String); +begin + if Length(AValue) > 0 then + begin + if Length(VCookie) > 0 then begin + VCookie := VCookie + '; '; {Do not Localize} + end; + // TODO: encode illegal characters? + VCookie := VCookie + AProperty + '=' + AValue; {Do not Localize} + end; +end; + +procedure AddCookieFlag(var VCookie: String; const AFlag: String); +begin + if Length(VCookie) > 0 then begin + VCookie := VCookie + '; '; { Do not Localize } + end; + VCookie := VCookie + AFlag; +end; + +{ TIdCookieList } + +{$IFNDEF HAS_GENERICS_TList} + +function TIdCookieList.GetCookie(Index: Integer): TIdCookie; +begin + Result := TIdCookie(Items[Index]); +end; + +procedure TIdCookieList.SetCookie(Index: Integer; AValue: TIdCookie); +begin + Items[Index] := AValue; +end; + +function TIdCookieList.IndexOfCookie(ACookie: TIdCookie): Integer; +begin + for Result := 0 to Count - 1 do + begin + if GetCookie(Result) = ACookie then begin + Exit; + end; + end; + Result := -1; +end; + +{$ENDIF} + +{ TIdCookie } + +constructor TIdCookie.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + FCreatedAt := Now; + FLastAccessed := FCreatedAt; +end; + +destructor TIdCookie.Destroy; +var + LCookieList: TIdCookieList; +begin + try + if Assigned(Collection) then + begin + LCookieList := TIdCookies(Collection).LockCookieList(caReadWrite); + try + LCookieList.Remove(Self); + finally + TIdCookies(Collection).UnlockCookieList(caReadWrite); + end; + end; + finally + inherited Destroy; + end; +end; + +procedure TIdCookie.Assign(Source: TPersistent); +var + LSource: TIdCookie; +begin + if Source is TIdCookie then + begin + LSource := TIdCookie(Source); + FDomain := LSource.FDomain; + FExpires := LSource.FExpires; + FHttpOnly := LSource.FHttpOnly; + FName := LSource.FName; + FPath := LSource.FPath; + FSecure := LSource.FSecure; + FValue := LSource.FValue; + FCreatedAt := LSource.FCreatedAt; + FHostOnly := LSource.FHostOnly; + FLastAccessed := LSource.FLastAccessed; + FPersistent := LSource.FPersistent; + end else + begin + inherited Assign(Source); + end; +end; + +function TIdCookie.IsAllowed(AURI: TIdURI; SecureOnly: Boolean): Boolean; + + function MatchesHost: Boolean; + begin + if HostOnly then begin + Result := TextIsSame(CanonicalizeHostName(AURI.Host), Domain); + end else begin + Result := IsDomainMatch(AURI.Host, Domain); + end; + end; + +begin + // using the algorithm defined in RFC 6265 section 5.4... + Result := MatchesHost and IsPathMatch(AURI.Path, Path) and + ((not Secure) or (Secure and SecureOnly)) and + ((not HttpOnly) or (HttpOnly and IsHTTP(AURI.Protocol))); +end; + +{$IFNDEF HAS_TryStrToInt64} +// TODO: move this to IdGlobalProtocols... +function TryStrToInt64(const S: string; out Value: Int64): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +var + E: Integer; +begin + Val(S, Value, E); + Result := E = 0; +end; +{$ENDIF} + +function TIdCookie.ParseServerCookie(const ACookieText: String; AURI: TIdURI): Boolean; +const + cTokenSeparators = '()<>@,;:\"/[]?={} '#9; + + procedure SplitCookieText(const CookieProp: TStringList; const S: string); + var + LNameValue, LAttrs, LAttr, LName, LValue: String; + LSecs: Int64; + LExpiryTime: TDateTime; + i: Integer; + begin + I := Pos(';', ACookieText); + if I > 0 then + begin + LNameValue := Copy(ACookieText, 1, I-1); + LAttrs := Copy(ACookieText, I, MaxInt); + end else + begin + LNameValue := ACookieText; + LAttrs := ''; + end; + + I := Pos('=', LNameValue); + if I = 0 then begin + Exit; + end; + + LName := Trim(Copy(LNameValue, 1, I-1)); + if LName = '' then begin + Exit; + end; + + LValue := Trim(Copy(LNameValue, I+1, MaxInt)); + if TextStartsWith(LValue, '"') then begin + IdDelete(LValue, 1, 1); + LNameValue := LValue; + LValue := Fetch(LNameValue, '"'); + end; + CookieProp.Add(LName + '=' + LValue); + + while LAttrs <> '' do + begin + IdDelete(LAttrs, 1, 1); + I := Pos(';', LAttrs); + if I > 0 then begin + LAttr := Copy(LAttrs, 1, I-1); + LAttrs := Copy(LAttrs, I, MaxInt); + end else begin + LAttr := LAttrs; + LAttrs := ''; + end; + I := Pos('=', LAttr); + if I > 0 then begin + LName := Trim(Copy(LAttr, 1, I-1)); + LValue := Trim(Copy(LAttr, I+1, MaxInt)); + // RLebeau: RFC 6265 does not account for quoted attribute values, + // despite several complaints asking for it. We'll do it anyway in + // the hopes that the RFC will be updated to "do the right thing"... + if TextStartsWith(LValue, '"') then begin + IdDelete(LValue, 1, 1); + LNameValue := LValue; + LValue := Fetch(LNameValue, '"'); + end; + end else begin + LName := Trim(LAttr); + LValue := ''; + end; + + case PosInStrArray(LName, ['Expires', 'Max-Age', 'Domain', 'Path', 'Secure', 'HttpOnly'], False) of + 0: begin + if TryStrToInt64(LValue, LSecs) then begin + // Not in the RFCs, but some servers specify Expires as an + // integer number in seconds instead of using Max-Age... + if LSecs >= 0 then begin + // TODO: use SecsPerDay instead: + // LExpiryTime := (Now + (LSecs / SecsPerDay)); + LExpiryTime := (Now + LSecs * 1000 / MSecsPerDay); + end else begin + LExpiryTime := EncodeDate(1, 1, 1); + end; + CookieProp.Add('EXPIRES=' + FloatToStr(LExpiryTime)); + end else + begin + LExpiryTime := CookieStrToLocalDateTime(LValue); + if LExpiryTime <> 0.0 then begin + CookieProp.Add('EXPIRES=' + FloatToStr(LExpiryTime)); + end; + end; + end; + 1: begin + if TryStrToInt64(LValue, LSecs) then begin + if LSecs >= 0 then begin + // TODO: use SecsPerDay instead: + // LExpiryTime := (Now + (LSecs / SecsPerDay)); + LExpiryTime := (Now + LSecs * 1000 / MSecsPerDay); + end else begin + LExpiryTime := EncodeDate(1, 1, 1); + end; + CookieProp.Add('MAX-AGE=' + FloatToStr(LExpiryTime)); + end; + end; + 2: begin + if LValue <> '' then begin + if TextStartsWith(LValue, '.') then begin {do not localize} + LValue := Copy(LValue, 2, MaxInt); + end; + // RLebeau: have encountered one cookie in the 'Set-Cookie' header that + // includes a port number in the domain, though the RFCs do not indicate + // this is allowed. RFC 2965 defines an explicit "port" attribute in the + // 'Set-Cookie2' header for that purpose instead. We'll just strip it off + // here if present... + I := Pos(':', LValue); + if I > 0 then begin + LValue := Copy(S, 1, I-1); + end; + CookieProp.Add('DOMAIN=' + LowerCase(LValue)); + end; + end; + 3: begin + if (LValue = '') or (not TextStartsWith(LValue, '/')) then begin + LValue := GetDefaultPath(AURI); + end; + CookieProp.Add('PATH=' + LValue); + end; + 4: begin + CookieProp.Add('SECURE='); + end; + 5: begin + CookieProp.Add('HTTPONLY='); + end; + end; + end; + end; + + function GetLastValueOf(const CookieProp: TStringList; const AName: String; var VValue: String): Boolean; + var + I: Integer; + begin + Result := False; + for I := CookieProp.Count-1 downto 0 do + begin + if TextIsSame(CookieProp.Names[I], AName) then + begin + VValue := IndyValueFromIndex(CookieProp, I); + Result := True; + Exit; + end; + end; + end; + +//Darcy: moved down the variables! Android compiler... bad boy! +var + CookieProp: TStringList; + S: string; +begin + Result := False; + + // using the algorithm defined in RFC 6265 section 5.2... + + CookieProp := TStringList.Create; + try + SplitCookieText(CookieProp, S); + if CookieProp.Count = 0 then begin + Exit; + end; + + FName := CookieProp.Names[0]; + FValue := IndyValueFromIndex(CookieProp, 0); + CookieProp.Delete(0); + + FCreatedAt := Now; + FLastAccessed := FCreatedAt; + + // using the algorithms defined in RFC 6265 section 5.3... + + if GetLastValueOf(CookieProp, 'MAX-AGE', S) then begin {Do not Localize} + FPersistent := True; + FExpires := StrToFloat(S); + end + else if GetLastValueOf(CookieProp, 'EXPIRES', S) then {Do not Localize} + begin + FPersistent := True; + FExpires := StrToFloat(S); + end else + begin + FPersistent := False; + FExpires := EncodeDate(9999, 12, 31) + EncodeTime(23, 59, 59, 999); + end; + + S := ''; + if GetLastValueOf(CookieProp, 'DOMAIN', S) then {Do not Localize} + begin + // TODO + { + If the user agent is configured to reject "public suffixes" and + the domain-attribute is a public suffix: + + If the domain-attribute is identical to the canonicalized + request-host: + + Let the domain-attribute be the empty string. + + Otherwise: + + Ignore the cookie entirely and abort these steps. + + NOTE: A "public suffix" is a domain that is controlled by a + public registry, such as "com", "co.uk", and "pvt.k12.wy.us". + This step is essential for preventing attacker.com from + disrupting the integrity of example.com by setting a cookie + with a Domain attribute of "com". Unfortunately, the set of + public suffixes (also known as "registry controlled domains") + changes over time. If feasible, user agents SHOULD use an + up-to-date public suffix list, such as the one maintained by + the Mozilla project at . + } + { + if RejectPublicSuffixes and IsPublicSuffix(S) then begin + if S <> CanonicalizeHostName(AURI.Host) then begin + Exit; + end; + S := ''; + end; + } + end; + + if Length(S) > 0 then + begin + if not IsDomainMatch(AURI.Host, S) then begin + Exit; + end; + FHostOnly := False; + FDomain := S; + end else + begin + FHostOnly := True; + FDomain := CanonicalizeHostName(AURI.Host); + end; + + if GetLastValueOf(CookieProp, 'PATH', S) then begin {Do not Localize} + FPath := S; + end else begin + FPath := GetDefaultPath(AURI); + end; + + FSecure := CookieProp.IndexOfName('SECURE') <> -1; { Do not Localize } + FHttpOnly := CookieProp.IndexOfName('HTTPONLY') <> -1; { Do not Localize } + + if FHttpOnly and (not IsHTTP(AURI.Protocol)) then begin + Exit; + end; + + Result := True; + finally + FreeAndNil(CookieProp); + end; +end; + +function TIdCookie.GetIsExpired: Boolean; +begin + Result := (FExpires <> 0.0) and (FExpires < Now); +end; + +function TIdCookie.GetMaxAge: Int64; +begin + if FExpires <> 0.0 then begin + Result := Trunc((FExpires - Now) * MSecsPerDay / 1000); + end else begin + Result := -1; + end; +end; + +{ + set-cookie-header = "Set-Cookie:" SP set-cookie-string + set-cookie-string = cookie-pair *( ";" SP cookie-av ) + cookie-pair = cookie-name "=" cookie-value + cookie-name = token + cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) + cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + ; US-ASCII characters excluding CTLs, + ; whitespace DQUOTE, comma, semicolon, + ; and backslash + token = + + cookie-av = expires-av / max-age-av / domain-av / + path-av / secure-av / httponly-av / + extension-av + expires-av = "Expires=" sane-cookie-date + sane-cookie-date = + max-age-av = "Max-Age=" non-zero-digit *DIGIT + ; In practice, both expires-av and max-age-av + ; are limited to dates representable by the + ; user agent. + non-zero-digit = %x31-39 + ; digits 1 through 9 + domain-av = "Domain=" domain-value + domain-value = + ; defined in [RFC1034], Section 3.5, as + ; enhanced by [RFC1123], Section 2.1 + path-av = "Path=" path-value + path-value = + secure-av = "Secure" + httponly-av = "HttpOnly" + extension-av = +} +function TIdCookie.GetServerCookie: String; +var + LExpires: TDateTime; + LMaxAge: Int64; +begin + Result := FName + '=' + FValue; {Do not Localize} + AddCookieProperty(Result, 'Path', FPath); {Do not Localize} + AddCookieProperty(Result, 'Domain', FDomain); {Do not Localize} + if FSecure then begin + AddCookieFlag(Result, 'Secure'); {Do not Localize} + end; + if FHttpOnly then begin + AddCookieFlag(Result, 'HttpOnly'); {Do not Localize} + end; + LMaxAge := MaxAge; + if LMaxAge >= 0 then begin + AddCookieProperty(Result, 'Max-Age', IntToStr(LMaxAge)); {Do not Localize} + end; + LExpires := Expires; + if LExpires <> 0.0 then begin + AddCookieProperty(Result, 'Expires', LocalDateTimeToCookieStr(LExpires)); {Do not Localize} + end; +end; + +{ + Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ... +} +function TIdCookie.GetClientCookie: String; +begin + Result := FName + '=' + FValue; +end; + +{ + cookie-header = "Cookie:" OWS cookie-string OWS + cookie-string = cookie-pair *( ";" SP cookie-pair ) +} +function TIdCookie.ParseClientCookie(const ACookieText: String): Boolean; +var + CookieProp: TStringList; + + procedure SplitCookieText; + var + LTemp, LName, LValue: String; + i: Integer; + IsFlag: Boolean; + begin + LTemp := Trim(ACookieText); + while LTemp <> '' do {Do not Localize} + begin + i := FindFirstOf('=;', LTemp); {Do not Localize} + if i = 0 then begin + CookieProp.Add(LTemp); + Break; + end; + IsFlag := (LTemp[i] = ';'); {Do not Localize} + LName := TrimRight(Copy(LTemp, 1, i-1)); + LTemp := TrimLeft(Copy(LTemp, i+1, MaxInt)); + LValue := ''; + if (not IsFlag) and (LTemp <> '') then + begin + if TextStartsWith(LTemp, '"') then {Do not Localize} + begin + IdDelete(LTemp, 1, 1); + LValue := Fetch(LTemp, '"'); {Do not Localize} + Fetch(LTemp, ';'); {Do not Localize} + end else begin + LValue := Trim(Fetch(LTemp, ';')); {Do not Localize} + end; + LTemp := TrimLeft(LTemp); + end; + if LName <> '' then begin + CookieProp.Add(LName + '=' + LValue); {Do not Localize} + end; + end; +end; + +begin + Result := False; + + CookieProp := TStringList.Create; + try + SplitCookieText; + if CookieProp.Count = 0 then begin + Exit; + end; + + FName := CookieProp.Names[0]; + FValue := IndyValueFromIndex(CookieProp, 0); + + Result := True; + finally + FreeAndNil(CookieProp); + end; +end; + +{ TIdCookies } + +constructor TIdCookies.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdCookie); + FRWLock := TMultiReadExclusiveWriteSynchronizer.Create; + FCookieList := TIdCookieList.Create; +end; + +destructor TIdCookies.Destroy; +begin + // This will force the Cookie removing process before we free FCookieList and FRWLock + Self.Clear; + FreeAndNil(FCookieList); + FreeAndNil(FRWLock); + inherited Destroy; +end; + +function TIdCookies.Add: TIdCookie; +begin + Result := TIdCookie(inherited Add); +end; + +function TIdCookies.AddCookie(ACookie: TIdCookie; AURI: TIdURI; AReplaceOld: Boolean = True): Boolean; +var + LOldCookie: TIdCookie; + I: Integer; +begin + Result := False; + LockCookieList(caReadWrite); + try + if AReplaceOld then + begin + for I := 0 to FCookieList.Count-1 do + begin + LOldCookie := FCookieList[I]; + if not TextIsSame(LOldCookie.CookieName, ACookie.CookieName) then begin + Continue; + end; + if not TextIsSame(LOldCookie.Domain, ACookie.Domain) then begin + Continue; + end; + if not TextIsSame(LOldCookie.Path, ACookie.Path) then begin + Continue; + end; + if ((AURI <> nil) and (not IsHTTP(AURI.Protocol))) and LOldCookie.HttpOnly then begin + Exit; + end; + ACookie.FCreatedAt := LOldCookie.CreatedAt; + FCookieList.Delete(I); + LOldCookie.Collection := nil; + LOldCookie.Free; + Break; + end; + end; + if not ACookie.IsExpired then begin + FCookieList.Add(ACookie); + Result := True; + end; + finally + UnlockCookieList(caReadWrite); + end; +end; + +procedure TIdCookies.Assign(ASource: TPersistent); +begin + if (ASource = nil) or (ASource is TIdCookies) then + begin + LockCookieList(caReadWrite); + try + Clear; + AddCookies(TIdCookies(ASource)); + finally + UnlockCookieList(caReadWrite); + end; + end else + begin + inherited Assign(ASource); + end; +end; + +function TIdCookies.GetCookie(Index: Integer): TIdCookie; +begin + Result := inherited GetItem(Index) as TIdCookie; +end; + +procedure TIdCookies.SetCookie(Index: Integer; const Value: TIdCookie); +begin + inherited SetItem(Index, Value); +end; + +function TIdCookies.AddClientCookie(const ACookie: string): TIdCookie; +var + LCookie: TIdCookie; +begin + Result := nil; + LCookie := Add; + try + if LCookie.ParseClientCookie(ACookie) then + begin + LockCookieList(caReadWrite); + try + FCookieList.Add(LCookie); + Result := LCookie; + LCookie := nil; + finally + UnlockCookieList(caReadWrite); + end; + end; + finally + if LCookie <> nil then + begin + LCookie.Collection := nil; + LCookie.Free; + end; + end; +end; + +procedure TIdCookies.AddClientCookies(const ACookie: string); +var + Temp: TStringList; + LCookie, S: String; + I: Integer; +begin + S := Trim(ACookie); + if S <> '' then begin + Temp := TStringList.Create; + try + repeat + LCookie := Fetch(S, ';'); + if LCookie <> '' then begin + Temp.Add(LCookie); + end; + until S = ''; + for I := 0 to Temp.Count-1 do begin + AddClientCookie(Temp[I]); + end; + finally + Temp.Free; + end; + end; +end; + +procedure TIdCookies.AddClientCookies(const ACookies: TStrings); +var + i: Integer; +begin + for i := 0 to ACookies.Count - 1 do begin + AddClientCookies(ACookies[i]); + end; +end; + +function TIdCookies.AddServerCookie(const ACookie: string; AURI: TIdURI): TIdCookie; +var + LCookie: TIdCookie; +begin + Result := nil; + LCookie := Add; + try + if LCookie.ParseServerCookie(ACookie, AURI) then begin + if AddCookie(LCookie, AURI) then + begin + Result := LCookie; + LCookie := nil; + end; + end; + finally + if LCookie <> nil then begin + LCookie.Collection := nil; + LCookie.Free; + end; + end; +end; + +procedure TIdCookies.AddServerCookies(const ACookies: TStrings; AURI: TIdURI); +var + i: Integer; +begin + for i := 0 to ACookies.Count - 1 do begin + AddServerCookie(ACookies[i], AURI); + end; +end; + +procedure TIdCookies.AddCookies(ASource: TIdCookies); +var + LSrcCookies: TIdCookieList; + LSrcCookie, LDestCookie: TIdCookie; + i: Integer; +begin + if (ASource <> nil) and (ASource <> Self) then + begin + LSrcCookies := ASource.LockCookieList(caRead); + try + LockCookieList(caReadWrite); + try + for i := 0 to LSrcCookies.Count - 1 do + begin + LSrcCookie := LSrcCookies[i]; + LDestCookie := TIdCookieClass(LSrcCookie.ClassType).Create(Self); + try + LDestCookie.Assign(LSrcCookie); + FCookieList.Add(LDestCookie); + except + LDestCookie.Collection := nil; + LDestCookie.Free; + raise; + end; + end; + finally + UnlockCookieList(caReadWrite); + end; + finally + ASource.UnlockCookieList(caRead); + end; + end; +end; + +function TIdCookies.GetCookieByNameAndDomain(const AName, ADomain: string): TIdCookie; +var + i: Integer; +begin + i := GetCookieIndex(AName, ADomain); + if i = -1 then begin + Result := nil; + end else begin + Result := Cookies[i]; + end; +end; + +function TIdCookies.GetCookieIndex(const AName: string; FirstIndex: Integer = 0): Integer; +var + i: Integer; +begin + Result := -1; + for i := FirstIndex to Count - 1 do + begin + if TextIsSame(Cookies[i].CookieName, AName) then + begin + Result := i; + Exit; + end; + end; +end; + +function TIdCookies.GetCookieIndex(const AName, ADomain: string; FirstIndex: Integer = 0): Integer; +var + LCookie: TIdCookie; + i: Integer; +begin + Result := -1; + for i := FirstIndex to Count - 1 do + begin + LCookie := Cookies[i]; + if TextIsSame(LCookie.CookieName, AName) and + TextIsSame(CanonicalizeHostName(LCookie.Domain), CanonicalizeHostName(ADomain)) then + begin + Result := i; + Exit; + end; + end; +end; + +procedure TIdCookies.Clear; +begin + LockCookieList(caReadWrite); + try + FCookieList.Clear; + inherited Clear; + finally + UnlockCookieList(caReadWrite); + end; +end; + +function TIdCookies.LockCookieList(AAccessType: TIdCookieAccess): TIdCookieList; +begin + case AAccessType of + caRead: + begin + FRWLock.BeginRead; + end; + caReadWrite: + begin + FRWLock.BeginWrite; + end; + end; + Result := FCookieList; +end; + +procedure TIdCookies.UnlockCookieList(AAccessType: TIdCookieAccess); +begin + case AAccessType of + caRead: + begin + FRWLock.EndRead; + end; + caReadWrite: + begin + FRWLock.EndWrite; + end; + end; +end; + +end. diff --git a/indy/Protocols/IdCookieManager.pas b/indy/Protocols/IdCookieManager.pas new file mode 100644 index 0000000..c58121c --- /dev/null +++ b/indy/Protocols/IdCookieManager.pas @@ -0,0 +1,337 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 2004.10.27 9:17:46 AM czhower + For TIdStrings + + Rev 1.4 7/28/04 11:43:32 PM RLebeau + Bug fix for CleanupCookieList() + + Rev 1.3 2004.02.03 5:45:02 PM czhower + Name changes + + Rev 1.2 1/22/2004 7:10:02 AM JPMugaas + Tried to fix AnsiSameText depreciation. + + Rev 1.1 2004.01.21 1:04:54 PM czhower + InitComponenet + + Rev 1.0 11/14/2002 02:16:26 PM JPMugaas + + 2001-Mar-31 Doychin Bondzhev + - Added new method AddCookie2 that is called when we have Set-Cookie2 as response + - The common code in AddCookie and AddCookie2 is now in DoAdd + + 2001-Mar-24 Doychin Bondzhev + - Added OnNewCookie event + This event is called for every new cookie. Can be used to ask the user program + do we have to store this cookie in the cookie collection + - Added new method AddCookie + This calls the OnNewCookie event and if the result is true it adds the new cookie + in the collection +} + +unit IdCookieManager; + +{ + Implementation of the HTTP State Management Mechanism as specified in RFC 6265. + + Author: Remy Lebeau (remy@lebeausoftware.org) + Copyright: (c) Chad Z. Hower and The Indy Team. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdBaseComponent, + IdCookie, + IdHeaderList, + IdURI; + +Type + TOnNewCookieEvent = procedure(ASender: TObject; ACookie: TIdCookie; var VAccept: Boolean) of object; + + TOnCookieManagerEvent = procedure(ASender: TObject; ACookieCollection: TIdCookies) of object; + TOnCookieCreateEvent = TOnCookieManagerEvent; + TOnCookieDestroyEvent = TOnCookieManagerEvent; + + TIdCookieManager = class(TIdBaseComponent) + protected + FOnCreate: TOnCookieCreateEvent; + FOnDestroy: TOnCookieDestroyEvent; + FOnNewCookie: TOnNewCookieEvent; + FCookieCollection: TIdCookies; + + procedure CleanupCookieList; + procedure DoOnCreate; virtual; + procedure DoOnDestroy; virtual; + function DoOnNewCookie(ACookie: TIdCookie): Boolean; virtual; + procedure InitComponent; override; + public + destructor Destroy; override; + // + procedure AddServerCookie(const ACookie: String; AURL: TIdURI); + procedure AddServerCookies(const ACookies: TStrings; AURL: TIdURI); + + procedure AddCookies(ASource: TIdCookieManager); + procedure CopyCookie(ACookie: TIdCookie); + // + procedure GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean; + Headers: TIdHeaderList); + // + property CookieCollection: TIdCookies read FCookieCollection; + published + property OnCreate: TOnCookieCreateEvent read FOnCreate write FOnCreate; + property OnDestroy: TOnCookieDestroyEvent read FOnDestroy write FOnDestroy; + property OnNewCookie: TOnNewCookieEvent read FOnNewCookie write FOnNewCookie; + end; + +//procedure SplitCookies(const ACookie: String; ACookies: TStrings); + +implementation + +uses + {$IFDEF HAS_UNIT_Generics_Defaults} + System.Generics.Defaults, + {$ENDIF} + IdAssignedNumbers, IdException, IdGlobal, IdGlobalProtocols, SysUtils; + +{ TIdCookieManager } + +destructor TIdCookieManager.Destroy; +begin + CleanupCookieList; + DoOnDestroy; + FreeAndNil(FCookieCollection); + inherited Destroy; +end; + +function SortCookiesFunc({$IFDEF HAS_GENERICS_TList}const {$ENDIF}Item1, Item2: TIdCookie): Integer; +begin + // using the algorithm defined in RFC 6265 section 5.4 + + if Item1 = Item2 then + begin + Result := 0; + end + else if Length(Item2.Path) > Length(Item1.Path) then + begin + Result := 1; + end + else if Length(Item1.Path) = Length(Item2.Path) then + begin + if Item2.CreatedAt < Item1.CreatedAt then begin + Result := 1; + end else begin + Result := -1; + end; + end else + begin + Result := -1; + end; +end; + +procedure TIdCookieManager.GenerateClientCookies(AURL: TIdURI; SecureOnly: Boolean; + Headers: TIdHeaderList); +var + I: Integer; + LCookieList: TIdCookieList; + LResultList: TIdCookieList; + LCookie: TIdCookie; + LCookiesToSend: String; + LNow: TDateTime; +begin + // check for expired cookies first... + CleanupCookieList; + + LCookieList := CookieCollection.LockCookieList(caRead); + try + if LCookieList.Count > 0 then begin + LResultList := TIdCookieList.Create; + try + // Search for cookies for this domain and URI + for I := 0 to LCookieList.Count-1 do begin + LCookie := LCookieList[I]; + if LCookie.IsAllowed(AURL, SecureOnly) then begin + LResultList.Add(LCookie); + end; + end; + + if LResultList.Count > 0 then begin + if LResultList.Count > 1 then begin + LResultList.Sort( + {$IFDEF HAS_GENERICS_TList} + TComparer.Construct(SortCookiesFunc) + {$ELSE} + TListSortCompare(@SortCookiesFunc) + {$ENDIF} + ); + end; + + LNow := Now; + for I := 0 to LResultList.Count-1 do begin + LResultList[I].LastAccessed := LNow; + end; + + LCookiesToSend := LResultList[0].ClientCookie; + for I := 1 to LResultList.Count-1 do begin + LCookiesToSend := LCookiesToSend + '; ' + LResultList[I].ClientCookie; {Do not Localize} + end; + + Headers.AddValue('Cookie', LCookiesToSend); {Do not Localize} + end; + finally + LResultList.Free; + end; + end; + finally + CookieCollection.UnlockCookieList(caRead); + end; +end; + +procedure TIdCookieManager.AddServerCookie(const ACookie: String; AURL: TIdURI); +var + LCookie: TIdCookie; +begin + // TODO: use TIdCookies.AddServerCookie() after adding + // a way for it to query the manager for rejections... + // + //FCookieCollection.AddServerCookie(ACookie, AURI); + + LCookie := FCookieCollection.Add; + try + if LCookie.ParseServerCookie(ACookie, AURL) then + begin + if DoOnNewCookie(LCookie) then + begin + if FCookieCollection.AddCookie(LCookie, AURL) then begin + LCookie := nil; + Exit; + end; + end; + end; + finally + if LCookie <> nil then + begin + LCookie.Collection := nil; + LCookie.Free; + end; + end; +end; + +procedure TIdCookieManager.AddCookies(ASource: TIdCookieManager); +begin + if (ASource <> nil) and (ASource <> Self) then begin + FCookieCollection.AddCookies(ASource.CookieCollection); + end; +end; + +procedure TIdCookieManager.AddServerCookies(const ACookies: TStrings; AURL: TIdURI); +var + I: Integer; +begin + for I := 0 to ACookies.Count-1 do begin + AddServerCookie(ACookies[I], AURL); + end; +end; + +procedure TIdCookieManager.CopyCookie(ACookie: TIdCookie); +var + LCookie: TIdCookie; +begin + LCookie := TIdCookieClass(ACookie.ClassType).Create(FCookieCollection); + try + LCookie.Assign(ACookie); + if LCookie.Domain <> '' then + begin + if DoOnNewCookie(LCookie) then + begin + if FCookieCollection.AddCookie(LCookie, nil) then begin + LCookie := nil; + end; + end; + end; + finally + if LCookie <> nil then + begin + LCookie.Collection := nil; + LCookie.Free; + end; + end; +end; + +function TIdCookieManager.DoOnNewCookie(ACookie: TIdCookie): Boolean; +begin + Result := True; + if Assigned(FOnNewCookie) then begin + OnNewCookie(Self, ACookie, Result); + end; +end; + +procedure TIdCookieManager.DoOnCreate; +begin + if Assigned(FOnCreate) then begin + OnCreate(Self, FCookieCollection); + end; +end; + +procedure TIdCookieManager.DoOnDestroy; +begin + if Assigned(FOnDestroy) then + begin + OnDestroy(Self, FCookieCollection); + end; +end; + +procedure TIdCookieManager.CleanupCookieList; +var + i: Integer; + LCookieList: TIdCookieList; + LCookie: TIdCookie; +begin + LCookieList := FCookieCollection.LockCookieList(caReadWrite); + try + for i := LCookieList.Count-1 downto 0 do + begin + LCookie := LCookieList[i]; + if LCookie.IsExpired then + begin + // The Cookie has expired. It has to be removed from the collection + LCookieList.Delete(i); + // must set the Collection to nil or the cookie will try to remove + // itself from the cookie collection and deadlock + LCookie.Collection := nil; + LCookie.Free; + end; + end; + finally + FCookieCollection.UnlockCookieList(caReadWrite); + end; +end; + +procedure TIdCookieManager.InitComponent; +begin + inherited InitComponent; + FCookieCollection := TIdCookies.Create(Self); + DoOnCreate; +end; + +end. diff --git a/indy/Protocols/IdCustomHTTPServer.pas b/indy/Protocols/IdCustomHTTPServer.pas new file mode 100644 index 0000000..57b0802 --- /dev/null +++ b/indy/Protocols/IdCustomHTTPServer.pas @@ -0,0 +1,2410 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.42 3/14/05 11:45:50 AM RLebeau + Buf fix for DoExecute() not not filling in the TIdHTTPRequestInfo.FormParams + correctly. + + Removed LImplicitPostStream variable from DoExecute(), no longer used. + TIdHTTPRequestInfo takes ownership of the PostStream anyway, so no need to + free it early. This also allows the PostStream to always be available in the + OnCommand... event handlers. + + Rev 1.41 2/9/05 2:11:02 AM RLebeau + Removed compiler hint + + Rev 1.40 2/9/05 1:19:26 AM RLebeau + Fixes for Compiler errors + + Rev 1.39 2/8/05 6:47:46 PM RLebeau + updated OnCommandOther to have ARequestInfo and AResponseInfo parameters + + Rev 1.38 12/16/04 2:15:20 AM RLebeau + Another DoExecute() update + + Rev 1.37 12/15/04 9:03:50 PM RLebeau + Renamed TIdHTTPRequestInfo.DecodeCommand() to DecodeHTTPCommand() and made it + into a standalone function. + + Rev 1.36 12/15/04 4:17:42 PM RLebeau + Updated DoExecute() to call LRequestInfo.DecodeCommand() + + Rev 1.35 12/2/2004 4:23:48 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.34 10/26/2004 8:59:32 PM JPMugaas + Updated with new TStrings references for more portability. + + Rev 1.33 2004.05.20 11:37:12 AM czhower + IdStreamVCL + + Rev 1.32 5/6/04 3:19:00 PM RLebeau + Added extra comments + + Rev 1.31 2004.04.18 12:52:06 AM czhower + Big bug fix with server disconnect and several other bug fixed that I found + along the way. + + Rev 1.30 2004.04.08 1:46:32 AM czhower + Small Optimizations + + Rev 1.29 7/4/2004 4:10:44 PM SGrobety + Small fix to keep it synched with the IOHandler properties + + Rev 1.28 6/4/2004 5:15:02 PM SGrobety + Implemented MaximumHeaderLineCount property (default to 1024) + + Rev 1.27 2004.02.03 5:45:02 PM czhower + Name changes + + Rev 1.26 1/27/2004 3:58:52 PM SPerry + StringStream ->IdStringStream + + Rev 1.25 2004.01.22 5:58:58 PM czhower + IdCriticalSection + + Rev 1.24 1/22/2004 8:26:28 AM JPMugaas + Ansi* calls changed. + + Rev 1.23 1/21/2004 1:57:30 PM JPMugaas + InitComponent + + Rev 1.22 21.1.2004 . 13:22:18 DBondzhev + Fix for Dccil bug + + Rev 1.21 10/25/2003 06:51:44 AM JPMugaas + Updated for new API changes and tried to restore some functionality. + + Rev 1.20 2003.10.24 10:43:02 AM czhower + TIdSTream to dos + + Rev 1.19 10/19/2003 11:49:40 AM DSiders + Added localization comments. + + Rev 1.18 10/17/2003 12:05:40 AM DSiders + Corrected spelling error in resource string. + + Rev 1.17 10/15/2003 11:10:16 PM GGrieve + DotNet changes + + Rev 1.16 2003.10.12 3:37:58 PM czhower + Now compiles again. + + Rev 1.15 6/24/2003 11:38:50 AM BGooijen + Fixed ssl support + + Rev 1.14 6/18/2003 11:44:04 PM BGooijen + Moved ServeFile and SmartServeFile to TIdHTTPResponseInfo. + Added TIdHTTPResponseInfo.HTTPServer field + + Rev 1.13 05.6.2003 . 11:11:12 DBondzhev + Socket exceptions should not be stopped after DoCommandGet. + + Rev 1.12 4/9/2003 9:38:40 PM BGooijen + fixed av on FSessionList.PurgeStaleSessions(Terminated); + + Rev 1.11 20/3/2003 19:49:24 GGrieve + Define SmartServeFile + + Rev 1.10 3/13/2003 10:21:14 AM BGooijen + Changed result of function .execute + + Rev 1.9 2/25/2003 10:43:36 AM BGooijen + removed unneeded assignment + + Rev 1.8 2/25/2003 10:38:46 AM BGooijen + The Serversoftware wasn't send to the client, because of duplicate properties + (.Server and .ServerSoftware). + + Rev 1.7 2/24/2003 08:20:50 PM JPMugaas + Now should compile with new code. + + Rev 1.6 11.2.2003 13:36:14 TPrami + - Fixed URL get paremeter handling (SeeRFC 1866 section 8.2.1.) + + Rev 1.5 1/17/2003 05:35:20 PM JPMugaas + Now compiles with new design. + + Rev 1.4 1-1-2003 20:12:44 BGooijen + Changed to support the new TIdContext class + + Rev 1.3 12-15-2002 13:08:38 BGooijen + simplified TimeStampInterval + + Rev 1.2 6/12/2002 10:59:34 AM SGrobety Version: 1.1 + Made to work with Indy 10 + + Rev 1.0 21/11/2002 12:41:04 PM SGrobety Version: Indy 10 + + Rev 1.0 11/14/2002 02:16:32 PM JPMugaas +} + +unit IdCustomHTTPServer; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdAssignedNumbers, + IdContext, IdException, + IdGlobal, IdStack, + IdExceptionCore, IdGlobalProtocols, IdHeaderList, IdCustomTCPServer, + IdTCPConnection, IdThread, IdCookie, IdHTTPHeaderInfo, IdStackConsts, + IdBaseComponent, IdThreadSafe, + SysUtils; + +type + // Enums + THTTPCommandType = (hcUnknown, hcHEAD, hcGET, hcPOST, hcDELETE, hcPUT, hcTRACE, hcOPTION); + +const + Id_TId_HTTPServer_KeepAlive = false; + Id_TId_HTTPServer_ParseParams = True; + Id_TId_HTTPServer_SessionState = False; + Id_TId_HTTPSessionTimeOut = 0; + Id_TId_HTTPAutoStartSession = False; + + Id_TId_HTTPMaximumHeaderLineCount = 1024; + + GResponseNo = 200; + GFContentLength = -1; + GServerSoftware = gsIdProductName + '/' + gsIdVersion; {Do not Localize} + GContentType = 'text/html'; {Do not Localize} + GSessionIDCookie = 'IDHTTPSESSIONID'; {Do not Localize} + HTTPRequestStrings: array[0..Ord(High(THTTPCommandType))] of string = ('UNKNOWN', 'HEAD','GET','POST','DELETE','PUT','TRACE', 'OPTIONS'); {do not localize} + +type + // Forwards + TIdHTTPSession = class; + TIdHTTPCustomSessionList = class; + TIdHTTPRequestInfo = class; + TIdHTTPResponseInfo = class; + TIdCustomHTTPServer = class; + + //events + TIdHTTPSessionEndEvent = procedure(Sender: TIdHTTPSession) of object; + TIdHTTPSessionStartEvent = procedure(Sender: TIdHTTPSession) of object; + TIdHTTPCreateSession = procedure(ASender:TIdContext; + var VHTTPSession: TIdHTTPSession) of object; + TIdHTTPCreatePostStream = procedure(AContext: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream) of object; + TIdHTTPDoneWithPostStream = procedure(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; var VCanFree: Boolean) of object; + TIdHTTPParseAuthenticationEvent = procedure(AContext: TIdContext; const AAuthType, AAuthData: String; var VUsername, VPassword: String; var VHandled: Boolean) of object; + TIdHTTPCommandEvent = procedure(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo) of object; + TIdHTTPCommandError = procedure(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; + AException: Exception) of object; + TIdHTTPInvalidSessionEvent = procedure(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; + var VContinueProcessing: Boolean; const AInvalidSessionID: String) of object; + TIdHTTPHeadersAvailableEvent = procedure(AContext: TIdContext; const AUri: string; AHeaders: TIdHeaderList; var VContinueProcessing: Boolean) of object; + TIdHTTPHeadersBlockedEvent = procedure(AContext: TIdContext; AHeaders: TIdHeaderList; var VResponseNo: Integer; var VResponseText, VContentText: String) of object; + TIdHTTPHeaderExpectationsEvent = procedure(AContext: TIdContext; const AExpectations: String; var VContinueProcessing: Boolean) of object; + TIdHTTPQuerySSLPortEvent = procedure(APort: TIdPort; var VUseSSL: Boolean) of object; + + //objects + EIdHTTPServerError = class(EIdException); + EIdHTTPHeaderAlreadyWritten = class(EIdHTTPServerError); + EIdHTTPErrorParsingCommand = class(EIdHTTPServerError); + EIdHTTPUnsupportedAuthorisationScheme = class(EIdHTTPServerError); + EIdHTTPCannotSwitchSessionStateWhenActive = class(EIdHTTPServerError); + EIdHTTPCannotSwitchSessionIDCookieNameWhenActive = class(EIdHTTPServerError); + + TIdHTTPRequestInfo = class(TIdRequestHeaderInfo) + protected + FAuthExists: Boolean; + FCookies: TIdCookies; + FParams: TStrings; + FPostStream: TStream; + FRawHTTPCommand: string; + FRemoteIP: string; + FSession: TIdHTTPSession; + FDocument: string; + FURI: string; + FCommand: string; + FVersion: string; + FVersionMajor: Integer; + FVersionMinor: Integer; + FAuthUsername: string; + FAuthPassword: string; + FUnparsedParams: string; + FQueryParams: string; + FFormParams: string; + FCommandType: THTTPCommandType; + // + procedure DecodeAndSetParams(const AValue: String); virtual; + public + constructor Create(AOwner: TPersistent); override; + destructor Destroy; override; + // + function IsVersionAtLeast(const AMajor, AMinor: Integer): Boolean; + property Session: TIdHTTPSession read FSession; + // + property AuthExists: Boolean read FAuthExists; + property AuthPassword: string read FAuthPassword; + property AuthUsername: string read FAuthUsername; + property Command: string read FCommand; + property CommandType: THTTPCommandType read FCommandType; + property Cookies: TIdCookies read FCookies; + property Document: string read FDocument write FDocument; // writable for isapi compatibility. Use with care + property URI: string read FURI; + property Params: TStrings read FParams; + property PostStream: TStream read FPostStream write FPostStream; + property RawHTTPCommand: string read FRawHTTPCommand; + property RemoteIP: String read FRemoteIP; + property UnparsedParams: string read FUnparsedParams write FUnparsedParams; // writable for isapi compatibility. Use with care + property FormParams: string read FFormParams write FFormParams; // writable for isapi compatibility. Use with care + property QueryParams: string read FQueryParams write FQueryParams; // writable for isapi compatibility. Use with care + property Version: string read FVersion; + property VersionMajor: Integer read FVersionMajor; + property VersionMinor: Integer read FVersionMinor; + end; + + TIdHTTPResponseInfo = class(TIdResponseHeaderInfo) + protected + FAuthRealm: string; + FConnection: TIdTCPConnection; + FResponseNo: Integer; + FCookies: TIdCookies; + FContentStream: TStream; + FContentText: string; + FCloseConnection: Boolean; + FFreeContentStream: Boolean; + FHeaderHasBeenWritten: Boolean; + FResponseText: string; + FHTTPServer: TIdCustomHTTPServer; + FSession: TIdHTTPSession; + FRequestInfo: TIdHTTPRequestInfo; + // + procedure ReleaseContentStream; + procedure SetCookies(const AValue: TIdCookies); + procedure SetHeaders; override; + procedure SetResponseNo(const AValue: Integer); + procedure SetCloseConnection(const Value: Boolean); + public + function GetServer: string; + procedure SetServer(const Value: string); + public + procedure CloseSession; + constructor Create(AServer: TIdCustomHTTPServer; ARequestInfo: TIdHTTPRequestInfo; AConnection: TIdTCPConnection); reintroduce; + destructor Destroy; override; + procedure Redirect(const AURL: string); + procedure WriteHeader; + procedure WriteContent; + // + function ServeFile(AContext: TIdContext; const AFile: String): Int64; virtual; + function SmartServeFile(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; const AFile: String): Int64; + // + property AuthRealm: string read FAuthRealm write FAuthRealm; + property CloseConnection: Boolean read FCloseConnection write SetCloseConnection; + property ContentStream: TStream read FContentStream write FContentStream; + property ContentText: string read FContentText write FContentText; + property Cookies: TIdCookies read FCookies write SetCookies; + property FreeContentStream: Boolean read FFreeContentStream write FFreeContentStream; + // writable for isapi compatibility. Use with care + property HeaderHasBeenWritten: Boolean read FHeaderHasBeenWritten write FHeaderHasBeenWritten; + property ResponseNo: Integer read FResponseNo write SetResponseNo; + property ResponseText: String read FResponseText write FResponseText; + property HTTPServer: TIdCustomHTTPServer read FHTTPServer; + property ServerSoftware: string read GetServer write SetServer; + property Session: TIdHTTPSession read FSession; + end; + + TIdHTTPSession = Class(TObject) + protected + FContent: TStrings; + FLastTimeStamp: TDateTime; + FLock: TIdCriticalSection; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FOwner: TIdHTTPCustomSessionList; + FSessionID: string; + FRemoteHost: string; + // + procedure SetContent(const Value: TStrings); + function IsSessionStale: boolean; virtual; + procedure DoSessionEnd; virtual; + public + constructor Create(AOwner: TIdHTTPCustomSessionList); virtual; + constructor CreateInitialized(AOwner: TIdHTTPCustomSessionList; const SessionID, + RemoteIP: string); virtual; + destructor Destroy; override; + procedure Lock; + procedure Unlock; + // + property Content: TStrings read FContent write SetContent; + property LastTimeStamp: TDateTime read FLastTimeStamp; + property RemoteHost: string read FRemoteHost; + property SessionID: String read FSessionID; + end; + + {$IFDEF HAS_GENERICS_TThreadList} + TIdHTTPSessionThreadList = TThreadList; + TIdHTTPSessionList = TList; + {$ELSE} + // TODO: flesh out to match TThreadList and TList for non-Generics compilers + TIdHTTPSessionThreadList = TThreadList; + TIdHTTPSessionList = TList; + {$ENDIF} + + TIdHTTPCustomSessionList = class(TIdBaseComponent) + private + FSessionTimeout: Integer; + FOnSessionEnd: TIdHTTPSessionEndEvent; + FOnSessionStart: TIdHTTPSessionStartEvent; + protected + // remove a session from the session list. Called by the session on "Free" + procedure RemoveSession(Session: TIdHTTPSession); virtual; abstract; + public + procedure Clear; virtual; abstract; + procedure PurgeStaleSessions(PurgeAll: Boolean = false); virtual; abstract; + function CreateUniqueSession(const RemoteIP: String): TIdHTTPSession; virtual; abstract; + function CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession; virtual; abstract; + function GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; virtual; abstract; + procedure Add(ASession: TIdHTTPSession); virtual; Abstract; + published + property SessionTimeout: Integer read FSessionTimeout write FSessionTimeout; + property OnSessionEnd: TIdHTTPSessionEndEvent read FOnSessionEnd write FOnSessionEnd; + property OnSessionStart: TIdHTTPSessionStartEvent read FOnSessionStart write FOnSessionStart; + end; + + TIdThreadSafeMimeTable = class(TIdThreadSafe) + protected + FTable: TIdMimeTable; + function GetLoadTypesFromOS: Boolean; + procedure SetLoadTypesFromOS(AValue: Boolean); + function GetOnBuildCache: TNotifyEvent; + procedure SetOnBuildCache(AValue: TNotifyEvent); + public + constructor Create(const AutoFill: Boolean = True); reintroduce; + destructor Destroy; override; + procedure BuildCache; + 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} + function Lock: TIdMimeTable; reintroduce; + procedure Unlock; reintroduce; + // + property LoadTypesFromOS: Boolean read GetLoadTypesFromOS write SetLoadTypesFromOS; + property OnBuildCache: TNotifyEvent read GetOnBuildCache write SetOnBuildCache; + end; + + TIdCustomHTTPServer = class(TIdCustomTCPServer) + protected + FAutoStartSession: Boolean; + FKeepAlive: Boolean; + FParseParams: Boolean; + FServerSoftware: string; + FMIMETable: TIdThreadSafeMimeTable; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSessionList: TIdHTTPCustomSessionList; + FImplicitSessionList: Boolean; + FSessionState: Boolean; + FSessionTimeOut: Integer; + // + FOnCreatePostStream: TIdHTTPCreatePostStream; + FOnDoneWithPostStream: TIdHTTPDoneWithPostStream; + FOnCreateSession: TIdHTTPCreateSession; + FOnInvalidSession: TIdHTTPInvalidSessionEvent; + FOnParseAuthentication: TIdHTTPParseAuthenticationEvent; + FOnSessionEnd: TIdHTTPSessionEndEvent; + FOnSessionStart: TIdHTTPSessionStartEvent; + FOnCommandGet: TIdHTTPCommandEvent; + FOnCommandOther: TIdHTTPCommandEvent; + FOnCommandError: TIdHTTPCommandError; + FOnHeadersAvailable: TIdHTTPHeadersAvailableEvent; + FOnHeadersBlocked: TIdHTTPHeadersBlockedEvent; + FOnHeaderExpectations: TIdHTTPHeaderExpectationsEvent; + FOnQuerySSLPort: TIdHTTPQuerySSLPortEvent; + // + FSessionCleanupThread: TIdThread; + FMaximumHeaderLineCount: Integer; + FSessionIDCookieName: string; + // + procedure CreatePostStream(ASender: TIdContext; AHeaders: TIdHeaderList; var VPostStream: TStream); virtual; + procedure DoneWithPostStream(ASender: TIdContext; ARequestInfo: TIdHTTPRequestInfo); virtual; + procedure DoOnCreateSession(AContext: TIdContext; var VNewSession: TIdHTTPSession); virtual; + procedure DoInvalidSession(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; + var VContinueProcessing: Boolean; const AInvalidSessionID: String); virtual; + procedure DoCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); virtual; + procedure DoCommandOther(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); virtual; + procedure DoCommandError(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo; AException: Exception); virtual; + procedure DoConnect(AContext: TIdContext); override; + function DoHeadersAvailable(ASender: TIdContext; const AUri: String; AHeaders: TIdHeaderList): Boolean; virtual; + procedure DoHeadersBlocked(ASender: TIdContext; AHeaders: TIdHeaderList; var VResponseNo: Integer; var VResponseText, VContentText: String); virtual; + function DoHeaderExpectations(ASender: TIdContext; const AExpectations: String): Boolean; virtual; + function DoParseAuthentication(ASender: TIdContext; const AAuthType, AAuthData: String; var VUsername, VPassword: String): Boolean; + function DoQuerySSLPort(APort: TIdPort): Boolean; virtual; + procedure DoSessionEnd(Sender: TIdHTTPSession); virtual; + procedure DoSessionStart(Sender: TIdHTTPSession); virtual; + // + function DoExecute(AContext:TIdContext): Boolean; override; + // + procedure Startup; override; + procedure Shutdown; override; + procedure SetSessionList(const AValue: TIdHTTPCustomSessionList); + procedure SetSessionState(const Value: Boolean); + procedure SetSessionIDCookieName(const AValue: string); + function IsSessionIDCookieNameStored: Boolean; + function GetSessionFromCookie(AContext:TIdContext; + AHTTPrequest: TIdHTTPRequestInfo; AHTTPResponse: TIdHTTPResponseInfo; + var VContinueProcessing: Boolean): TIdHTTPSession; + procedure InitComponent; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + { to be published in TIdHTTPServer} + property OnCreatePostStream: TIdHTTPCreatePostStream read FOnCreatePostStream write FOnCreatePostStream; + property OnDoneWithPostStream: TIdHTTPDoneWithPostStream read FOnDoneWithPostStream write FOnDoneWithPostStream; + property OnCommandGet: TIdHTTPCommandEvent read FOnCommandGet write FOnCommandGet; + public + function CreateSession(AContext:TIdContext; + HTTPResponse: TIdHTTPResponseInfo; + HTTPRequest: TIdHTTPRequestInfo): TIdHTTPSession; + destructor Destroy; override; + function EndSession(const SessionName: String; const RemoteIP: String = ''): Boolean; + // + property MIMETable: TIdThreadSafeMimeTable read FMIMETable; + property SessionList: TIdHTTPCustomSessionList read FSessionList write SetSessionList; + published + property AutoStartSession: boolean read FAutoStartSession write FAutoStartSession default Id_TId_HTTPAutoStartSession; + property DefaultPort default IdPORT_HTTP; + property KeepAlive: Boolean read FKeepAlive write FKeepAlive default Id_TId_HTTPServer_KeepAlive; + property MaximumHeaderLineCount: Integer read FMaximumHeaderLineCount write FMaximumHeaderLineCount default Id_TId_HTTPMaximumHeaderLineCount; + property ParseParams: boolean read FParseParams write FParseParams default Id_TId_HTTPServer_ParseParams; + property ServerSoftware: string read FServerSoftware write FServerSoftware; + property SessionState: Boolean read FSessionState write SetSessionState default Id_TId_HTTPServer_SessionState; + property SessionTimeOut: Integer read FSessionTimeOut write FSessionTimeOut default Id_TId_HTTPSessionTimeOut; + property SessionIDCookieName: string read FSessionIDCookieName write SetSessionIDCookieName stored IsSessionIDCookieNameStored; + // + property OnCommandError: TIdHTTPCommandError read FOnCommandError write FOnCommandError; + property OnCommandOther: TIdHTTPCommandEvent read FOnCommandOther write FOnCommandOther; + property OnCreateSession: TIdHTTPCreateSession read FOnCreateSession write FOnCreateSession; + property OnInvalidSession: TIdHTTPInvalidSessionEvent read FOnInvalidSession write FOnInvalidSession; + property OnHeadersAvailable: TIdHTTPHeadersAvailableEvent read FOnHeadersAvailable write FOnHeadersAvailable; + property OnHeadersBlocked: TIdHTTPHeadersBlockedEvent read FOnHeadersBlocked write FOnHeadersBlocked; + property OnHeaderExpectations: TIdHTTPHeaderExpectationsEvent read FOnHeaderExpectations write FOnHeaderExpectations; + property OnParseAuthentication: TIdHTTPParseAuthenticationEvent read FOnParseAuthentication write FOnParseAuthentication; + property OnQuerySSLPort: TIdHTTPQuerySSLPortEvent read FOnQuerySSLPort write FOnQuerySSLPort; + property OnSessionStart: TIdHTTPSessionStartEvent read FOnSessionStart write FOnSessionStart; + property OnSessionEnd: TIdHTTPSessionEndEvent read FOnSessionEnd write FOnSessionEnd; + end; + + TIdHTTPDefaultSessionList = Class(TIdHTTPCustomSessionList) + protected + FSessionList: TIdHTTPSessionThreadList; + procedure RemoveSession(Session: TIdHTTPSession); override; + // remove a session surgically when list already locked down (prevent deadlock) + procedure RemoveSessionFromLockedList(AIndex: Integer; ALockedSessionList: TIdHTTPSessionList); + protected + procedure InitComponent; override; + public + destructor Destroy; override; + property SessionList: TIdHTTPSessionThreadList read FSessionList; + procedure Clear; override; + procedure Add(ASession: TIdHTTPSession); override; + procedure PurgeStaleSessions(PurgeAll: Boolean = false); override; + function CreateUniqueSession(const RemoteIP: String): TIdHTTPSession; override; + function CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession; override; + function GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; override; + end; + + TIdHTTPRangeStream = class(TIdBaseStream) + private + FSourceStream: TStream; + FOwnsSource: Boolean; + FRangeStart, FRangeEnd: Int64; + FResponseCode: Integer; + protected + function IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; override; + function IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; override; + function IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override; + procedure IdSetSize(ASize: Int64); override; + public + constructor Create(ASource: TStream; ARangeStart, ARangeEnd: Int64; AOwnsSource: Boolean = True); + destructor Destroy; override; + property ResponseCode: Integer read FResponseCode; + property RangeStart: Int64 read FRangeStart; + property RangeEnd: Int64 read FRangeEnd; + property SourceStream: TStream read FSourceStream; + end; + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.SyncObjs, + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + Libc, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + System.Threading, + {$ENDIF} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + {$IFDEF VCL_2010_OR_ABOVE} + {$IFDEF WINDOWS} + Windows, + {$ENDIF} + {$ENDIF} + IdCoderMIME, IdResourceStringsProtocols, IdURI, IdIOHandler, IdIOHandlerSocket, + IdSSL, IdResourceStringsCore, IdStream; + +const + SessionCapacity = 128; + ContentTypeFormUrlencoded = 'application/x-www-form-urlencoded'; {Do not Localize} + + // Calculate the number of MS between two TimeStamps + +function TimeStampInterval(const AStartStamp, AEndStamp: TDateTime): integer; +begin + Result := Trunc((AEndStamp - AStartStamp) * MSecsPerDay); +end; + +{ //(Bas Gooijen) was: +function TimeStampInterval(StartStamp, EndStamp: TDateTime): integer; +var + days: Integer; + hour, min, s, ms: Word; +begin + days := Trunc(EndStamp - StartStamp); // whole days + DecodeTime(EndStamp - StartStamp, hour, min, s, ms); + Result := (((days * 24 + hour) * 60 + min) * 60 + s) * 1000 + ms; +end; +} + + +function GetRandomString(NumChar: UInt32): string; +const + CharMap = 'qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890'; {Do not Localize} + MaxChar: UInt32 = Length(CharMap) - 1; +var + i: integer; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + randomize; + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(NumChar); + {$ELSE} + SetLength(Result, NumChar); + {$ENDIF} + for i := 1 to NumChar do + begin + // Add one because CharMap is 1-based + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharMap[Random(MaxChar) + 1]); + {$ELSE} + Result[i] := CharMap[Random(MaxChar) + 1]; + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +function DecodeHTTPCommand(const ACmd: string): THTTPCommandType; +var + I: Integer; +begin + Result := hcUnknown; + for I := Low(HTTPRequestStrings) to High(HTTPRequestStrings) do begin + if TextIsSame(ACmd, HTTPRequestStrings[i]) then begin + Result := THTTPCommandType(i); + Exit; + end; + end; // for +end; + +type + TIdHTTPSessionCleanerThread = Class(TIdThread) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSessionList: TIdHTTPCustomSessionList; + public + constructor Create(SessionList: TIdHTTPCustomSessionList); reintroduce; + procedure AfterRun; override; + procedure Run; override; + end; // class + +function InternalReadLn(AIOHandler: TIdIOHandler): String; +begin + Result := AIOHandler.ReadLn; + if AIOHandler.ReadLnTimedout then begin + raise EIdReadTimeout.Create(RSReadTimeout); + end; +end; + +{ TIdThreadSafeMimeTable } + +constructor TIdThreadSafeMimeTable.Create(const AutoFill: Boolean = True); +begin + inherited Create; + FTable := TIdMimeTable.Create(AutoFill); +end; + +destructor TIdThreadSafeMimeTable.Destroy; +begin + inherited Lock; + try + FreeAndNil(FTable); + finally + inherited Unlock; + end; + inherited Destroy; +end; + +function TIdThreadSafeMimeTable.GetLoadTypesFromOS: Boolean; +begin + Lock; + try + Result := FTable.LoadTypesFromOS; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.SetLoadTypesFromOS(AValue: Boolean); +begin + Lock; + try + FTable.LoadTypesFromOS := AValue; + finally + Unlock; + end; +end; + +function TIdThreadSafeMimeTable.GetOnBuildCache: TNotifyEvent; +begin + Lock; + try + Result := FTable.OnBuildCache; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.SetOnBuildCache(AValue: TNotifyEvent); +begin + Lock; + try + FTable.OnBuildCache := AValue; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.BuildCache; +begin + Lock; + try + FTable.BuildCache; + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.AddMimeType(const Ext, MIMEType: string; const ARaiseOnError: Boolean = True); +begin + Lock; + try + FTable.AddMimeType(Ext, MIMEType, ARaiseOnError); + finally + Unlock; + end; +end; + +function TIdThreadSafeMimeTable.GetFileMIMEType(const AFileName: string): string; +begin + Lock; + try + Result := FTable.GetFileMIMEType(AFileName); + finally + Unlock; + end; +end; + +function TIdThreadSafeMimeTable.GetDefaultFileExt(const MIMEType: string): string; +begin + Lock; + try + Result := FTable.GetDefaultFileExt(MIMEType); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.LoadFromStrings(const AStrings: TStrings; const MimeSeparator: Char = '='); {Do not Localize} +begin + Lock; + try + FTable.LoadFromStrings(AStrings, MimeSeparator); + finally + Unlock; + end; +end; + +procedure TIdThreadSafeMimeTable.SaveToStrings(const AStrings: TStrings; const MimeSeparator: Char = '='); {Do not Localize} +begin + Lock; + try + FTable.SaveToStrings(AStrings, MimeSeparator); + finally + Unlock; + end; +end; + +function TIdThreadSafeMimeTable.Lock: TIdMimeTable; +begin + inherited Lock; + Result := FTable; +end; + +procedure TIdThreadSafeMimeTable.Unlock; +begin + inherited Unlock; +end; + +{ TIdHTTPRangeStream } + +constructor TIdHTTPRangeStream.Create(ASource: TStream; ARangeStart, ARangeEnd: Int64; + AOwnsSource: Boolean = True); +var + LSize: Int64; +begin + inherited Create; + FSourceStream := ASource; + FOwnsSource := AOwnsSource; + FResponseCode := 200; + if (ARangeStart > -1) or (ARangeEnd > -1) then begin + LSize := ASource.Size; + if ARangeStart > -1 then begin + // requesting prefix range from BOF + if ARangeStart >= LSize then begin + // range unsatisfiable + FResponseCode := 416; + Exit; + end; + if ARangeEnd > -1 then begin + if ARangeEnd < ARangeStart then begin + // invalid syntax + Exit; + end; + ARangeEnd := IndyMin(ARangeEnd, LSize-1); + end else begin + ARangeEnd := LSize-1; + end; + end else begin + // requesting suffix range from EOF + if ARangeEnd = 0 then begin + // range unsatisfiable + FResponseCode := 416; + Exit; + end; + ARangeStart := IndyMax(LSize - ARangeEnd, 0); + ARangeEnd := LSize-1; + end; + FResponseCode := 206; + FRangeStart := ARangeStart; + FRangeEnd := ARangeEnd; + end; +end; + +destructor TIdHTTPRangeStream.Destroy; +begin + if FOwnsSource then begin + FreeAndNil(FSourceStream); + end; + inherited Destroy; +end; + +function TIdHTTPRangeStream.IdRead(var VBuffer: TIdBytes; AOffset, ACount: Longint): Longint; +begin + if FResponseCode = 206 then begin + ACount := Longint(IndyMin(Int64(ACount), (FRangeEnd+1) - FSourceStream.Position)); + end; + Result := TIdStreamHelper.ReadBytes(FSourceStream, VBuffer, ACount, AOffset); +end; + +function TIdHTTPRangeStream.IdSeek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; +var + LOffset: Int64; +begin + if FResponseCode = 206 then begin + case AOrigin of + soBeginning: LOffset := FRangeStart + AOffset; + soCurrent: LOffset := FSourceStream.Position + AOffset; + soEnd: LOffset := (FRangeEnd+1) + AOffset; + else + // TODO: move this into IdResourceStringsProtocols.pas + raise EIdException.Create('Unknown Seek Origin'); {do not localize} + end; + LOffset := IndyMax(LOffset, FRangeStart); + LOffset := IndyMin(LOffset, FRangeEnd+1); + Result := TIdStreamHelper.Seek(FSourceStream, LOffset, soBeginning) - FRangeStart; + end else begin + Result := TIdStreamHelper.Seek(FSourceStream, AOffset, AOrigin); + end; +end; + +function TIdHTTPRangeStream.IdWrite(const ABuffer: TIdBytes; AOffset, ACount: Longint): Longint; +begin + Result := 0; +end; + +procedure TIdHTTPRangeStream.IdSetSize(ASize: Int64); +begin +end; + +{ TIdCustomHTTPServer } + +procedure TIdCustomHTTPServer.InitComponent; +begin + inherited InitComponent; + FSessionState := Id_TId_HTTPServer_SessionState; + DefaultPort := IdPORT_HTTP; + ParseParams := Id_TId_HTTPServer_ParseParams; + FMIMETable := TIdThreadSafeMimeTable.Create(False); + FSessionTimeOut := Id_TId_HTTPSessionTimeOut; + FAutoStartSession := Id_TId_HTTPAutoStartSession; + FKeepAlive := Id_TId_HTTPServer_KeepAlive; + FMaximumHeaderLineCount := Id_TId_HTTPMaximumHeaderLineCount; + FSessionIDCookieName := GSessionIDCookie; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdCustomHTTPServer.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FSessionList) then begin + FSessionList := nil; + FImplicitSessionList := False; + end; + inherited Notification(AComponent, Operation); +end; + +function TIdCustomHTTPServer.DoParseAuthentication(ASender: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String): Boolean; +var + s: String; + LDecoder: TIdDecoderMIME; +begin + Result := False; + if Assigned(FOnParseAuthentication) then begin + FOnParseAuthentication(ASender, AAuthType, AAuthData, VUsername, VPassword, Result); + end; + if (not Result) and TextIsSame(AAuthType, 'Basic') then begin {Do not Localize} + LDecoder := TIdDecoderMIME.Create; + try + s := LDecoder.DecodeString(AAuthData); + finally + LDecoder.Free; + end; + VUsername := Fetch(s, ':'); {Do not Localize} + VPassword := s; + Result := True; + end; +end; + +procedure TIdCustomHTTPServer.DoOnCreateSession(AContext: TIdContext; Var VNewSession: TIdHTTPSession); +begin + VNewSession := nil; + if Assigned(FOnCreateSession) then + begin + OnCreateSession(AContext, VNewSession); + end; +end; + +function TIdCustomHTTPServer.CreateSession(AContext: TIdContext; HTTPResponse: TIdHTTPResponseInfo; + HTTPRequest: TIdHTTPRequestInfo): TIdHTTPSession; +var + LCookie: TIdCookie; + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + Result := nil; + if SessionState then begin + LSessionList := FSessionList; + if Assigned(LSessionList) then begin + DoOnCreateSession(AContext, Result); + if not Assigned(Result) then begin + Result := LSessionList.CreateUniqueSession(HTTPRequest.RemoteIP); + end else begin + LSessionList.Add(Result); + end; + + LCookie := HTTPResponse.Cookies.Add; + LCookie.CookieName := SessionIDCookieName; + LCookie.Value := Result.SessionID; + LCookie.Path := '/'; {Do not Localize} + + // By default the cookie will be valid until the user has closed his browser window. + // MaxAge := SessionTimeOut div 1000; + HTTPResponse.FSession := Result; + HTTPRequest.FSession := Result; + end; + end; +end; + +destructor TIdCustomHTTPServer.Destroy; +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + Active := False; // Set Active to false in order to close all active sessions. + FreeAndNil(FMIMETable); + LSessionList := FSessionList; + if Assigned(LSessionList) and FImplicitSessionList then begin + FSessionList := nil; + FImplicitSessionList := False; + IdDisposeAndNil(LSessionList); + end; + inherited Destroy; +end; + +procedure TIdCustomHTTPServer.DoCommandGet(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +begin + if Assigned(FOnCommandGet) then begin + FOnCommandGet(AContext, ARequestInfo, AResponseInfo); + end; +end; + +procedure TIdCustomHTTPServer.DoCommandOther(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +begin + if Assigned(FOnCommandOther) then begin + FOnCommandOther(AContext, ARequestInfo, AResponseInfo); + end; +end; + +procedure TIdCustomHTTPServer.DoCommandError(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; + AException: Exception); +begin + if Assigned(FOnCommandError) then begin + FOnCommandError(AContext, ARequestInfo, AResponseInfo, AException); + end; +end; + +procedure TIdCustomHTTPServer.DoConnect(AContext: TIdContext); +begin + // RLebeau 6/17/08: let the user decide whether to enable SSL in their + // own event handler. Indy should not be making any assumptions about + // whether to implicitally force SSL on any given connection. This + // prevents a single server from handling both SSL and non-SSL connections + // together. The whole point of the PassThrough property is to allow + // per-connection SSL handling. + // + // TODO: move this new logic into TIdCustomTCPServer directly somehow + + if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin + TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := + not DoQuerySSLPort(AContext.Connection.Socket.Binding.Port); + end; + inherited DoConnect(AContext); +end; + +function TIdCustomHTTPServer.DoQuerySSLPort(APort: TIdPort): Boolean; +begin + Result := not Assigned(FOnQuerySSLPort); + if not Result then begin + FOnQuerySSLPort(APort, Result); + end; +end; + +function TIdCustomHTTPServer.DoHeadersAvailable(ASender: TIdContext; const AUri: String; + AHeaders: TIdHeaderList): Boolean; +begin + Result := True; + if Assigned(OnHeadersAvailable) then begin + OnHeadersAvailable(ASender, AUri, AHeaders, Result); + end; +end; + +procedure TIdCustomHTTPServer.DoHeadersBlocked(ASender: TIdContext; AHeaders: TIdHeaderList; + var VResponseNo: Integer; var VResponseText, VContentText: String); +begin + VResponseNo := 403; + VResponseText := ''; + VContentText := ''; + if Assigned(OnHeadersBlocked) then begin + OnHeadersBlocked(ASender, AHeaders, VResponseNo, VResponseText, VContentText); + end; +end; + +function TIdCustomHTTPServer.DoHeaderExpectations(ASender: TIdContext; const AExpectations: String): Boolean; +begin + Result := TextIsSame(AExpectations, '100-continue'); {Do not Localize} + if Assigned(OnHeaderExpectations) then begin + OnHeaderExpectations(ASender, AExpectations, Result); + end; +end; + +function TIdCustomHTTPServer.DoExecute(AContext:TIdContext): boolean; +var + LRequestInfo: TIdHTTPRequestInfo; + LResponseInfo: TIdHTTPResponseInfo; + + procedure ReadCookiesFromRequestHeader; + var + LRawCookies: TStringList; + begin + LRawCookies := TStringList.Create; + try + LRequestInfo.RawHeaders.Extract('Cookie', LRawCookies); {Do not Localize} + LRequestInfo.Cookies.AddClientCookies(LRawCookies); + finally + FreeAndNil(LRawCookies); + end; + end; + + function GetRemoteIP(ASocket: TIdIOHandlerSocket): String; + begin + Result := ''; + if ASocket <> nil then begin + if ASocket.Binding <> nil then begin + Result := ASocket.Binding.PeerIP; + end; + end; + end; + + function HeadersCanContinue: Boolean; + var + LResponseNo: Integer; + LResponseText, LContentText, S: String; + begin + // let the user decide if the request headers are acceptable + Result := DoHeadersAvailable(AContext, LRequestInfo.URI, LRequestInfo.RawHeaders); + if not Result then begin + DoHeadersBlocked(AContext, LRequestInfo.RawHeaders, LResponseNo, LResponseText, LContentText); + LResponseInfo.ResponseNo := LResponseNo; + if Length(LResponseText) > 0 then begin + LResponseInfo.ResponseText := LResponseText; + end; + LResponseInfo.ContentText := LContentText; + LResponseInfo.CloseConnection := True; + LResponseInfo.WriteHeader; + if Length(LContentText) > 0 then begin + LResponseInfo.WriteContent; + end; + Exit; + end; + + // check for HTTP v1.1 'Host' and 'Expect' headers... + + if not LRequestInfo.IsVersionAtLeast(1, 1) then begin + Exit; + end; + + // MUST report a 400 (Bad Request) error if an HTTP/1.1 + // request does not include a 'Host' header + S := LRequestInfo.RawHeaders.Values['Host']; + if Length(S) = 0 then begin + LResponseInfo.ResponseNo := 400; + LResponseInfo.CloseConnection := True; + LResponseInfo.WriteHeader; + Exit; + end; + + // if the client has already sent some or all of the request + // body then don't bother checking for a v1.1 'Expect' header + if not AContext.Connection.IOHandler.InputBufferIsEmpty then begin + Exit; + end; + + S := LRequestInfo.RawHeaders.Values['Expect']; + if Length(S) = 0 then begin + Exit; + end; + + // check if the client expectations can be satisfied... + Result := DoHeaderExpectations(AContext, S); + if not Result then begin + LResponseInfo.ResponseNo := 417; + LResponseInfo.CloseConnection := True; + LResponseInfo.WriteHeader; + Exit; + end; + + if Pos('100-continue', LowerCase(S)) > 0 then begin {Do not Localize} + // the client requested a '100-continue' expectation so send + // a '100 Continue' reply now before the request body can be read + AContext.Connection.IOHandler.WriteLn(LRequestInfo.Version + ' 100 ' + RSHTTPContinue + EOL); {Do not Localize} + end; + end; + + function PreparePostStream: Boolean; + var + I, Size: Integer; + S: String; + LIOHandler: TIdIOHandler; + begin + Result := False; + LIOHandler := AContext.Connection.IOHandler; + + // RLebeau 1/6/2009: don't create the PostStream unless there is + // actually something to read. This should make it easier for the + // request handler to know when to use the PostStream and when to + // use the (Unparsed)Params instead... + + if (LRequestInfo.TransferEncoding <> '') and + (not TextIsSame(LRequestInfo.TransferEncoding, 'identity')) then {do not localize} + begin + if IndyPos('chunked', LowerCase(LRequestInfo.TransferEncoding)) = 0 then begin {do not localize} + LResponseInfo.ResponseNo := 400; // bad request + LResponseInfo.CloseConnection := True; + LResponseInfo.WriteHeader; + Exit; + end; + CreatePostStream(AContext, LRequestInfo.RawHeaders, LRequestInfo.FPostStream); + if LRequestInfo.FPostStream = nil then begin + LRequestInfo.FPostStream := TMemoryStream.Create; + end; + LRequestInfo.PostStream.Position := 0; + repeat + S := InternalReadLn(LIOHandler); + I := IndyPos(';', S); {do not localize} + if I > 0 then begin + S := Copy(S, 1, I - 1); + end; + Size := IndyStrToInt('$' + Trim(S), 0); {do not localize} + if Size = 0 then begin + Break; + end; + LIOHandler.ReadStream(LRequestInfo.PostStream, Size); + InternalReadLn(LIOHandler); // CRLF at end of chunk data + until False; + // skip trailer headers + repeat until InternalReadLn(LIOHandler) = ''; + LRequestInfo.PostStream.Position := 0; + end + else if LRequestInfo.HasContentLength then + begin + CreatePostStream(AContext, LRequestInfo.RawHeaders, LRequestInfo.FPostStream); + if LRequestInfo.FPostStream = nil then begin + LRequestInfo.FPostStream := TMemoryStream.Create; + end; + LRequestInfo.PostStream.Position := 0; + if LRequestInfo.ContentLength > 0 then begin + LIOHandler.ReadStream(LRequestInfo.PostStream, LRequestInfo.ContentLength); + LRequestInfo.PostStream.Position := 0; + end; + end + // If HTTP Pipelining is used by the client, bytes may exist that belong to + // the NEXT request! We need to look at the CURRENT request and only check + // for misreported body data if a body is actually expected. GET and HEAD + // requests do not have bodies... + else if LRequestInfo.CommandType in [hcPOST, hcPUT] then + begin + if LIOHandler.InputBufferIsEmpty then begin + LIOHandler.CheckForDataOnSource(1); + end; + if not LIOHandler.InputBufferIsEmpty then begin + LResponseInfo.ResponseNo := 411; // length required + LResponseInfo.CloseConnection := True; + LResponseInfo.WriteHeader; + Exit; + end; + end; + Result := True; + end; + +var + i: integer; + s, LInputLine, LRawHTTPCommand, LCmd, LContentType, LAuthType: String; + LURI: TIdURI; + LContinueProcessing, LCloseConnection: Boolean; + LConn: TIdTCPConnection; + LEncoding: IIdTextEncoding; +begin + LContinueProcessing := True; + Result := False; + LCloseConnection := not KeepAlive; + try + try + LConn := AContext.Connection; + repeat + LInputLine := InternalReadLn(LConn.IOHandler); + i := RPos(' ', LInputLine, -1); {Do not Localize} + if i = 0 then begin + raise EIdHTTPErrorParsingCommand.Create(RSHTTPErrorParsingCommand); + end; + LRequestInfo := TIdHTTPRequestInfo.Create(Self); + try + LResponseInfo := TIdHTTPResponseInfo.Create(Self, LRequestInfo, LConn); + try + // SG 05.07.99 + // Set the ServerSoftware string to what it's supposed to be. {Do not Localize} + LResponseInfo.ServerSoftware := Trim(ServerSoftware); + + // S.G. 6/4/2004: Set the maximum number of lines that will be catured + // S.G. 6/4/2004: to prevent a remote resource starvation DOS + LConn.IOHandler.MaxCapturedLines := MaximumHeaderLineCount; + + // Retrieve the HTTP version + LRawHTTPCommand := LInputLine; + LRequestInfo.FVersion := Copy(LInputLine, i + 1, MaxInt); + + s := LRequestInfo.Version; + Fetch(s, '/'); {Do not localize} + LRequestInfo.FVersionMajor := IndyStrToInt(Fetch(s, '.'), -1); {Do not Localize} + LRequestInfo.FVersionMinor := IndyStrToInt(S, -1); + + SetLength(LInputLine, i - 1); + + // Retrieve the HTTP header + LRequestInfo.RawHeaders.Clear; + LConn.IOHandler.Capture(LRequestInfo.RawHeaders, '', False); {Do not Localize} + // TODO: call HeadersCanContinue() here before the headers are parsed, + // in case the user needs to overwrite any values... + LRequestInfo.ProcessHeaders; + + // HTTP 1.1 connections are keep-alive by default + if not FKeepAlive then begin + LResponseInfo.CloseConnection := True; + end + else if LRequestInfo.IsVersionAtLeast(1, 1) then begin + LResponseInfo.CloseConnection := TextIsSame(LRequestInfo.Connection, 'close'); {Do not Localize} + end else begin + LResponseInfo.CloseConnection := not TextIsSame(LRequestInfo.Connection, 'keep-alive'); {Do not Localize} + end; + + {TODO Check for 1.0 only at this point} + LCmd := UpperCase(Fetch(LInputLine, ' ')); {Do not Localize} + + // check for overrides when LCmd is 'POST'... + if TextIsSame(LCmd, 'POST') then begin + s := LRequestInfo.MethodOverride; // Google/GData + if s = '' then begin + // TODO: make RequestInfo properties for these + s := LRequestInfo.RawHeaders.Values['X-HTTP-Method']; // Microsoft {do not localize} + if s = '' then begin + s := LRequestInfo.RawHeaders.Values['X-METHOD-OVERRIDE']; // IBM {do not localize} + end; + end; + if s <> '' then begin + LCmd := UpperCase(s); + end; + end; + + LRequestInfo.FRawHTTPCommand := LRawHTTPCommand; + LRequestInfo.FRemoteIP := GetRemoteIP(LConn.Socket); + LRequestInfo.FCommand := LCmd; + LRequestInfo.FCommandType := DecodeHTTPCommand(LCmd); + + // GET data - may exist with POSTs also + LRequestInfo.QueryParams := LInputLine; + LInputLine := Fetch(LRequestInfo.FQueryParams, '?'); {Do not Localize} + + // Host + // the next line is done in TIdHTTPRequestInfo.ProcessHeaders()... + // LRequestInfo.FHost := LRequestInfo.Headers.Values['host']; {Do not Localize} + + LRequestInfo.FURI := LInputLine; + + // Parse the document input line + if LInputLine = '*' then begin {Do not Localize} + LRequestInfo.FDocument := '*'; {Do not Localize} + end else begin + LURI := TIdURI.Create(LInputLine); + try + // SG 29/11/01: Per request of Doychin + // Try to fill the "host" parameter + LRequestInfo.FDocument := TIdURI.URLDecode(LURI.Path) + TIdURI.URLDecode(LURI.Document); + if (Length(LURI.Host) > 0) and (Length(LRequestInfo.FHost) = 0) then begin + LRequestInfo.FHost := LURI.Host; + end; + finally + FreeAndNil(LURI); + end; + end; + + // RLebeau 12/14/2005: provide the user with the headers and let the + // user decide whether the response processing should continue... + if not HeadersCanContinue then begin + Break; + end; + + // retreive the base ContentType with attributes omitted + LContentType := ExtractHeaderItem(LRequestInfo.ContentType); + + // Grab Params so we can parse them + // POSTed data - may exist with GETs also. With GETs, the action + // params from the form element will be posted + // TODO: Rune this is the area that needs fixed. Ive hacked it for now + // Get data can exists with POSTs, but can POST data exist with GETs? + // If only the first, the solution is easy. If both - need more + // investigation. + + if not PreparePostStream then begin + Break; + end; + + if LRequestInfo.PostStream <> nil then begin + if TextIsSame(LContentType, ContentTypeFormUrlencoded) then + begin + // decoding percent-encoded octets and applying the CharSet is handled by DecodeAndSetParams() further below... + EnsureEncoding(LEncoding, enc8Bit); + LRequestInfo.FormParams := ReadStringFromStream(LRequestInfo.PostStream, -1, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + DoneWithPostStream(AContext, LRequestInfo); // don't need the PostStream anymore + end; + end; + + // glue together parameters passed in the URL and those + // + // RLebeau: should we really be doing this? For a GET, it might + // makes sense to do, but for a POST the FormParams is the content + // and the QueryParams belongs to the URL only, not the content. + // We should be keeping everything separate for accuracy... + LRequestInfo.UnparsedParams := LRequestInfo.FormParams; + if Length(LRequestInfo.QueryParams) > 0 then begin + if Length(LRequestInfo.UnparsedParams) = 0 then begin + LRequestInfo.FUnparsedParams := LRequestInfo.QueryParams; + end else begin + LRequestInfo.FUnparsedParams := LRequestInfo.UnparsedParams + '&' {Do not Localize} + + LRequestInfo.QueryParams; + end; + end; + + // Parse Params + if ParseParams then begin + if TextIsSame(LContentType, ContentTypeFormUrlencoded) then begin + LRequestInfo.DecodeAndSetParams(LRequestInfo.UnparsedParams); + end else begin + // Parse only query params when content type is not 'application/x-www-form-urlencoded' {Do not Localize} + LRequestInfo.DecodeAndSetParams(LRequestInfo.QueryParams); + end; + end; + + // Cookies + ReadCookiesFromRequestHeader; + + // Authentication + s := LRequestInfo.RawHeaders.Values['Authorization']; {Do not Localize} + if Length(s) > 0 then begin + LAuthType := Fetch(s, ' '); + LRequestInfo.FAuthExists := DoParseAuthentication(AContext, LAuthType, s, LRequestInfo.FAuthUsername, LRequestInfo.FAuthPassword); + if not LRequestInfo.FAuthExists then begin + raise EIdHTTPUnsupportedAuthorisationScheme.Create( + RSHTTPUnsupportedAuthorisationScheme); + end; + end; + + // Session management + GetSessionFromCookie(AContext, LRequestInfo, LResponseInfo, LContinueProcessing); + if LContinueProcessing then begin + try + // These essentially all "retrieve" so they are all "Get"s + if LRequestInfo.CommandType in [hcGET, hcPOST, hcHEAD] then begin + DoCommandGet(AContext, LRequestInfo, LResponseInfo); + end else begin + DoCommandOther(AContext, LRequestInfo, LResponseInfo); + end; + except + on E: EIdSocketError do begin // don't stop socket exceptions + raise; + end; + on E: Exception do begin + LResponseInfo.ResponseNo := 500; + LResponseInfo.ContentText := E.Message; + DoCommandError(AContext, LRequestInfo, LResponseInfo, E); + end; + end; + end; + + // Write even though WriteContent will, may be a redirect or other + if not LResponseInfo.HeaderHasBeenWritten then begin + LResponseInfo.WriteHeader; + end; + // Always check ContentText first + if (Length(LResponseInfo.ContentText) > 0) + or Assigned(LResponseInfo.ContentStream) then begin + LResponseInfo.WriteContent; + end; + finally + LCloseConnection := LResponseInfo.CloseConnection; + FreeAndNil(LResponseInfo); + end; + finally + FreeAndNil(LRequestInfo); + end; + until LCloseConnection; + except + on E: EIdSocketError do begin + if not ((E.LastError = Id_WSAESHUTDOWN) or (E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + raise; + end; + end; + on E: EIdClosedSocket do begin + AContext.Connection.Disconnect; + end; + end; + finally + AContext.Connection.Disconnect(False); + end; +end; + +procedure TIdCustomHTTPServer.DoInvalidSession(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo; + var VContinueProcessing: Boolean; const AInvalidSessionID: String); +begin + if Assigned(FOnInvalidSession) then begin + FOnInvalidSession(AContext, ARequestInfo, AResponseInfo, VContinueProcessing, AInvalidSessionID) + end; +end; + +function TIdCustomHTTPServer.EndSession(const SessionName: String; const RemoteIP: String = ''): Boolean; +var + LSession: TIdHTTPSession; + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + Result := False; + LSessionList := SessionList; + if Assigned(LSessionList) then begin + LSession := SessionList.GetSession(SessionName, RemoteIP); {Do not Localize} + if Assigned(LSession) then begin + LSessionList.RemoveSession(LSession); + LSession.DoSessionEnd; + // must set the owner to nil or the session will try to fire the OnSessionEnd + // event again, and also remove itself from the session list and deadlock + LSession.FOwner := nil; + FreeAndNil(LSession); + Result := True; + end; + end; +end; + +procedure TIdCustomHTTPServer.DoSessionEnd(Sender: TIdHTTPSession); +begin + if Assigned(FOnSessionEnd) then begin + FOnSessionEnd(Sender); + end; +end; + +procedure TIdCustomHTTPServer.DoSessionStart(Sender: TIdHTTPSession); +begin + if Assigned(FOnSessionStart) then begin + FOnSessionStart(Sender); + end; +end; + +function TIdCustomHTTPServer.GetSessionFromCookie(AContext: TIdContext; + AHTTPRequest: TIdHTTPRequestInfo; AHTTPResponse: TIdHTTPResponseInfo; + var VContinueProcessing: Boolean): TIdHTTPSession; +var + LIndex: Integer; + LSessionID: String; + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + Result := nil; + VContinueProcessing := True; + if SessionState then + begin + LSessionList := FSessionList; + LIndex := AHTTPRequest.Cookies.GetCookieIndex(SessionIDCookieName); + while LIndex >= 0 do + begin + LSessionID := AHTTPRequest.Cookies[LIndex].Value; + if Assigned(LSessionList) then begin + Result := LSessionList.GetSession(LSessionID, AHTTPRequest.RemoteIP); + if Assigned(Result) then begin + Break; + end; + end; + DoInvalidSession(AContext, AHTTPRequest, AHTTPResponse, VContinueProcessing, LSessionID); + if not VContinueProcessing then begin + Break; + end; + LIndex := AHTTPRequest.Cookies.GetCookieIndex(SessionIDCookieName, LIndex+1); + end; { while } + // check if a session was returned. If not and if AutoStartSession is set to + // true, Create a new session + if (Result = nil) and VContinueProcessing and FAutoStartSession then begin + Result := CreateSession(AContext, AHTTPResponse, AHTTPrequest); + end; + end; + AHTTPRequest.FSession := Result; + AHTTPResponse.FSession := Result; +end; + +procedure TIdCustomHTTPServer.Startup; +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + inherited Startup; + + // set the session timeout and options + LSessionList := FSessionList; + if not Assigned(LSessionList) then begin + LSessionList := TIdHTTPDefaultSessionList.Create(Self); + FSessionList := LSessionList; + FImplicitSessionList := True; + end; + + if FSessionTimeOut <> 0 then begin + LSessionList.FSessionTimeout := FSessionTimeOut; + end else begin + FSessionState := False; + end; + + // Session events + LSessionList.OnSessionStart := DoSessionStart; + LSessionList.OnSessionEnd := DoSessionEnd; + + // If session handling is enabled, create the housekeeper thread + if SessionState then begin + FSessionCleanupThread := TIdHTTPSessionCleanerThread.Create(LSessionList); + end; +end; + +procedure TIdCustomHTTPServer.Shutdown; +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + // Boost the clear thread priority to give it a good chance to terminate + if Assigned(FSessionCleanupThread) then begin + IndySetThreadPriority(FSessionCleanupThread, tpNormal); + FSessionCleanupThread.TerminateAndWaitFor; + FreeAndNil(FSessionCleanupThread); + end; + + // RLebeau: FSessionList might not be assignd yet if Shutdown() is being + // called due to an exception raised in Startup()... + LSessionList := FSessionList; + if Assigned(LSessionList) then begin + if FImplicitSessionList then begin + SetSessionList(nil); + end else begin + LSessionList.Clear; + end; + {$IFDEF USE_OBJECT_ARC}LSessionList := nil;{$ENDIF} + end; + + inherited Shutdown; +end; + +procedure TIdCustomHTTPServer.SetSessionList(const AValue: TIdHTTPCustomSessionList); +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + LSessionList := FSessionList; + + if LSessionList <> AValue then + begin + // RLebeau - is this really needed? What should happen if this + // gets called by Notification() if the sessionList is freed while + // the server is still Active? + if Active then begin + raise EIdException.Create(RSHTTPCannotSwitchSessionListWhenActive); + end; + + // under ARC, all weak references to a freed object get nil'ed automatically + + // If implicit one already exists free it + // Free the default SessionList + if FImplicitSessionList then begin + // Under D8 notification gets called after .Free of FreeAndNil, but before + // its set to nil with a side effect of IDisposable. To counteract this we + // set it to nil first. + // -Kudzu + FSessionList := nil; + FImplicitSessionList := False; + IdDisposeAndNil(LSessionList); + end; + + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will no longer be notified when the component is freed + if LSessionList <> nil then begin + LSessionList.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FSessionList := AValue; + + {$IFNDEF USE_OBJECT_ARC} + // Ensure we will be notified when the component is freed, even is it's on + // another form + if AValue <> nil then begin + AValue.FreeNotification(Self); + end; + {$ENDIF} + end; +end; + +procedure TIdCustomHTTPServer.SetSessionState(const Value: Boolean); +begin + // ToDo: Add thread multiwrite protection here + if (not (IsDesignTime or IsLoading)) and Active then begin + raise EIdHTTPCannotSwitchSessionStateWhenActive.Create(RSHTTPCannotSwitchSessionStateWhenActive); + end; + FSessionState := Value; +end; + +procedure TIdCustomHTTPServer.SetSessionIDCookieName(const AValue: string); +var + LCookieName: string; +begin + // ToDo: Add thread multiwrite protection here + if (not (IsDesignTime or IsLoading)) and Active then begin + raise EIdHTTPCannotSwitchSessionIDCookieNameWhenActive.Create(RSHTTPCannotSwitchSessionIDCookieNameWhenActive); + end; + LCookieName := Trim(AValue); + if LCookieName = '' then begin + // TODO: move this into IdResourceStringsProtocols.pas + raise EIdException.Create('Invalid cookie name'); {do not localize} + end; + FSessionIDCookieName := AValue; +end; + +function TIdCustomHTTPServer.IsSessionIDCookieNameStored: Boolean; +begin + Result := not TextIsSame(SessionIDCookieName, GSessionIDCookie); +end; + +procedure TIdCustomHTTPServer.CreatePostStream(ASender: TIdContext; + AHeaders: TIdHeaderList; var VPostStream: TStream); +begin + if Assigned(OnCreatePostStream) then begin + OnCreatePostStream(ASender, AHeaders, VPostStream); + end; +end; + +procedure TIdCustomHTTPServer.DoneWithPostStream(ASender: TIdContext; + ARequestInfo: TIdHTTPRequestInfo); +var + LCanFree: Boolean; +begin + LCanFree := True; + if Assigned(FOnDoneWithPostStream) then begin + FOnDoneWithPostStream(ASender, ARequestInfo, LCanFree); + end; + if LCanFree then begin + FreeAndNil(ARequestInfo.FPostStream); + end; +end; + +{ TIdHTTPSession } + +constructor TIdHTTPSession.Create(AOwner: TIdHTTPCustomSessionList); +begin + inherited Create; + + FLock := TIdCriticalSection.Create; + FContent := TStringList.Create; + FOwner := AOwner; + if Assigned(AOwner) then + begin + if Assigned(AOwner.OnSessionStart) then begin + AOwner.OnSessionStart(Self); + end; + end; +end; + +constructor TIdHTTPSession.CreateInitialized(AOwner: TIdHTTPCustomSessionList; const SessionID, RemoteIP: string); +begin + inherited Create; + + FSessionID := SessionID; + FRemoteHost := RemoteIP; + FLastTimeStamp := Now; + FLock := TIdCriticalSection.Create; + FContent := TStringList.Create; + FOwner := AOwner; + if Assigned(AOwner) then + begin + if Assigned(AOwner.OnSessionStart) then begin + AOwner.OnSessionStart(Self); + end; + end; +end; + +destructor TIdHTTPSession.Destroy; +begin +// code added here should also be reflected in +// the TIdHTTPDefaultSessionList.RemoveSessionFromLockedList method +// Why? It calls this function and this code gets executed? + DoSessionEnd; + FreeAndNil(FContent); + FreeAndNil(FLock); + if Assigned(FOwner) then begin + FOwner.RemoveSession(Self); + end; + inherited Destroy; +end; + +procedure TIdHTTPSession.DoSessionEnd; +begin + if Assigned(FOwner) and Assigned(FOwner.FOnSessionEnd) then begin + FOwner.FOnSessionEnd(Self); + end; +end; + +function TIdHTTPSession.IsSessionStale: boolean; +var + // under ARC, convert a weak reference to a strong reference before working with it + LOwner: TIdHTTPCustomSessionList; +begin + LOwner := FOwner; + if Assigned(LOwner) then begin + Result := TimeStampInterval(FLastTimeStamp, Now) > Integer(LOwner.SessionTimeout); + end else begin + Result := True; + end; +end; + +procedure TIdHTTPSession.Lock; +begin + // ToDo: Add session locking code here + FLock.Enter; +end; + +procedure TIdHTTPSession.SetContent(const Value: TStrings); +begin + FContent.Assign(Value); +end; + +procedure TIdHTTPSession.Unlock; +begin + // ToDo: Add session unlocking code here + FLock.Leave; +end; + +{ TIdHTTPRequestInfo } + +constructor TIdHTTPRequestInfo.Create(AOwner: TPersistent); +begin + inherited Create(AOwner); + FCommandType := hcUnknown; + FCookies := TIdCookies.Create(Self); + FParams := TStringList.Create; + ContentLength := -1; +end; + +procedure TIdHTTPRequestInfo.DecodeAndSetParams(const AValue: String); +var + i, j : Integer; + s: string; + LEncoding: IIdTextEncoding; +begin + // Convert special characters + // ampersand '&' separates values {Do not Localize} + Params.BeginUpdate; + try + Params.Clear; + // TODO: provide an event or property that lets the user specify + // which charset to use for decoding query string parameters. We + // should not be using the 'Content-Type' charset for that. For + // 'application/x-www-form-urlencoded' forms, we should be, though... + LEncoding := CharsetToEncoding(CharSet); + i := 1; + while i <= Length(AValue) do + begin + j := i; + while (j <= Length(AValue)) and (AValue[j] <> '&') do {do not localize} + begin + Inc(j); + end; + s := Copy(AValue, i, j-i); + // See RFC 1866 section 8.2.1. TP + s := ReplaceAll(s, '+', ' '); {do not localize} + Params.Add(TIdURI.URLDecode(s, LEncoding)); + i := j + 1; + end; + finally + Params.EndUpdate; + end; +end; + +destructor TIdHTTPRequestInfo.Destroy; +begin + FreeAndNil(FCookies); + FreeAndNil(FParams); + FreeAndNil(FPostStream); + inherited Destroy; +end; + +function TIdHTTPRequestInfo.IsVersionAtLeast(const AMajor, AMinor: Integer): Boolean; +begin + Result := (FVersionMajor > AMajor) or + ((FVersionMajor = AMajor) and (FVersionMinor >= AMinor)); +end; + +{ TIdHTTPResponseInfo } + +procedure TIdHTTPResponseInfo.CloseSession; +var + i: Integer; + LCookie: TIdCookie; +begin + i := Cookies.GetCookieIndex(HTTPServer.SessionIDCookieName); + while i > -1 do begin + Cookies.Delete(i); + i := Cookies.GetCookieIndex(HTTPServer.SessionIDCookieName, i); + end; + LCookie := Cookies.Add; + LCookie.CookieName := HTTPServer.SessionIDCookieName; + LCookie.Expires := Date-7; + FreeAndNil(FSession); +end; + +constructor TIdHTTPResponseInfo.Create(AServer: TIdCustomHTTPServer; + ARequestInfo: TIdHTTPRequestInfo; AConnection: TIdTCPConnection); +begin + inherited Create(AServer); + + FRequestInfo := ARequestInfo; + FConnection := AConnection; + FHttpServer := AServer; + + FFreeContentStream := True; + + ResponseNo := GResponseNo; + ContentType := ''; //GContentType; + ContentLength := GFContentLength; + + {Some clients may not support folded lines} + RawHeaders.FoldLines := False; + FCookies := TIdCookies.Create(Self); + + {TODO Specify version - add a class method dummy that calls version} + ServerSoftware := GServerSoftware; + +end; + +destructor TIdHTTPResponseInfo.Destroy; +begin + FreeAndNil(FCookies); + ReleaseContentStream; + inherited Destroy; +end; + +procedure TIdHTTPResponseInfo.Redirect(const AURL: string); +begin + ResponseNo := 302; + Location := AURL; +end; + +procedure TIdHTTPResponseInfo.ReleaseContentStream; +begin + if FreeContentStream then begin + FreeAndNil(FContentStream); + end else begin + FContentStream := nil; + end; +end; + +procedure TIdHTTPResponseInfo.SetCloseConnection(const Value: Boolean); +begin + Connection := iif(Value, 'close', 'keep-alive'); {Do not Localize} + // TODO: include a 'Keep-Alive' header to specify a timeout value + FCloseConnection := Value; +end; + +procedure TIdHTTPResponseInfo.SetCookies(const AValue: TIdCookies); +begin + FCookies.Assign(AValue); +end; + +procedure TIdHTTPResponseInfo.SetHeaders; +begin + inherited SetHeaders; + if Server <> '' then begin + FRawHeaders.Values['Server'] := Server; {Do not Localize} + end; + if Location <> '' then begin + FRawHeaders.Values['Location'] := Location; {Do not Localize} + end; + if FLastModified > 0 then begin + FRawHeaders.Values['Last-Modified'] := LocalDateTimeToHttpStr(FLastModified); {do not localize} + end; + if AuthRealm <> '' then begin + FRawHeaders.Values['WWW-Authenticate'] := 'Basic realm="' + AuthRealm + '"'; {Do not Localize} + end; +end; + +procedure TIdHTTPResponseInfo.SetResponseNo(const AValue: Integer); +begin + FResponseNo := AValue; + case FResponseNo of + 100: ResponseText := RSHTTPContinue; + // 2XX: Success + 200: ResponseText := RSHTTPOK; + 201: ResponseText := RSHTTPCreated; + 202: ResponseText := RSHTTPAccepted; + 203: ResponseText := RSHTTPNonAuthoritativeInformation; + 204: ResponseText := RSHTTPNoContent; + 205: ResponseText := RSHTTPResetContent; + 206: ResponseText := RSHTTPPartialContent; + // 3XX: Redirections + 301: ResponseText := RSHTTPMovedPermanently; + 302: ResponseText := RSHTTPMovedTemporarily; + 303: ResponseText := RSHTTPSeeOther; + 304: ResponseText := RSHTTPNotModified; + 305: ResponseText := RSHTTPUseProxy; + // 4XX Client Errors + 400: ResponseText := RSHTTPBadRequest; + 401: ResponseText := RSHTTPUnauthorized; + 403: ResponseText := RSHTTPForbidden; + 404: begin + ResponseText := RSHTTPNotFound; + // Close connection + CloseConnection := True; + end; + 405: ResponseText := RSHTTPMethodNotAllowed; + 406: ResponseText := RSHTTPNotAcceptable; + 407: ResponseText := RSHTTPProxyAuthenticationRequired; + 408: ResponseText := RSHTTPRequestTimeout; + 409: ResponseText := RSHTTPConflict; + 410: ResponseText := RSHTTPGone; + 411: ResponseText := RSHTTPLengthRequired; + 412: ResponseText := RSHTTPPreconditionFailed; + 413: ResponseText := RSHTTPRequestEntityTooLong; + 414: ResponseText := RSHTTPRequestURITooLong; + 415: ResponseText := RSHTTPUnsupportedMediaType; + 417: ResponseText := RSHTTPExpectationFailed; + 428: ResponseText := RSHTTPPreconditionRequired; + 429: ResponseText := RSHTTPTooManyRequests; + 431: ResponseText := RSHTTPRequestHeaderFieldsTooLarge; + // 5XX Server errors + 500: ResponseText := RSHTTPInternalServerError; + 501: ResponseText := RSHTTPNotImplemented; + 502: ResponseText := RSHTTPBadGateway; + 503: ResponseText := RSHTTPServiceUnavailable; + 504: ResponseText := RSHTTPGatewayTimeout; + 505: ResponseText := RSHTTPHTTPVersionNotSupported; + 511: ResponseText := RSHTTPNetworkAuthenticationRequired; + else + ResponseText := RSHTTPUnknownResponseCode; + end; + + {if ResponseNo >= 400 then + // Force COnnection closing when there is error during the request processing + CloseConnection := true; + end;} +end; + +function TIdHTTPResponseInfo.ServeFile(AContext: TIdContext; const AFile: String): Int64; +var + EnableTransferFile: Boolean; +begin + if Length(ContentType) = 0 then begin + ContentType := HTTPServer.MIMETable.GetFileMIMEType(AFile); + end; + ContentLength := FileSizeByName(AFile); + if Length(ContentDisposition) = 0 then begin + // TODO: use EncodeHeader() here... + ContentDisposition := IndyFormat('attachment; filename="%s";', [ExtractFileName(AFile)]); + end; + WriteHeader; + EnableTransferFile := not (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase); + Result := AContext.Connection.IOHandler.WriteFile(AFile, EnableTransferFile); +end; + +function TIdHTTPResponseInfo.SmartServeFile(AContext: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; const AFile: String): Int64; +var + LFileDate : TDateTime; + LReqDate : TDateTime; +begin + LFileDate := IndyFileAge(AFile); + if (LFileDate = 0.0) and (not FileExists(AFile)) then + begin + ResponseNo := 404; + Result := 0; + end else + begin + LReqDate := GMTToLocalDateTime(ARequestInfo.RawHeaders.Values['If-Modified-Since']); {do not localize} + // if the file date in the If-Modified-Since header is within 2 seconds of the + // actual file, then we will send a 304. We don't use the ETag - offers nothing + // over the file date for static files on windows. Linux: consider using iNode + + // RLebeau 2/21/2011: TODO - make use of ETag. It is supposed to be updated + // whenever the file contents change, regardless of the file's timestamps. + + if (LReqDate <> 0) and (abs(LReqDate - LFileDate) < 2 * (1 / (24 * 60 * 60))) then + begin + ResponseNo := 304; + Result := 0; + end else + begin + Date := Now; + LastModified := LFileDate; + Result := ServeFile(AContext, AFile); + end; + end; +end; + +procedure TIdHTTPResponseInfo.WriteContent; +begin + if not HeaderHasBeenWritten then begin + WriteHeader; + end; + + // RLebeau 11/23/2014: Per RFC 2616 Section 4.3: + // + // For response messages, whether or not a message-body is included with + // a message is dependent on both the request method and the response + // status code (section 6.1.1). All responses to the HEAD request method + // MUST NOT include a message-body, even though the presence of entity- + // header fields might lead one to believe they do. All 1xx + // (informational), 204 (no content), and 304 (not modified) responses + // MUST NOT include a message-body. All other responses do include a + // message-body, although it MAY be of zero length. + + if not ( + (FRequestInfo.CommandType = hcHEAD) or + ((ResponseNo div 100) = 1) or + (ResponseNo = 204) or + (ResponseNo = 304) + ) then + begin + // Always check ContentText first + if ContentText <> '' then begin + FConnection.IOHandler.Write(ContentText, CharsetToEncoding(CharSet)); + end + else if Assigned(ContentStream) then begin + ContentStream.Position := 0; + FConnection.IOHandler.Write(ContentStream); + end + else begin + FConnection.IOHandler.WriteLn('' + IntToStr(ResponseNo) + ' ' + ResponseText {Do not Localize} + + '', CharsetToEncoding(CharSet)); {Do not Localize} + end; + end; + + // Clear All - This signifies that WriteConent has been called. + ContentText := ''; {Do not Localize} + ReleaseContentStream; +end; + +procedure TIdHTTPResponseInfo.WriteHeader; +var + i: Integer; + LBufferingStarted: Boolean; +begin + if HeaderHasBeenWritten then begin + raise EIdHTTPHeaderAlreadyWritten.Create(RSHTTPHeaderAlreadyWritten); + end; + FHeaderHasBeenWritten := True; + + if AuthRealm <> '' then + begin + ResponseNo := 401; + if (Length(ContentText) = 0) and (not Assigned(ContentStream)) then + begin + ContentType := 'text/html; charset=utf-8'; {Do not Localize} + ContentText := '' + IntToStr(ResponseNo) + ' ' + ResponseText + ''; {Do not Localize} + ContentLength := -1; // calculated below + end; + end; + + // RLebeau 5/15/2012: for backwards compatibility. We really should + // make the user set this every time instead... + if ContentType = '' then begin + if (ContentText <> '') or (Assigned(ContentStream)) then begin + ContentType := 'text/html; charset=ISO-8859-1'; {Do not Localize} + end; + end; + + // RLebeau: according to RFC 2616 Section 4.4: + // + // If a Content-Length header field (section 14.13) is present, its + // decimal value in OCTETs represents both the entity-length and the + // transfer-length. The Content-Length header field MUST NOT be sent + // if these two lengths are different (i.e., if a Transfer-Encoding + // header field is present). If a message is received with both a + // Transfer-Encoding header field and a Content-Length header field, + // the latter MUST be ignored. + // ... + // Messages MUST NOT include both a Content-Length header field and a + // non-identity transfer-coding. If the message does include a non- + // identity transfer-coding, the Content-Length MUST be ignored. + + if (ContentLength = -1) and + ((TransferEncoding = '') or TextIsSame(TransferEncoding, 'identity')) then {do not localize} + begin + // Always check ContentText first + if ContentText <> '' then begin + ContentLength := CharsetToEncoding(CharSet).GetByteCount(ContentText); + end + else if Assigned(ContentStream) then begin + ContentLength := ContentStream.Size; + end else begin + ContentLength := 0; + end; + end; + + if Date <= 0 then begin + Date := Now; + end; + + SetHeaders; + + LBufferingStarted := not FConnection.IOHandler.WriteBufferingActive; + if LBufferingStarted then begin + FConnection.IOHandler.WriteBufferOpen; + end; + try + // Write HTTP status response + FConnection.IOHandler.WriteLn('HTTP/1.1 ' + IntToStr(ResponseNo) + ' ' + ResponseText); {Do not Localize} + // Write headers + FConnection.IOHandler.Write(RawHeaders); + // Write cookies + for i := 0 to Cookies.Count - 1 do begin + FConnection.IOHandler.WriteLn('Set-Cookie: ' + Cookies[i].ServerCookie); {Do not Localize} + end; + // HTTP headers end with a double CR+LF + FConnection.IOHandler.WriteLn; + if LBufferingStarted then begin + FConnection.IOHandler.WriteBufferClose; + end; + except + if LBufferingStarted then begin + FConnection.IOHandler.WriteBufferCancel; + end; + raise; + end; +end; + +function TIdHTTPResponseInfo.GetServer: string; +begin + Result := Server; +end; + +procedure TIdHTTPResponseInfo.SetServer(const Value: string); +begin + Server := Value; +end; + +{ TIdHTTPDefaultSessionList } + +procedure TIdHTTPDefaultSessionList.Add(ASession: TIdHTTPSession); +begin + SessionList.Add(ASession); +end; + +procedure TIdHTTPDefaultSessionList.Clear; +var + LSessionList: TIdHTTPSessionList; + LSession: TIdHTTPSession; + i: Integer; +begin + LSessionList := SessionList.LockList; + try + for i := LSessionList.Count - 1 DownTo 0 do + begin + LSession := {$IFDEF HAS_GENERICS_TList}LSessionList[i]{$ELSE}TIdHTTPSession(LSessionList[i]){$ENDIF}; + if LSession <> nil then + begin + LSession.DoSessionEnd; + // must set the owner to nil or the session will try to fire the + // OnSessionEnd event again, and also remove itself from the session + // list and deadlock + LSession.FOwner := nil; + FreeAndNil(LSession); + end; + end; + LSessionList.Clear; + LSessionList.Capacity := SessionCapacity; + finally + SessionList.UnlockList; + end; +end; + +function TIdHTTPDefaultSessionList.CreateSession(const RemoteIP, SessionID: String): TIdHTTPSession; +begin + Result := TIdHTTPSession.CreateInitialized(Self, SessionID, RemoteIP); + SessionList.Add(Result); +end; + +function TIdHTTPDefaultSessionList.CreateUniqueSession( + const RemoteIP: String): TIdHTTPSession; +var + SessionID: String; +begin + SessionID := GetRandomString(15); + while GetSession(SessionID, RemoteIP) <> nil do + begin + SessionID := GetRandomString(15); + end; // while + Result := CreateSession(RemoteIP, SessionID); +end; + +destructor TIdHTTPDefaultSessionList.Destroy; +begin + Clear; + FreeAndNil(FSessionList); + inherited destroy; +end; + +function TIdHTTPDefaultSessionList.GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; +var + LSessionList: TIdHTTPSessionList; + LSession: TIdHTTPSession; + i: Integer; +begin + Result := nil; + LSessionList := SessionList.LockList; + try + // get current time stamp + for i := 0 to LSessionList.Count - 1 do + begin + LSession := TIdHTTPSession(LSessionList[i]); + // the stale sessions check has been removed... the cleanup thread should suffice plenty + if Assigned(LSession) and TextIsSame(LSession.FSessionID, SessionID) and ((Length(RemoteIP) = 0) or TextIsSame(LSession.RemoteHost, RemoteIP)) then + begin + // Session found + LSession.FLastTimeStamp := Now; + Result := LSession; + Break; + end; + end; + finally + SessionList.UnlockList; + end; +end; + +procedure TIdHTTPDefaultSessionList.InitComponent; +var + LList: TIdHTTPSessionList; +begin + inherited InitComponent; + FSessionList := TIdHTTPSessionThreadList.Create; + LList := FSessionList.LockList; + try + LList.Capacity := SessionCapacity; + finally + FSessionList.UnlockList; + end; +end; + +procedure TIdHTTPDefaultSessionList.PurgeStaleSessions(PurgeAll: Boolean = false); +var + LSessionList: TIdHTTPSessionList; + LSession: TIdHTTPSession; + i: Integer; +begin + // S.G. 24/11/00: Added a way to force a session purge (Used when thread is terminated) + // Get necessary data + Assert(SessionList<>nil); + + LSessionList := SessionList.LockList; + try + // Loop though the sessions. + for i := LSessionList.Count - 1 downto 0 do + begin + // Identify the stale sessions + LSession := {$IFDEF HAS_GENERICS_TList}LSessionList[i]{$ELSE}TIdHTTPSession(LSessionList[i]){$ENDIF}; + if Assigned(LSession) and (PurgeAll or LSession.IsSessionStale) then + begin + RemoveSessionFromLockedList(i, LSessionList); + end; + end; + finally + SessionList.UnlockList; + end; +end; + +procedure TIdHTTPDefaultSessionList.RemoveSession(Session: TIdHTTPSession); +var + LSessionList: TIdHTTPSessionList; + Index: integer; +begin + LSessionList := SessionList.LockList; + try + Index := LSessionList.IndexOf(Session); + if index > -1 then + begin + LSessionList.Delete(index); + end; + finally + SessionList.UnlockList; + end; +end; + +procedure TIdHTTPDefaultSessionList.RemoveSessionFromLockedList(AIndex: Integer; + ALockedSessionList: TIdHTTPSessionList); +var + LSession: TIdHTTPSession; +begin + LSession := {$IFDEF HAS_GENERICS_TList}ALockedSessionList[AIndex]{$ELSE}TIdHTTPSession(ALockedSessionList[AIndex]){$ENDIF}; + LSession.DoSessionEnd; + // must set the owner to nil or the session will try to fire the OnSessionEnd + // event again, and also remove itself from the session list and deadlock + LSession.FOwner := nil; + FreeAndNil(LSession); + ALockedSessionList.Delete(AIndex); +end; + +{ TIdHTTPSessionClearThread } + +procedure TIdHTTPSessionCleanerThread.AfterRun; +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + LSessionList := FSessionList; + if Assigned(LSessionList) then begin + LSessionList.PurgeStaleSessions(True); + end; + inherited AfterRun; +end; + +constructor TIdHTTPSessionCleanerThread.Create(SessionList: TIdHTTPCustomSessionList); +begin + inherited Create(false); + // thread priority used to be set to tpIdle but this is not supported + // under DotNet. How low do you want to go? + IndySetThreadPriority(Self, tpLowest); + FSessionList := SessionList; +end; + +procedure TIdHTTPSessionCleanerThread.Run; +var + // under ARC, convert a weak reference to a strong reference before working with it + LSessionList: TIdHTTPCustomSessionList; +begin + IndySleep(1000); + LSessionList := FSessionList; + if Assigned(LSessionList) then begin + LSessionList.PurgeStaleSessions(Terminated); + end; +end; + +end. diff --git a/indy/Protocols/IdDICT.pas b/indy/Protocols/IdDICT.pas new file mode 100644 index 0000000..1451ca4 --- /dev/null +++ b/indy/Protocols/IdDICT.pas @@ -0,0 +1,384 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 10/26/2004 8:59:34 PM JPMugaas + Updated with new TStrings references for more portability. + + Rev 1.7 2004.10.26 11:47:54 AM czhower + Changes to fix a conflict with aliaser. + + Rev 1.6 7/6/2004 4:55:22 PM DSiders + Corrected spelling of Challenge. + + Rev 1.5 6/11/2004 9:34:08 AM DSiders + Added "Do not Localize" comments. + + Rev 1.4 6/11/2004 6:16:44 AM DSiders + Corrected spelling in class names, properties, and methods. + + Rev 1.3 3/8/2004 10:08:48 AM JPMugaas + IdDICT now compiles with new code. IdDICT now added to palette. + + Rev 1.2 3/5/2004 7:23:56 AM JPMugaas + Fix for one server that does not send a feature list in the banner as RFC + 2229 requires. + + Rev 1.1 3/4/2004 3:55:02 PM JPMugaas + Untested work with SASL. + Fixed a problem with multiple entries using default. If AGetAll is true, a + "*" is used for all of the databases. "!" is for just the first database an + entry is found in. + + Rev 1.0 3/4/2004 2:44:16 PM JPMugaas + RFC 2229 DICT client. This is a preliminary version that was tested at + dict.org +} + +unit IdDICT; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, IdComponent, + IdDICTCommon, IdSASLCollection, IdTCPClient, IdTCPConnection; + +// TODO: MIME should be integrated into this. + +type + TIdDICTAuthenticationType = (datDefault, datSASL); + +const + DICT_AUTHDEF = datDefault; + DEF_TRYMIME = False; + +type + TIdDICT = class(TIdTCPClient) + protected + FTryMIME: Boolean; + FAuthType : TIdDICTAuthenticationType; + FSASLMechanisms : TIdSASLEntries; + FServer : String; + FClient : String; + //feature negotiation stuff + FCapabilities : TStrings; + procedure InitComponent; override; + function IsCapaSupported(const ACapa : String) : Boolean; + procedure SetClient(const AValue : String); + procedure InternalGetList(const ACmd : String; AENtries : TCollection); + procedure InternalGetStrs(const ACmd : String; AStrs : TStrings); + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + destructor Destroy; override; + procedure Connect; override; + procedure DisconnectNotifyPeer; override; + procedure GetDictInfo(const ADict : String; AResults : TStrings); + procedure GetSvrInfo(AResults : TStrings); + procedure GetDBList(ADB : TIdDBList); + procedure GetStrategyList(AStrats : TIdStrategyList); + procedure Define(const AWord, ADBName : String; AResults : TIdDefinitions); overload; + procedure Define(const AWord : String; AResults : TIdDefinitions; const AGetAll : Boolean = True); overload; + procedure Match(const AWord, ADBName, AStrat : String; AResults : TIdMatchList); overload; + procedure Match(const AWord, AStrat : String; AResults : TIdMatchList; const AGetAll : Boolean = True); overload; + procedure Match(const AWord : String; AResults : TIdMatchList; const AGetAll : Boolean = True); overload; + property Capabilities : TStrings read FCapabilities; + property Server : String read FServer; + published + property TryMIME : Boolean read FTryMIME write FTryMIME default DEF_TRYMIME; + property Client : String read FClient write SetClient; + property AuthType : TIdDICTAuthenticationType read FAuthType write FAuthType default DICT_AUTHDEF; + property SASLMechanisms : TIdSASLEntries read FSASLMechanisms write FSASLMechanisms; + property Port default IdPORT_DICT; + property Username; + property Password; + end; + +implementation + +uses + IdFIPS, + IdGlobal, IdGlobalProtocols, IdHash, IdHashMessageDigest, SysUtils; + +const + DEF_CLIENT_FMT = 'Indy Library %s'; {do not localize} + +{ TIdDICT } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDICT.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDICT.Connect; +var + LBuf : String; + LFeat : String; + s : String; + LMD5: TIdHashMessageDigest5; +begin + LBuf := ''; + FCapabilities.Clear; + + FServer := ''; + try + inherited Connect; + IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; + GetResponse(220); + if LastCmdResult.Text.Count > 0 then begin + // 220 pan.alephnull.com dictd 1.8.0/rf on Linux 2.4.18-14 <258510.25288.1078409724@pan.alephnull.com> + LBuf := LastCmdResult.Text[0]; + //server + FServer := TrimRight(Fetch(LBuf,'<')); + //feature negotiation + LFeat := Fetch(LBuf,'>'); + //One server I tested with has no feature negotiation at all and it returns something + //like this: + //220 dict.org Ho Ngoc Duc's DICT server 2.2 <1078465742246@dict.org> + if (IndyPos('@',LFeat)=0) and (IndyPos('<',LBuf)>0) then begin + BreakApart ( LFeat, '.', FCapabilities ); + end else begin + LBuf := '<'+LFeat+'>'; + end; + //LBuf is now for the APOP3 like Challenge + LBuf := Trim(LBuf); + end; + SendCmd('CLIENT '+FClient); {do not localize} + if FAuthType = datDefault then begin + if IsCapaSupported('auth') then begin {do not localize} + // RLebeau: why does this require FIPS? + if GetFIPSMode and (FPassword <> '') and (FUserName <> '') then begin + LMD5 := TIdHashMessageDigest5.Create; + try + S := LowerCase(LMD5.HashStringAsHex(LBuf+Password)); + finally + LMD5.Free; + end;//try + SendCmd('AUTH ' + Username + ' ' + S, 230); {do not localize} + end; + end; + end else begin + FSASLMechanisms.LoginSASL('SASLAUTH',FHost, 'dict', ['230'], ['330'], Self, FCapabilities, ''); {do not localize} + end; + if FTryMIME and IsCapaSupported('MIME') then begin {do not localize} + SendCmd('OPTION MIME'); {do not localize} + end; + except + Disconnect(False); + raise; + end; +end; + +procedure TIdDICT.Define(const AWord, ADBName : String; AResults : TIdDefinitions); +var LDef : TIdDefinition; + LBuf : String; +begin + AResults.Clear; + SendCmd('DEFINE '+ ADBName + ' ' + AWord); {do not localize} + repeat + if (LastCmdResult.NumericCode div 100) = 1 then begin + //Good, we got a response + LBuf := LastCmdResult.Text[0]; + case LastCmdResult.NumericCode of + 151 : + begin + LDef := AResults.Add; + //151 "Stuart" wn "WordNet (r) 2.0" + IOHandler.Capture(LDef.Definition); + //Word + Fetch(LBuf,'"'); + LDef.Word := Fetch(LBuf,'"'); + //db Name + Fetch(LBuf); + LDef.DB.Name := Fetch(LBuf); + //DB Description + Fetch(LBuf,'"'); + LDef.DB.Desc := Fetch(LBuf,'"'); + end; + 150 : + begin + // not sure what to do with the number + //get the defintions + end; + end; + Self.GetInternalResponse; + end else begin + Break; + end; + until False; +end; + +procedure TIdDICT.Define(const AWord : String; AResults : TIdDefinitions; const AGetAll : Boolean = True); +begin + if AGetAll then begin + Define(AWord,'*',AResults); + end else begin + Define(AWord,'!',AResults); + end; +end; + +destructor TIdDICT.Destroy; +begin + FreeAndNil(FSASLMechanisms); + FreeAndNil(FCapabilities); + inherited Destroy; +end; + +procedure TIdDICT.DisconnectNotifyPeer; +begin + try + if Connected then begin + SendCmd('QUIT', 221); {Do not Localize} + end; + finally + inherited DisconnectNotifyPeer; + end; +end; + +procedure TIdDICT.GetDBList(ADB: TIdDBList); +begin + InternalGetList('SHOW DB', ADB); {do not localize} +end; + +procedure TIdDICT.GetDictInfo(const ADict: String; AResults: TStrings); +begin + InternalGetStrs('SHOW INFO ' + ADict, AResults); {do not localize} +end; + +procedure TIdDICT.GetStrategyList(AStrats: TIdStrategyList); +begin + InternalGetList('SHOW STRAT', AStrats); {do not localize} +end; + +procedure TIdDICT.GetSvrInfo(AResults: TStrings); +begin + InternalGetStrs('SHOW SERVER', AResults); {do not localize} +end; + +procedure TIdDICT.InitComponent; +begin + inherited InitComponent; + FCapabilities := TStringList.create; + FSASLMechanisms := TIdSASLEntries.Create(Self); + FPort := IdPORT_DICT; + FAuthType := DICT_AUTHDEF; + FHost := 'dict.org'; {do not localize} + FClient := IndyFormat(DEF_CLIENT_FMT, [gsIdVersion]); +end; + +procedure TIdDICT.InternalGetList(const ACmd: String; AENtries: TCollection); +var + LEnt : TIdGeneric; + LS : TStrings; + i : Integer; + s : String; +begin + AEntries.Clear; + LS := TStringList.Create; + try + InternalGetStrs(ACmd,LS); + for i := 0 to LS.Count - 1 do begin + LEnt := AENtries.Add as TIdGeneric; + s := LS[i]; + LEnt.Name := Fetch(s); + Fetch(s, '"'); + LEnt.Desc := Fetch(s, '"'); + end; + finally + FreeAndNil(LS); + end; +end; + +procedure TIdDICT.InternalGetStrs(const ACmd: String; AStrs: TStrings); +begin + AStrs.Clear; + SendCmd(ACmd); + if (LastCmdResult.NumericCode div 100) = 1 then begin + IOHandler.Capture(AStrs); + GetInternalResponse; + end; +end; + +function TIdDICT.IsCapaSupported(const ACapa: String): Boolean; +var + i : Integer; +begin + Result := False; + for i := 0 to FCapabilities.Count-1 do begin + Result := TextIsSame(ACapa, FCapabilities[i]); + if Result then begin + Break; + end; + end; +end; + +procedure TIdDICT.Match(const AWord, ADBName, AStrat: String; + AResults: TIdMatchList); +var + LS : TStrings; + i : Integer; + s : String; + LM : TIdMatchItem; +begin + AResults.Clear; + LS := TStringList.Create; + try + InternalGetStrs('MATCH '+ADBName+' '+AStrat+' '+AWord,LS); {do not localize} + for i := 0 to LS.Count -1 do begin + s := LS[i]; + LM := AResults.Add; + LM.DB := Fetch(s); + Fetch(s, '"'); + LM.Word := Fetch(s, '"'); + end; + finally + FreeAndNil(LS); + end; +end; + +procedure TIdDICT.Match(const AWord, AStrat: String; + AResults: TIdMatchList; const AGetAll: Boolean); +begin + if AGetAll then begin + Match(AWord,'*','.',AResults); + end else begin + Match(AWord,'!','.',AResults); + end; +end; + +procedure TIdDICT.Match(const AWord: String; AResults: TIdMatchList; + const AGetAll: Boolean); +begin + Match(AWord,'.',AResults,AGetAll); +end; + +procedure TIdDICT.SetClient(const AValue: String); +//RFC 2229 says that a CLIENT command should always be +//sent immediately after connection. +begin + if AValue <> '' then begin + FClient := AValue; + end; +end; + +end. diff --git a/indy/Protocols/IdDICTCommon.pas b/indy/Protocols/IdDICTCommon.pas new file mode 100644 index 0000000..4edb99f --- /dev/null +++ b/indy/Protocols/IdDICTCommon.pas @@ -0,0 +1,291 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 10/26/2004 8:59:34 PM JPMugaas + Updated with new TStrings references for more portability. + + Rev 1.3 2004.10.26 11:47:56 AM czhower + Changes to fix a conflict with aliaser. + + Rev 1.2 8/16/2004 1:15:00 PM JPMugaas + Create and Destroy need to have the same visibility as inherited methods. + + Rev 1.1 6/11/2004 6:16:48 AM DSiders + Corrected spelling in class names, properties, and methods. + + Rev 1.0 3/4/2004 2:43:26 PM JPMugaas + RFC 2229 DICT Protocol helper objects for the client and probably when the + server when we get to it. +} + +unit IdDICTCommon; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes; + +type + TIdMatchItem = class(TCollectionItem) + protected + FDB : String; + FWord : String; + published + property DB : String read FDB write FDB; + property Word : String read FWord write FWord; + end; + + TIdMatchList = class(TCollection) + protected + function GetItems(AIndex: Integer): TIdMatchItem; + procedure SetItems(AIndex: Integer; const AValue: TIdMatchItem); + public + constructor Create; reintroduce; virtual; + function IndexOf(AItem: TIdMatchItem): Integer; + function Add: TIdMatchItem; + property Items[AIndex: Integer]: TIdMatchItem read GetItems write SetItems; default; + end; + + TIdGeneric = class(TCollectionItem) + protected + FName : String; + FDesc : String; + published + property Name : String read FName write FName; + property Desc : String read FDesc write FDesc; + end; + + TIdStrategy = class(TIdGeneric); + + TIdStrategyList = class(TCollection) + protected + function GetItems(AIndex: Integer): TIdStrategy; + procedure SetItems(AIndex: Integer; const AValue: TIdStrategy); + public + constructor Create; reintroduce; virtual; + function IndexOf(AItem: TIdStrategy): Integer; + function Add: TIdStrategy; + property Items[AIndex: Integer]: TIdStrategy read GetItems write SetItems; default; + end; + + TIdDBInfo = class(TIdGeneric); + + TIdDBList = class(TCollection) + protected + function GetItems(AIndex: Integer): TIdDBInfo; + procedure SetItems(AIndex: Integer; const AValue: TIdDBInfo); + public + constructor Create; reintroduce; virtual; + function IndexOf(AItem: TIdDBInfo): Integer; + function Add: TIdDBInfo; + property Items[AIndex: Integer]: TIdDBInfo read GetItems write SetItems; default; + end; + + TIdDefinition = class(TCollectionItem) + protected + FWord : String; + FDefinition : TStrings; + FDB : TIdDBInfo; + procedure SetDefinition(AValue : TStrings); + public + constructor Create(AOwner: TCollection); override; + destructor Destroy; override; + published + property Word : string read FWord write FWord; + property DB : TIdDBInfo read FDB write FDB; + property Definition : TStrings read FDefinition write SetDefinition; + end; + + TIdDefinitions = class(TCollection) + protected + function GetItems(AIndex: Integer): TIdDefinition; + procedure SetItems(AIndex: Integer; const AValue: TIdDefinition); + public + constructor Create; reintroduce; virtual; + function IndexOf(AItem: TIdDefinition): Integer; + function Add: TIdDefinition; + property Items[AIndex: Integer]: TIdDefinition read GetItems write SetItems; default; + end; + +implementation + +uses + IdGlobal, SysUtils; + +{ TIdDefinitions } + +function TIdDefinitions.Add: TIdDefinition; +begin + Result := TIdDefinition(inherited Add); +end; + +constructor TIdDefinitions.Create; +begin + inherited Create(TIdDefinition); +end; + +function TIdDefinitions.GetItems(AIndex: Integer): TIdDefinition; +begin + Result := TIdDefinition(inherited Items[AIndex]); +end; + +function TIdDefinitions.IndexOf(AItem: TIdDefinition): Integer; +Var + i: Integer; +begin + Result := -1; + for i := 0 to Count - 1 do + if AItem = Items[i] then begin + Result := i; + Break; + end; +end; + +procedure TIdDefinitions.SetItems(AIndex: Integer; + const AValue: TIdDefinition); +begin + inherited Items[AIndex] := AValue; +end; + +{ TIdDefinition } + +constructor TIdDefinition.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FDefinition := TStringList.Create; + FDB := TIdDBInfo.Create(nil); +end; + +destructor TIdDefinition.Destroy; +begin + FreeAndNil(FDB); + FreeAndNil(FDefinition); + inherited Destroy; +end; + +procedure TIdDefinition.SetDefinition(AValue: TStrings); +begin + FDefinition.Assign(AValue); +end; + +{ TIdDBList } + +function TIdDBList.Add: TIdDBInfo; +begin + Result := TIdDBInfo(inherited Add); +end; + +constructor TIdDBList.Create; +begin + inherited Create(TIdDBInfo); +end; + +function TIdDBList.GetItems(AIndex: Integer): TIdDBInfo; +begin + Result := TIdDBInfo(inherited Items[AIndex]); +end; + +function TIdDBList.IndexOf(AItem: TIdDBInfo): Integer; +Var + i: Integer; +begin + Result := -1; + for i := 0 to Count - 1 do + if AItem = Items[i] then begin + Result := i; + Break; + end; +end; + +procedure TIdDBList.SetItems(AIndex: Integer; const AValue: TIdDBInfo); +begin + inherited Items[AIndex] := AValue; +end; + +{ TIdStrategyList } + +function TIdStrategyList.Add: TIdStrategy; +begin + Result := TIdStrategy( inherited Add); +end; + +constructor TIdStrategyList.Create; +begin + inherited Create(TIdStrategy); +end; + +function TIdStrategyList.GetItems(AIndex: Integer): TIdStrategy; +begin + Result := TIdStrategy(inherited Items[AIndex]); +end; + +function TIdStrategyList.IndexOf(AItem: TIdStrategy): Integer; +Var + i: Integer; +begin + Result := -1; + for i := 0 to Count - 1 do + if AItem = Items[i] then begin + Result := i; + Break; + end; +end; + +procedure TIdStrategyList.SetItems(AIndex: Integer; + const AValue: TIdStrategy); +begin + inherited Items[AIndex] := AValue; +end; + +{ TIdMatchList } + +function TIdMatchList.Add: TIdMatchItem; +begin + Result := TIdMatchItem(inherited Add); +end; + +constructor TIdMatchList.Create; +begin + inherited Create(TIdMatchItem); +end; + +function TIdMatchList.GetItems(AIndex: Integer): TIdMatchItem; +begin + Result := TIdMatchItem(Inherited Items[AIndex]); +end; + +function TIdMatchList.IndexOf(AItem: TIdMatchItem): Integer; +Var + i: Integer; +begin + Result := -1; + for i := 0 to Count - 1 do + if AItem = Items[i] then begin + Result := i; + Break; + end; +end; + +procedure TIdMatchList.SetItems(AIndex: Integer; const AValue: TIdMatchItem); +begin + inherited SetItem(AIndex,AValue); +end; + +end. diff --git a/indy/Protocols/IdDICTServer.pas b/indy/Protocols/IdDICTServer.pas new file mode 100644 index 0000000..f49e9b8 --- /dev/null +++ b/indy/Protocols/IdDICTServer.pas @@ -0,0 +1,269 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 6/11/2004 6:16:50 AM DSiders + Corrected spelling in class names, properties, and methods. + + Rev 1.9 2004.02.03 5:45:06 PM czhower + Name changes + + Rev 1.8 1/21/2004 2:12:44 PM JPMugaas + InitComponent + + Rev 1.7 10/19/2003 11:51:16 AM DSiders + Added localization comments. + + Rev 1.6 2003.10.18 9:42:08 PM czhower + Boatload of bug fixes to command handlers. + + Rev 1.5 7/18/2003 4:24:48 PM SPerry + New DICT server using command handlers + + Rev 1.4 2/24/2003 08:22:52 PM JPMugaas + SHould compile with new code. + + Rev 1.3 1/17/2003 05:35:14 PM JPMugaas + Now compiles with new design. + + Rev 1.2 1-1-2003 20:12:52 BGooijen + Changed to support the new TIdContext class + + Rev 1.1 12/6/2002 02:17:42 PM JPMugaas + Now compiles with Indy 10. + + Rev 1.0 11/14/2002 02:17:24 PM JPMugaas + + 2000-15-May: J. Peter Mugaas - renamed events to have Id prefix + + 2000-22-Apr: J. Peter Mugaas + Ported to Indy + + 2000-23-JanL MTL Moved to new Palette Scheme + + 1999-23-Apr: Final Version +} + +unit IdDICTServer; + +{ + RFC 2229 - Dictionary Protocol (Structure). + Original Author: Ozz Nixon +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdGlobal, IdGlobalProtocols, IdResourceStringsProtocols, + IdAssignedNumbers, IdCommandHandlers, IdCmdTCPServer; + +type + TIdDICTGetEvent = procedure (AContext:TIdCommand) of object; + TIdDICTOtherEvent = procedure (AContext:TIdCommand; Command, Parm:String) of object; + TIdDICTDefineEvent = procedure (AContext:TIdCommand; Database, WordToFind : String) of object; + TIdDICTMatchEvent = procedure (AContext:TIdCommand; Database, Strategy,WordToFind : String) of object; + TIdDICTShowEvent = procedure (AContext:TIdCommand; Command : String) of object; + TIdDICTAuthEvent = procedure (AContext:TIdCommand; Username, authstring : String) of object; + + TIdDICTServer = class(TIdCmdTCPServer) + protected + fOnCommandHELP:TIdDICTGetEvent; + fOnCommandDEFINE:TIdDICTDefineEvent; + fOnCommandMATCH:TIdDICTMatchEvent; + fOnCommandQUIT:TIdDICTGetEvent; + fOnCommandSHOW:TIdDICTShowEvent; + fOnCommandAUTH, fOnCommandSASLAuth:TIdDICTAuthEvent; + fOnCommandOption:TIdDICTOtherEvent; + fOnCommandSTAT:TIdDICTGetEvent; + fOnCommandCLIENT:TIdDICTShowEvent; + fOnCommandOther:TIdDICTOtherEvent; + // + procedure DoOnCommandHELP(ASender: TIdCommand); + procedure DoOnCommandDEFINE(ASender: TIdCommand); + procedure DoOnCommandMATCH(ASender: TIdCommand); + procedure DoOnCommandQUIT(ASender: TIdCommand); + procedure DoOnCommandSHOW(ASender: TIdCommand); + procedure DoOnCommandAUTH(ASender: TIdCommand); + procedure DoOnCommandSASLAuth(ASender: TIdCommand); + procedure DoOnCommandOption(ASender: TIdCommand); + procedure DoOnCommandSTAT(ASender: TIdCommand); + procedure DoOnCommandCLIENT(ASender: TIdCommand); + procedure DoOnCommandOther(ASender: TIdCommand); + procedure DoOnCommandNotHandled(ASender: TIdCommandHandler; ACommand: TIdCommand; + const AData, AMessage: String); + // + procedure InitializeCommandHandlers; override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_DICT; + // + property OnCommandHelp: TIdDICTGetEvent read fOnCommandHelp write fOnCommandHelp; + property OnCommandDefine: TIdDICTDefineEvent read fOnCommandDefine write fOnCommandDefine; + property OnCommandMatch: TIdDICTMatchEvent read fOnCommandMatch write fOnCommandMatch; + property OnCommandQuit: TIdDICTGetEvent read fOnCommandQuit write fOnCommandQuit; + property OnCommandShow: TIdDICTShowEvent read fOnCommandShow write fOnCommandShow; + property OnCommandAuth: TIdDICTAuthEvent read fOnCommandAuth write fOnCommandAuth; + property OnCommandSASLAuth: TIdDICTAuthEvent read fOnCommandSASLAuth write fOnCommandSASLAuth; + property OnCommandOption: TIdDICTOtherEvent read fOnCommandOption write fOnCommandOption; + property OnCommandStatus: TIdDICTGetEvent read fOnCommandStat write fOnCommandStat; + property OnCommandClient: TIdDICTShowEvent read fOnCommandClient write fOnCommandClient; + property OnCommandOther: TIdDICTOtherEvent read fOnCommandOther write fOnCommandOther; + end; + +implementation + +procedure TIdDICTServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_DICT; +end; + +{ Command handlers } + +procedure TIdDICTServer.DoOnCommandHELP(ASender: TIdCommand); +begin + if assigned (OnCommandHelp) then + OnCommandHelp(ASender); +end; + +procedure TIdDICTServer.DoOnCommandDEFINE(ASender: TIdCommand); +begin + if assigned (OnCommandDefine) then + begin + OnCommandDefine (ASender, ASender.Params[0], ASender.Params[1]); + end; +end; + +procedure TIdDICTServer.DoOnCommandMATCH(ASender: TIdCommand); +begin + if assigned (OnCommandMatch) then + begin + OnCommandMatch(ASender, ASender.Params[0], ASender.Params[1], ASender.Params[2]); + end; +end; + +procedure TIdDICTServer.DoOnCommandQUIT(ASender: TIdCommand); +begin + if assigned (OnCommandQuit) then + OnCommandQuit (ASender); +end; + +procedure TIdDICTServer.DoOnCommandSHOW(ASender: TIdCommand); +begin + if assigned ( OnCommandShow ) then + OnCommandShow (ASender, ASender.Params[0]); +end; + +procedure TIdDICTServer.DoOnCommandAUTH(ASender: TIdCommand); +begin + if assigned (OnCommandAuth) then + begin + OnCommandAuth (ASender, ASender.Params[0], ASender.Params[1]); + end; +end; + +procedure TIdDICTServer.DoOnCommandSASLAuth(ASender: TIdCommand); +begin + if assigned ( OnCommandSASLAuth ) then + begin + OnCommandSASLAuth(ASender, ASender.Params[0], ASender.Params[1]); + end; +end; + +procedure TIdDICTServer.DoOnCommandOption(ASender: TIdCommand); +begin + if assigned(OnCommandOption) then + OnCommandOption(ASender, ASender.Params[0], ''); +end; + +procedure TIdDICTServer.DoOnCommandSTAT(ASender: TIdCommand); +begin + if assigned ( OnCommandStatus ) then + OnCommandStatus (ASender); +end; + +procedure TIdDICTServer.DoOnCommandCLIENT(ASender: TIdCommand); +begin + if assigned (OnCommandClient) then + OnCommandClient (ASender, ASender.Params[0]); +end; + +procedure TIdDICTServer.DoOnCommandOther(ASender: TIdCommand); +begin + +end; + +procedure TIdDICTServer.DoOnCommandNotHandled(ASender: TIdCommandHandler; + ACommand: TIdCommand; const AData, AMessage: String); +begin + ACommand.Context.Connection.IOHandler.WriteLn('500 ' + RSCMDNotRecognized); {do not localize} +end; + + +procedure TIdDICTServer.InitializeCommandHandlers; +var + LCommandHandler: TIdCommandHandler; +begin + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'AUTH'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandAUTH; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CLIENT'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandCLIENT; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'DEFINE'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandDEFINE; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'HELP'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandHELP; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'MATCH'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandMATCH; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'OPTION'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandOPTION; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'QUIT'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandQUIT; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SASLAUTH'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandSASLAUTH; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SHOW'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandSHOW; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'STATUS'; {do not localize} + LCommandHandler.OnCommand := DoOnCommandSTAT; + + { Other } + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := ''; + //LCommandHandler.OnCommand :=; + //LCommandHandler.OnException :=; +end; + +end. diff --git a/indy/Protocols/IdDNSCommon.pas b/indy/Protocols/IdDNSCommon.pas new file mode 100644 index 0000000..f94c8c5 --- /dev/null +++ b/indy/Protocols/IdDNSCommon.pas @@ -0,0 +1,2039 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ + + Rev 1.29 1/31/2005 9:02:44 PM JPMugaas + Should compile again. OOPS!! + + + Rev 1.28 1/28/2005 8:06:08 PM JPMugaas + Bug with MINFO, it was not returning the responsible E-Mail address. + + + Rev 1.27 1/28/2005 7:12:34 PM JPMugaas + Minor formatting adjustments. + + + Rev 1.26 1/28/2005 3:46:18 PM JPMugaas + Should compile. + + + Rev 1.25 2005/1/28 U 12:40:08 DChang + Add a new method for TIdTextModeResourceRecord to clean the created FAnswer, + then while the record updated, new data can be used in the FAnswer. + + + Rev 1.23 2005/1/25 U 12:24:14 DChang + For speeding up the query, one private variable is added into all TIdRR_ + series object, only first time query will generate the binary codes, the + others will read the result form the first time generated. + + + Rev 1.22 2004/12/15 W 11:12:18 DChang Version: 1.22 + Fix all BinQueryRecord method of TIdRR_*, + TIdRR_TXT.BinQueryRecord is completed, + and remark the comment of TIdTextModeResourceRecord.BinQueryRecord, + it's should be empty. + + + Rev 1.21 10/26/2004 9:06:30 PM JPMugaas + Updated references. + + + Rev 1.20 9/15/2004 4:59:34 PM DSiders + Added localization comments. + + + Rev 1.19 2004/7/19 U 09:43:40 DChang + 1. Move the TIdTextModeResourceRecords which was defined in + IdDNSServer.pas to here. + 2. Add a QueryType (DqtIXFR) in TDNSQueryRecordTypes. + + + Rev 1.18 6/29/04 1:22:32 PM RLebeau + Updated NormalStrToDNSStr() to use CopyTIdBytes() instead of AppendBytes() + + + Rev 1.17 2/11/2004 5:21:12 AM JPMugaas + Vladimir Vassiliev changes for removal of byte flipping. Network conversion + order conversion functions are used instead. + IPv6 addresses are returned in the standard form. + In WKS records, Address was changed to IPAddress to be consistant with other + record types. Address can also imply a hostname. + + + Rev 1.16 2/7/2004 7:18:30 PM JPMugaas + Moved some functions out of IdDNSCommon so we can use them elsewhere. + + + Rev 1.15 2004.02.07 5:45:10 PM czhower + Fixed compile error in D7. + + + Rev 1.14 2004.02.07 5:03:26 PM czhower + .net fixes. + + + Rev 1.13 2004.02.03 5:45:56 PM czhower + Name changes + + + Rev 1.12 12/7/2003 8:07:24 PM VVassiliev + string -> TIdBytes + + + Rev 1.11 11/15/2003 1:16:06 PM VVassiliev + Move AppendByte from IdDNSCommon to IdCoreGlobal + + + Rev 1.10 11/13/2003 5:46:04 PM VVassiliev + DotNet + + + Rev 1.9 10/25/2003 06:51:50 AM JPMugaas + Updated for new API changes and tried to restore some functionality. + + + Rev 1.8 10/19/2003 11:56:12 AM DSiders + Added localization comments. + + + Rev 1.7 2003.10.12 3:50:38 PM czhower + Compile todos + + + Rev 1.6 2003/5/8 U 08:07:12 DChang + Add several constants for IdDNSServer + + + Rev 1.5 4/28/2003 03:34:56 PM JPMugaas + Illiminated constant for the service path. IFDEF's for platforms are only + allowed in designated units. Besides, the location of the services file is + different in Win9x operating systems than NT operating systems. + + + Rev 1.4 4/28/2003 02:30:46 PM JPMugaas + reverted back to the old one as the new one checked will not compile, has + problametic dependancies on Contrs and Dialogs (both not permitted). + + + Rev 1.2 4/28/2003 07:00:04 AM JPMugaas + Should now compile. + + + Rev 1.0 11/14/2002 02:18:20 PM JPMugaas + Rev 1.3 04/28/2003 01:15:20 AM DenniesChang + + + // Add iRCode mode constants in May 4, 2003. + // Modify all DNS relative header in IdDNSCommon.pas + // Apr. 28, 2003 + + // Jun. 03, 2002. + // Add AXFR function + Duplicate some varible and constants in DNSCommon, + because Indy change version very frequently, these + varlibles and objects are isolated. + + I had added some methods into IdDNSResolver of Indy 9.02, + for parsing DN record directly and skip some check actions + from original query, but this modification will not relfect + the action of DN Query. + + Original Programmer: Dennies Chang + No Copyright. Code is given to the Indy Pit Crew. + + Started: Jan. 20, 2002. + Finished: +} + +unit IdDNSCommon; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdContainers, + IdException, + IdGlobal, + IdResourceStrings, + IdResourceStringsProtocols; + +const + IdDNSServerVersion = 'Indy DNSServer 20040121301'; {do not localize} + cRCodeNoError = 0; + cRCodeFormatErr = 1; + cRCodeServerErr = 2; + cRCodeNameErr = 3; + cRCodeNotImplemented = 4; + cRCodeRefused = 5; + + iRCodeQueryNotImplement = 0; + iRCodeQueryReturned = 1; + iRCodeQueryOK = 2; + iRCodeQueryNotFound = 3; + + iRCodeNoError = 0; + iRCodeFormatError = 1; + iRCodeServerFailure = 2; + iRCodeNameError = 3; + iRCodeNotImplemented = 4; + iRCodeRefused = 5; + + iQr_Question = 0; + iQr_Answer = 1; + + iAA_NotAuthoritative = 0; + iAA_Authoritative = 1; + + cRCodeQueryNotImplement = 'NA'; {do not localize} + cRCodeQueryReturned = 'RC'; // Return Completed. {do not localize} + cRCodeQueryOK = 'OK'; {do not localize} + cRCodeQueryCacheOK = 'COK'; {do not localize} + cRCodeQueryNotFound = 'NOTFOUND'; {do not localize} + cRCodeQueryCacheFindError = 'CFoundError'; {do not localize} + + RSDNSServerAXFRError_QuerySequenceError = 'First record must be SOA!'; {do not localize} + RSDNSServerSettingError_MappingHostError = 'Host must be an IP address'; {do not localize} + + cOrigin = '$ORIGIN'; {do not localize} + cInclude = '$INCLUDE'; {do not localize} + cAAAA = 'AAAA'; {do not localize} + cAt = '@'; {do not localize} + cA = 'A'; {do not localize} + cNS = 'NS'; {do not localize} + cMD = 'MD'; {do not localize} + cMF = 'MF'; {do not localize} + cCName = 'CNAME'; {do not localize} + cSOA = 'SOA'; {do not localize} + cMB = 'MB'; {do not localize} + cMG = 'MG'; {do not localize} + cMR = 'MR'; {do not localize} + cNULL = 'NULL'; {do not localize} + cWKS = 'WKS'; {do not localize} + cPTR = 'PTR'; {do not localize} + cHINFO = 'HINFO'; {do not localize} + cMINFO = 'MINFO'; {do not localize} + cMX = 'MX'; {do not localize} + cTXT = 'TXT'; {do not localize} + cNSAP = 'NSAP'; {do not localize} + cNSAP_PTR = 'NSAP-PTR'; {do not localize} + cLOC = 'LOC'; {do not localize} + cAXFR = 'AXFR'; {do not localize} + cIXFR = 'IXFR'; {do not localize} + cSTAR = 'STAR'; {do not localize} + + cRCodeStrs : Array[cRCodeNoError..cRCodeRefused] Of String = + (RSCodeNoError, + RSCodeQueryFormat, + RSCodeQueryServer, + RSCodeQueryName, + RSCodeQueryNotImplemented, + RSCodeQueryQueryRefused); + + Class_IN = 1; + Class_CHAOS = 3; + + TypeCode_A = 1; + TypeCode_NS = 2; + TypeCode_MD = 3; + TypeCode_MF = 4; + TypeCode_CName = 5; + TypeCode_SOA = 6; + TypeCode_MB = 7; + TypeCode_MG = 8; + TypeCode_MR = 9; + TypeCode_NULL = 10; + TypeCode_WKS = 11; + TypeCode_PTR = 12; + TypeCode_HINFO = 13; + TypeCode_MINFO = 14; + TypeCode_MX = 15; + TypeCode_TXT = 16; + TypeCode_RP = 17; + TypeCode_AFSDB = 18; + TypeCode_X25 = 19; + TypeCode_ISDN = 20; + TypeCode_RT = 21; + TypeCode_NSAP = 22; + TypeCode_NSAP_PTR = 23; + TypeCode_SIG = 24; + TypeCode_KEY = 25; + TypeCode_PX = 26; + TypeCode_QPOS = 27; + TypeCode_AAAA = 28; + TypeCode_LOC = 29; + TypeCode_NXT = 30; + TypeCode_R31 = 31; + TypeCode_R32 = 32; + TypeCode_Service = 33; + TypeCode_R34 = 34; + TypeCode_NAPTR = 35; + TypeCode_KX = 36; + TypeCode_CERT = 37; + TypeCode_V6Addr = 38; + TypeCode_DNAME = 39; + TypeCode_R40 = 40; + TypeCode_OPTIONAL = 41; + TypeCode_IXFR = 251; + TypeCode_AXFR = 252; + TypeCode_STAR = 255; + TypeCode_Error = 0; + +type + {NormalTags = (cA, cNS, cMD, cMF, cCName, cSOA, cMB, cMG, cMR, cNULL, cWKS, cPTR, + cHINFO, cMINFO, cMX, cTXT); } + TDNSQueryRecordTypes = (DqtA, DqtNS, DqtMD, DqtMF, DqtName, DqtSOA, DqtMB, + DqtMG, DqtMR, DqtNull, DqtWKS, DqtPTR, DqtHINFO, DqtMINFO, DqtMX, DqtTXT, + DqtNSAP, DqtNSAP_PTR, DqtLOC, DqtIXFR, DqtAXFR, DqtSTAR, DqtAAAA); + + TDNSServerTypes = (stPrimary, stSecondary); + + EIdDNSServerSyncException = class(EIdSilentException); + EIdDNSServerSettingException = class(EIdSilentException); + + // TODO: enable AD and CD properties. Those fields are reserved in RFC 1035, but defined in RFC 6895 + TDNSHeader = class + private + FID: UInt16; + FBitCode: UInt16; + FQDCount: UInt16; + FANCount: UInt16; + FNSCount: UInt16; + FARCount: UInt16; + function GetAA: UInt16; + //function GetAD: UInt16; + //function GetCD: UInt16; + function GetOpCode: UInt16; + function GetQr: UInt16; + function GetRA: UInt16; + function GetRCode: UInt16; + function GetRD: UInt16; + function GetTC: UInt16; + procedure SetAA(const Value: UInt16); + //procedure SetAD(const Value: UInt16); + //procedure SetCD(const Value: UInt16); + procedure SetOpCode(const Value: UInt16); + procedure SetQr(const Value: UInt16); + procedure SetRA(const Value: UInt16); + procedure SetRCode(const Value: UInt16); + procedure SetRD(const Value: UInt16); + procedure SetTC(const Value: UInt16); + procedure SetBitCode(const Value: UInt16); + public + constructor Create; + procedure ClearByteCode; + function ParseQuery(Data : TIdBytes) : integer; + function GenerateBinaryHeader : TIdBytes; + + property ID: UInt16 read FID write FID; + property Qr: UInt16 read GetQr write SetQr; + property OpCode: UInt16 read GetOpCode write SetOpCode; + property AA: UInt16 read GetAA write SetAA; + //property AD: UInt16 get GetAD write SetAD; + //property CD: UInt16 get GetCD write SetCD; + property TC: UInt16 read GetTC write SetTC; + property RD: UInt16 read GetRD write SetRD; + property RA: UInt16 read GetRA write SetRA; + property RCode: UInt16 read GetRCode write SetRCode; + property BitCode: UInt16 read FBitCode write SetBitCode; + property QDCount: UInt16 read FQDCount write FQDCount; + property ANCount: UInt16 read FANCount write FANCount; + property NSCount: UInt16 read FNSCount write FNSCount; + property ARCount: UInt16 read FARCount write FARCount; + end; + + TIdTextModeResourceRecord = class(TObject) + protected + FAnswer : TIdBytes; + FRRName: string; + FRRDatas: TStrings; //TODO Should not be TIdStrings + FTTL: Int32; + FTypeCode: Integer; + FTimeOut: string; + function FormatQName(const AFullName: string): string; overload; + function FormatQName(const AName, AFullName: string): string; overload; + function FormatQNameFull(const AFullName: string): string; + function FormatRecord(const AFullName: String; const ARRData: TIdBytes): TIdBytes; + procedure SetRRDatas(const Value: TStrings); + procedure SetTTL(const Value: Int32); + public + constructor CreateInit(const ARRName: String; ATypeCode: Integer); + destructor Destroy; override; + property TypeCode : Integer read FTypeCode; + property RRName : string read FRRName write FRRName; + property RRDatas : TStrings read FRRDatas write SetRRDatas; + property TTL : integer read FTTL write SetTTL; + property TimeOut : string read FTimeOut write FTimeOut; + function ifAddFullName(AFullName: string; AGivenName: string = ''): boolean; + function GetValue(const AName: String): String; + procedure SetValue(const AName: String; const AValue: String); + function ItemCount : Integer; + function BinQueryRecord(AFullName: string): TIdBytes; virtual; + function TextRecord(AFullName: string): string; virtual; + procedure ClearAnswer; + end; + + TIdTextModeRRs = class(TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}) + private + FItemNames : TStrings; + {$IFNDEF HAS_GENERICS_TObjectList} + function GetItem(Index: Integer): TIdTextModeResourceRecord; + procedure SetItem(Index: Integer; const Value: TIdTextModeResourceRecord); + {$ENDIF} + procedure SetItemNames(const Value: TStrings); + public + constructor Create; + destructor Destroy; override; + + property ItemNames : TStrings read FItemNames write SetItemNames; + {$IFNDEF HAS_GENERICS_TObjectList} + property Items[Index: Integer]: TIdTextModeResourceRecord read GetItem write SetItem; default; + {$ENDIF} + end; + + + TIdRR_CName = class(TIdTextModeResourceRecord) + protected + function GetCName: String; + procedure SetCName(const Value: String); + public + constructor Create; + property CName : String read GetCName write SetCName; + function BinQueryRecord(AFullName: string): TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_HINFO = class(TIdTextModeResourceRecord) + protected + procedure SetCPU(const Value: String); + function GetCPU: String; + function GetOS: String; + procedure SetOS(const Value: String); + public + constructor Create; + property CPU : String read GetCPU write SetCPU; + property OS : String read GetOS write SetOS; + function BinQueryRecord(AFullName : string): TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_MB = class(TIdTextModeResourceRecord) + protected + function GetMADName: String; + procedure SetMADName(const Value: String); + public + constructor Create; + property MADName : String read GetMADName write SetMADName; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_MG = class(TIdTextModeResourceRecord) + protected + function GetMGMName: String; + procedure SetMGMName(const Value: String); + public + constructor Create; + property MGMName : String read GetMGMName write SetMGMName; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_MINFO = class(TIdTextModeResourceRecord) + protected + procedure SetErrorHandle_Mail(const Value: String); + procedure SetResponsible_Mail(const Value: String); + function GetEMail: String; + function GetRMail: String; + public + constructor Create; + property Responsible_Mail : String read GetRMail write SetResponsible_Mail; + property ErrorHandle_Mail : String read GetEMail write SetErrorHandle_Mail; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_MR = class(TIdTextModeResourceRecord) + protected + function GetNewName: String; + procedure SetNewName(const Value: String); + public + constructor Create; + property NewName : String read GetNewName write SetNewName; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_MX = class(TIdTextModeResourceRecord) + protected + function GetExchang: String; + procedure SetExchange(const Value: String); + function GetPref: String; + procedure SetPref(const Value: String); + public + constructor Create; + property Exchange : String read GetExchang write SetExchange; + property Preference : String read GetPref write SetPref; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_NS = class(TIdTextModeResourceRecord) + protected + function GetNS: String; + procedure SetNS(const Value: String); + public + constructor Create; + property NSDName : String read GetNS write SetNS; + function BinQueryRecord(AFullName : string): TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_PTR = class(TIdTextModeResourceRecord) + protected + function GetPTRName: String; + procedure SetPTRName(const Value: String); + public + constructor Create; + property PTRDName : String read GetPTRName write SetPTRName; + function BinQueryRecord(AFullName : string): TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_SOA = class(TIdTextModeResourceRecord) + protected + function GetName(const CLabel : String): String; + procedure SetName(const CLabel: String; const Value : String); + function GetMName: String; + function GetRName: String; + procedure SetMName(const Value: String); + procedure SetRName(const Value: String); + function GetMin: String; + function GetRefresh: String; + function GetRetry: String; + function GetSerial: String; + procedure SetMin(const Value: String); + procedure SetRefresh(const Value: String); + procedure SetRetry(const Value: String); + procedure SetSerial(const Value: String); + function GetExpire: String; + procedure SetExpire(const Value: String); + public + constructor Create; + property MName : String read GetMName write SetMName; + property RName : String read GetRName write SetRName; + property Serial : String read GetSerial write SetSerial; + property Refresh : String read GetRefresh write SetRefresh; + property Retry : String read GetRetry write SetRetry; + property Expire : String read GetExpire write SetExpire; + property Minimum : String read GetMin write SetMin; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + + TIdRR_A = class(TIdTextModeResourceRecord) + protected + function GetA: String; + procedure SetA(const Value: String); + public + constructor Create; + property Address : String read GetA write SetA; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + TIdRR_AAAA = class(TIdTextModeResourceRecord) + protected + function GetA: String; + procedure SetA(const Value: String); + public + constructor Create; + property Address : String read GetA write SetA; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + { TODO : implement WKS record class } + TIdRR_WKS = class(TIdTextModeResourceRecord) + public + constructor Create; + end; + + TIdRR_TXT = class(TIdTextModeResourceRecord) + protected + function GetTXT: String; + procedure SetTXT(const Value: String); + public + constructor Create; + property TXT : String read GetTXT write SetTXT; + function BinQueryRecord(AFullName : string) : TIdBytes; override; + function TextRecord(AFullName : string) : string; override; + end; + + TIdRR_Error = class(TIdTextModeResourceRecord) + public + constructor Create; + end; + +function DomainNameToDNSStr(const ADomain : String): TIdBytes; +function NormalStrToDNSStr(const Str : String): TIdBytes; +function IPAddrToDNSStr(const IPAddress : String): TIdBytes; +function IsValidIPv6(const v6Address : String): Boolean; +function ConvertToValidv6IP(const OrgIP : String) : string; +function ConvertToCanonical6IP(const OrgIP : String) : string; +function IPv6AAAAToDNSStr(const AIPv6Address : String): TIdBytes; +function GetErrorStr(const Code, Id: Integer): String; +function GetRCodeStr(RCode : Integer): String; +function ReplaceSpecString(Source, Target, NewString : string; ReplaceAll : boolean = True) : string; +function IsBig5(ch1, ch2: Char) : Boolean; + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + {$IFNDEF NEXTGEN} + System.Contnrs, + {$ENDIF} + {$ENDIF} + {$IFDEF HAS_UNIT_DateUtils} + DateUtils, + {$ENDIF} + IdGlobalProtocols, + IdStack, SysUtils; + +const + ValidHexChars = '0123456789ABCDEFabcdef'; + +procedure IdBytesCopyBytes(const ASource: TIdBytes; var VDest: TIdBytes; var VDestIndex: Integer); +begin + CopyTIdBytes(ASource, 0, VDest, VDestIndex, Length(ASource)); + Inc(VDestIndex, Length(ASource)); +end; + +procedure IdBytesCopyUInt16(const ASource: UInt16; var VDest: TIdBytes; var VDestIndex: Integer); +begin + CopyTIdUInt16(ASource, VDest, VDestIndex); + Inc(VDestIndex, SizeOf(UInt16)); +end; + +procedure IdBytesCopyUInt32(const ASource: UInt32; var VDest: TIdBytes; var VDestIndex: Integer); +begin + CopyTIdUInt32(ASource, VDest, VDestIndex); + Inc(VDestIndex, SizeOf(UInt32)); +end; + +function DomainNameToDNSStr(const ADomain : string): TIdBytes; +var + BufStr, LDomain : String; + LIdx : Integer; + LLen: Byte; +begin + if Length(ADomain) = 0 then begin + SetLength(Result, 0); + end else begin + // TODO: ned to re-write this... + SetLength(Result, Length(ADomain)+1); + LIdx := 0; + LDomain := ADomain; + repeat + BufStr := Fetch(LDomain, '.'); + LLen := Length(BufStr); + Result[LIdx] := LLen; + CopyTIdString(BufStr, Result, LIdx+1, LLen); + Inc(LIdx, LLen+1); + until LDomain = ''; + Result[LIdx] := 0; + SetLength(Result, LIdx+1); + end; +end; + +function NormalStrToDNSStr(const Str : String): TIdBytes; +var + LLen: Byte; + LStr: TIdBytes; +begin + LStr := ToBytes(Str); + LLen := IndyMin(Length(LStr), $FF); + SetLength(Result, 1 + LLen); + Result[0] := LLen; + CopyTIdBytes(LStr, 0, Result, 1, LLen); +end; + +function IPAddrToDNSStr(const IPAddress : String): TIdBytes; +Var + j, i: Integer; + s : string; +begin + SetLength(Result, 0); + if IsValidIP(IPAddress) then begin + s := Trim(IPAddress); + SetLength(Result, 4); + for i := 0 to 3 do begin + j := IndyStrToInt(Fetch(s, '.'), -1); {do not localize} + if (j < 0) or (j > 255) then begin + Result := ToBytes('Error IP'); {do not localize} + Exit; + end; + Result[I] := Byte(j); + end; + end else begin + Result := ToBytes('Error IP'); {do not localize} + end; +end; + +procedure IdHexToBin(const AText: TIdBytes; var Buffer: TIdBytes; const BufSize: Integer); +const + Convert: array['0'..'f'] of Int16 = {do not localize} + ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15); +var + BufferPos, TextPos: Integer; + ValidChars: TIdBytes; +begin + ValidChars := ToBytes(ValidHexChars); + BufferPos := 0; + TextPos := 0; + repeat + if (not ByteIsInSet(AText, TextPos, ValidChars)) or + (not ByteIsInSet(AText, TextPos+1, ValidChars)) then + begin + Break; + end; + Buffer[BufferPos] := (Convert[Char(AText[TextPos])] shl 4) + Convert[Char(AText[TextPos + 1])]; + Inc(BufferPos); + Inc(TextPos, 2); + until False; +end; + +function IPv6AAAAToDNSStr(const AIPv6Address : String): TIdBytes; +var + LAddr : TIdIPv6Address; +begin + IPv6ToIdIPv6Address(AIPv6Address, LAddr); + SetLength(Result, 16); + CopyTIdIPV6Address(LAddr, Result, 0); +end; + +function IsValidIPv6(const v6Address : String): boolean; +var + Temps : TStrings; + Apart, All: String; + Count, Loc, Goal : integer; +begin + All := v6Address; + Temps := TStringList.Create; + try + // Check Double Colon existence, but only single. + Count := 0; + + repeat + Loc := IndyPos('::', All); {do not localize} + if Loc > 0 then begin + Count := Count + 1; + IdDelete(All, Loc, 2); + end; + until Loc = 0; + + if Count <= 1 then begin + // Convert Double colon into compatible format. + All := ReplaceSpecString(v6Address, '::', ':Multi:'); {do not localize} + repeat + Apart := Fetch(All, ':'); {do not localize} + Temps.Add(Apart); + until All = ''; {do not localize} + + Loc := Temps.IndexOf('Multi'); {do not localize} + if Loc > -1 then begin + Goal := 8 - Temps.Count; + Temps.Strings[Loc] := '0000'; {do not localize} + for Count := 0 to Goal -1 do begin + Temps.Insert(Loc, '0000'); {do not localize} + end; + if Temps.Strings[0] = '' then begin {do not localize} + Temps.Strings[0] := '0000'; {do not localize} + end; + end; + + All := ReplaceSpecString(Temps.CommaText, ',', ':'); {do not localize} + Result := True; + Temps.Clear; + + repeat + Apart := Trim(Fetch(All, ':')); {do not localize} + if Length(Apart) <= 4 then begin + Apart := '0000' + Apart; {do not localize} + Apart := Copy(Apart, Length(Apart)-3, 4); + Temps.Add(Apart); + end else begin + Result := False; + end; + until (All = '') or (not Result); {do not localize} + + if (not Result) or (Temps.Count > 8) then begin + Result := False; + end else begin + for Count := 0 to Temps.Count -1 do begin + All := All + Temps.Strings[Count]; + end; + Result := Length(All) > 0; + for Count := 1 to Length(All) do begin + Result := CharIsInSet(All, Count, ValidHexChars); + if not Result then begin + Break; + end; + end; + end; + end else begin + // mulitple Double colon, it's an incorrect IPv6 address. + Result := False; + end; + finally + FreeAndNil(Temps); + end; +end; + +function ConvertToValidv6IP(const OrgIP : String) : string; +var + All, Apart : string; + Temps : TStrings; + Count, Loc, Goal : integer; +begin + Result := ''; + All := OrgIP; + Temps := TStringList.Create; + try + // Check Double Colon existence, but only single. + // Count := 0; + + repeat + Loc := IndyPos('::', All); {do not localize} + if Loc > 0 then begin + // Count := Count + 1; + IdDelete(All, Loc, 2); + end; + until Loc = 0; + + // Convert Double colon into compatible format. + All := ReplaceSpecString(OrgIP, '::', ':Multi:'); {do not localize} + repeat + Apart := Fetch(All, ':'); {do not localize} + Temps.Add(Apart); + until All = ''; {do not localize} + + Loc := Temps.IndexOf('Multi'); {do not localize} + if Loc > -1 then begin + Goal := 8 - Temps.Count; + Temps.Strings[Loc] := '0000'; {do not localize} + for Count := 0 to Goal -1 do begin + Temps.Insert(Loc, '0000'); {do not localize} + end; + if Temps.Strings[0] = '' then begin + Temps.Strings[0] := '0000'; {do not localize} + end; + end; + Result := ReplaceSpecString(Temps.CommaText, ',', ':'); {do not localize} + finally + FreeAndNil(Temps); + end; +end; + +function ConvertToCanonical6IP(const OrgIP : String) : string; +var + All, Apart: string; +begin + {Supposed OrgIP is valid IPV6 string} + Result := ''; {do not localize} + All := ConvertToValidv6IP(OrgIP); + repeat + Apart := Trim(Fetch(All, ':')); {do not localize} + if Length(Apart) < 4 then + begin + Apart := '0000' + Apart; {do not localize} + Apart := Copy(Apart, Length(Apart)-3, 4); + end; + Result := Result + Apart + ':'; {do not localize} + until (All = ''); {do not localize} + SetLength(Result, Length(Result) - 1); //Remove last : +end; + +{ TODO : Move these to member } +function GetErrorStr(const Code, Id: Integer): String; +begin + case Code of + 1 : Result := IndyFormat(RSQueryInvalidQueryCount, [Id]); + 2 : Result := IndyFormat(RSQueryInvalidPacketSize, [Id]); + 3 : Result := IndyFormat(RSQueryLessThanFour, [Id]); + 4 : Result := IndyFormat(RSQueryInvalidHeaderID, [Id] ); + 5 : Result := IndyFormat(RSQueryLessThanTwelve, [Id]); + 6 : Result := IndyFormat(RSQueryPackReceivedTooSmall, [Id]); + else + Result := IndyFormat(RSQueryUnknownError, [Code, Id]); + end; //case code Of +end; + +function GetRCodeStr(RCode : Integer): String; +begin + if Rcode in [cRCodeNoError..cRCodeRefused] then begin + Result := cRCodeStrs[Rcode]; + end else begin // if Rcode in [cRCodeNoError..cRCodeRefused] then + Result := RSCodeQueryUnknownError; + end; //else.. if Rcode in [cRCodeNoError..cRCodeRefused] then +end; + + +{ TDNSHeader } + +procedure TDNSHeader.ClearByteCode; +begin + FBitCode := 0; +end; + +constructor TDNSHeader.Create; +begin + inherited Create; + Randomize; + FId := Random(65535); +end; + +function TDNSHeader.GenerateBinaryHeader: TIdBytes; +{ + + +The header contains the following fields: + + 1 1 1 1 1 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ID | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | QDCOUNT/ZOCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ANCOUNT/PRCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | NSCOUNT/UPCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ARCOUNT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +where: + +ID A 16 bit identifier assigned by the program that + generates any kind of query. This identifier is copied + the corresponding reply and can be used by the requester + to match up replies to outstanding queries. + +QR A one bit field that specifies whether this message is a + query (0), or a response (1). + +OPCODE A four bit field that specifies kind of query in this + message. This value is set by the originator of a query + and copied into the response. The values are: + + 0 a standard query (QUERY) + + 1 an inverse query (IQUERY) + + 2 a server status request (STATUS) + + 3-15 reserved for future use + +AA Authoritative Answer - this bit is valid in responses, + and specifies that the responding name server is an + authority for the domain name in question section. + + Note that the contents of the answer section may have + multiple owner names because of aliases. The AA bit + + corresponds to the name which matches the query name, or + the first owner name in the answer section. + +TC TrunCation - specifies that this message was truncated + due to length greater than that permitted on the + transmission channel. + +RD Recursion Desired - this bit may be set in a query and + is copied into the response. If RD is set, it directs + the name server to pursue the query recursively. + Recursive query support is optional. + +RA Recursion Available - this be is set or cleared in a + response, and denotes whether recursive query support is + available in the name server. + +Z Reserved for future use. Must be zero in all queries + and responses. + +AD Authentic Data - signal indicating that the requester + understands and is interested in the value of the AD bit + in the response. This allows a requester to indicate that + it understands the AD bit without also requesting DNSSEC + data via the DO bit. + +CD Checking Disabled + +RCODE Response code - this 4 bit field is set as part of + responses. The values have the following + interpretation: + + 0 No error condition + + 1 Format error - The name server was + unable to interpret the query. + + 2 Server failure - The name server was + unable to process this query due to a + problem with the name server. + + 3 Name Error - Meaningful only for + responses from an authoritative name + server, this code signifies that the + domain name referenced in the query does + not exist. + + 4 Not Implemented - The name server does + not support the requested kind of query. + + 5 Refused - The name server refuses to + perform the specified operation for + policy reasons. For example, a name + server may not wish to provide the + information to the particular requester, + or a name server may not wish to perform + a particular operation (e.g., zone + + transfer) for particular data. + + 6-15 Reserved for future use. + +QDCOUNT an unsigned 16 bit integer specifying the number of + entries in the question section. + +ANCOUNT an unsigned 16 bit integer specifying the number of + resource records in the answer section. + +NSCOUNT an unsigned 16 bit integer specifying the number of name + server resource records in the authority records + section. + +ARCOUNT an unsigned 16 bit integer specifying the number of + resource records in the additional records section. +} +begin + SetLength(Result, 12); + UInt16ToTwoBytes(GStack.HostToNetwork(ID), Result, 0); + UInt16ToTwoBytes(GStack.HostToNetwork(BitCode), Result, 2); + UInt16ToTwoBytes(GStack.HostToNetwork(QDCount), Result, 4); + UInt16ToTwoBytes(GStack.HostToNetwork(ANCount), Result, 6); + UInt16ToTwoBytes(GStack.HostToNetwork(NSCount), Result, 8); + UInt16ToTwoBytes(GStack.HostToNetwork(ARCount), Result, 10); +end; + +function TDNSHeader.GetAA: UInt16; +begin + Result := (FBitCode shr 10) and $0001; +end; + +{ +function TDNSHeader.GetAD: UInt16; +begin + Result := (FBitCode shr 5) and $0001; +end; + +function TDNSHeader.GetCD: UInt16; +begin + Result := (FBitCode shr 4) and $0001; +end; +} + +function TDNSHeader.GetOpCode: UInt16; +begin + Result := (FBitCode shr 11) and $000F; +end; + +function TDNSHeader.GetQr: UInt16; +begin + Result := (FBitCode shr 15) and $0001; +end; + +function TDNSHeader.GetRA: UInt16; +begin + Result := (FBitCode shr 7) and $0001; +end; + +function TDNSHeader.GetRCode: UInt16; +begin + Result := FBitCode and $000F; +end; + +function TDNSHeader.GetRD: UInt16; +begin + Result := (FBitCode shr 8) and $0001; +end; + +function TDNSHeader.GetTC: UInt16; +begin + Result := (FBitCode shr 9) and $0001; +end; + +function TDNSHeader.ParseQuery(Data: TIdBytes): integer; +begin + Result := -1; + if Length(Data) >= 12 then begin + try + ID := GStack.NetworkToHost(BytesToUInt16(Data, 0)); + BitCode := GStack.NetworkToHost(BytesToUInt16(Data, 2)); + QDCount := GStack.NetworkToHost(BytesToUInt16(Data, 4)); + ANCount := GStack.NetworkToHost(BytesToUInt16(Data, 6)); + NSCount := GStack.NetworkToHost(BytesToUInt16(Data, 8)); + ARCount := GStack.NetworkToHost(BytesToUInt16(Data, 10)); + Result := 0; + except + end; + end; +end; + +procedure TDNSHeader.SetAA(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FBFF; + end else begin + FBitCode := FBitCode or $0400; + end; +end; + +{ +procedure TDNSHeader.SetAD(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FFDF; + end else begin + FBitCode := FBitCode or $0020; + end; +end; +} + +procedure TDNSHeader.SetBitCode(const Value: UInt16); +begin + FBitCode := Value; +end; + +{ +procedure TDNSHeader.SetCD(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FFEF; + end else begin + FBitCode := FBitCode or $0010; + end; +end; +} + +procedure TDNSHeader.SetOpCode(const Value: UInt16); +begin + FBitCode := (FBitCode and $87FF) or ((Value and $000F) shl 11); +end; + +procedure TDNSHeader.SetQr(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $7FFF; + end else begin + FBitCode := FBitCode or $8000; + end; +end; + +procedure TDNSHeader.SetRA(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FF7F; + end else begin + FBitCode := FBitCode or $0080; + end; +end; + +procedure TDNSHeader.SetRCode(const Value: UInt16); +begin + FBitCode := (FBitCode and $FFF0) or (Value and $000F); +end; + +procedure TDNSHeader.SetRD(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FEFF; + end else begin + FBitCode := FBitCode or $0100; + end; +end; + +procedure TDNSHeader.SetTC(const Value: UInt16); +begin + if Value = 0 then begin + FBitCode := FBitCode and $FDFF; + end else begin + FBitCode := FBitCode or $0200; + end; +end; + + +{ TIdTextModeResourceRecord } + +function TIdTextModeResourceRecord.BinQueryRecord(AFullName: string): TIdBytes; +begin + // This was empty? Where did it go? + //todo; + // Explain by Dennies : No, here must be empty, it's only a + // virtual method, for child class to implement. + Result := nil; +end; + +procedure TIdTextModeResourceRecord.ClearAnswer; +begin + SetLength(FAnswer, 0); +end; + +constructor TIdTextModeResourceRecord.CreateInit(const ARRName: String; ATypeCode: Integer); +begin + inherited Create; + SetLength(FAnswer, 0); + FRRName := ARRName; + FTypeCode := ATypeCode; + FRRDatas := TStringList.Create; + TTL := 0; +end; + +destructor TIdTextModeResourceRecord.Destroy; +begin + FreeAndNil(FRRDatas); + inherited Destroy; +end; + +function TIdTextModeResourceRecord.FormatQName(const AFullName: string): string; +begin + Result := FormatQName(FRRName, AFullName); +end; + +function TIdTextModeResourceRecord.FormatQName(const AName, AFullName: string): string; +begin + if Copy(AName, Length(AName), 1) <> '.' then begin + Result := AName + '.' + AFullName; + end else begin + Result := AName; + end; +end; + +function TIdTextModeResourceRecord.FormatQNameFull(const AFullName: string): string; +var + LQName: string; +begin + LQName := FRRName + '.'; + if LQName <> AFullName then begin + LQName := FormatQName(AFullName); + end; + if LQName = AFullName then begin + Result := '@'; + end else begin + Result := LQName; + end; +end; + +function TIdTextModeResourceRecord.FormatRecord(const AFullName: String; const ARRData: TIdBytes): TIdBytes; +var + LDomain: TIdBytes; + LIdx: Integer; +begin + LDomain := DomainNameToDNSStr(FormatQName(AFullName)); + SetLength(Result, Length(LDomain)+(SizeOf(UInt16)*3)+SizeOf(UInt32)+Length(ARRData)); + LIdx := 0; + IdBytesCopyBytes(LDomain, Result, LIdx); + IdBytesCopyUInt16(GStack.HostToNetwork(UInt16(TypeCode)), Result, LIdx); + IdBytesCopyUInt16(GStack.HostToNetwork(UInt16(Class_IN)), Result, LIdx); + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(TTL)), Result, LIdx); + IdBytesCopyUInt16(GStack.HostToNetwork(UInt16(Length(ARRData))), Result, LIdx); + IdBytesCopyBytes(ARRData, Result, LIdx); +end; + +function TIdTextModeResourceRecord.GetValue(const AName: String): String; +begin + Result := RRDatas.Values[AName]; +end; + +procedure TIdTextModeResourceRecord.SetValue(const AName: String; const AValue: String); +begin + RRDatas.Values[AName] := AValue; +end; + +function TIdTextModeResourceRecord.ifAddFullName(AFullName, AGivenName: string): boolean; +var + LTailString, LBackString, LDestination : string; + LTS, LRR : integer; +begin + if AGivenName = '' then begin + LDestination := RRName; + end else begin + LDestination := AGivenName; + end; + + if TextEndsWith(LDestination, '.') then begin + Result := False; + end else begin + if TextEndsWith(AFullName, '.') then begin + LTailString := Copy(AFullName, 1, Length(AFullName) - 1); + end else begin + LTailString := AFullName; + end; + + LTS := Length(LTailString); + LRR := Length(LDestination); + + if LRR >= LTS then begin + LBackString := Copy(LDestination, LRR - LTS + 1 , LTS); + Result := not (LBackString = LTailString); + end else begin + Result := True; + end; + end; +end; + +function TIdTextModeResourceRecord.ItemCount: integer; +begin + Result := RRDatas.Count; +end; + +procedure TIdTextModeResourceRecord.SetRRDatas(const Value: TStrings); +begin + FRRDatas.Assign(Value); +end; + +procedure TIdTextModeResourceRecord.SetTTL(const Value: integer); +begin + FTTL := Value; + FTimeOut := DateTimeToStr(AddMSecToTime(Now, Value * 1000)); +end; + +function TIdTextModeResourceRecord.TextRecord(AFullName: string): string; +begin + Result := ''; +end; + +{ TIdTextModeRRs } + +constructor TIdTextModeRRs.Create; +begin + inherited Create; + FItemNames := TStringList.Create; +end; + +destructor TIdTextModeRRs.Destroy; +begin + FreeAndNil(FItemNames); + inherited Destroy; +end; + +{$IFNDEF HAS_GENERICS_TObjectList} +function TIdTextModeRRs.GetItem(Index: Integer): TIdTextModeResourceRecord; +begin + Result := TIdTextModeResourceRecord(inherited GetItem(Index)); +end; + +procedure TIdTextModeRRs.SetItem(Index: Integer; const Value: TIdTextModeResourceRecord); +begin + inherited SetItem(Index, Value); +end; +{$ENDIF} + +procedure TIdTextModeRRs.SetItemNames(const Value: TStrings); +begin + FItemNames.Assign(Value); +end; + +{ TIdRR_CName } + +function TIdRR_CName.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(CName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_CName.Create; +begin + inherited CreateInit('CName', TypeCode_CName); {do not localize} + CName := ''; +end; + +function TIdRR_CName.GetCName: String; +begin + Result := GetValue('CName'); {do not localize} +end; + +procedure TIdRR_CName.SetCName(const Value: String); +begin + SetValue('CName', Value); {do not localize} +end; + +function TIdRR_CName.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'CNAME' + Chr(9) + CName + EOL; {do not localize} +end; + +{ TIdRR_HINFO } + +function TIdRR_HINFO.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + if Length(FAnswer) = 0 then begin + RRData := NormalStrToDNSStr(CPU); + AppendBytes(RRData, NormalStrToDNSStr(OS)); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_HINFO.Create; +begin + inherited CreateInit('HINFO', TypeCode_HINFO); {do not localize} + CPU := ''; + OS := ''; +end; + +function TIdRR_HINFO.GetCPU: String; +begin + Result := GetValue('CPU'); {do not localize} +end; + +function TIdRR_HINFO.GetOS: String; +begin + Result := GetValue('OS'); {do not localize} +end; + +procedure TIdRR_HINFO.SetCPU(const Value: String); +begin + SetValue('CPU', Value); {do not localize} +end; + +procedure TIdRR_HINFO.SetOS(const Value: String); +begin + SetValue('OS', Value); {do not localize} +end; + +function TIdRR_HINFO.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'HINFO' + Chr(9) + + '"' + CPU + '" "' + OS + '"' + EOL; {do not localize} +end; + +{ TIdRR_MB } + +function TIdRR_MB.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(MADName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_MB.Create; +begin + inherited CreateInit('MB', TypeCode_MB); {do not localize} + MADName := ''; +end; + +function TIdRR_MB.GetMADName: String; +begin + Result := GetValue('MADNAME'); {do not localize} +end; + +procedure TIdRR_MB.SetMADName(const Value: String); +begin + SetValue('MADNAME', Value); {do not localize} +end; + +function TIdRR_MB.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'MB' + Chr(9) + MADName + EOL; {do not localize} +end; + +{ TIdRR_MG } + +function TIdRR_MG.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(MGMName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_MG.Create; +begin + inherited CreateInit('MG', TypeCode_MG); {do not localize} + MGMName := ''; +end; + +function TIdRR_MG.GetMGMName: String; +begin + Result := GetValue('MGMNAME'); {do not localize} +end; + +procedure TIdRR_MG.SetMGMName(const Value: String); +begin + SetValue('MGMNAME', Value); {do not localize} +end; + +function TIdRR_MG.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'MG' + Chr(9) + MGMName + EOL; {do not localize} +end; + +{ TIdRR_MINFO } + +function TIdRR_MINFO.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +{ +From: http://www.its.uq.edu.au/DMT/RFC/rfc1035.html#MINFO_RR +3.3.7. MINFO RDATA format (EXPERIMENTAL) + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + / RMAILBX / + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + / EMAILBX / + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +} +begin + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(Responsible_Mail); + AppendBytes(RRData, DomainNameToDNSStr(ErrorHandle_Mail)); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_MINFO.Create; +begin + inherited CreateInit('MINFO', TypeCode_MINFO); {do not localize} + Responsible_Mail := ''; + ErrorHandle_Mail := ''; +end; + +function TIdRR_MINFO.GetEMail: String; +begin + Result := GetValue('EMAILBX'); {do not localize} +end; + +function TIdRR_MINFO.GetRMail: String; +begin + Result := GetValue('RMAILBX'); {do not localize} +end; + +procedure TIdRR_MINFO.SetErrorHandle_Mail(const Value: String); +begin + SetValue('EMAILBX', Value); {do not localize} +end; + +procedure TIdRR_MINFO.SetResponsible_Mail(const Value: String); +begin + SetValue('RMAILBX', Value); {do not localize} +end; + +function TIdRR_MINFO.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'MINFO' + Chr(9) {do not localize} + + Responsible_Mail + ' ' + ErrorHandle_Mail + EOL; {do not localize} +end; + +{ TIdRR_MR } + +function TIdRR_MR.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(NewName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_MR.Create; +begin + inherited CreateInit('MR', TypeCode_MR); {do not localize} + NewName := ''; +end; + +function TIdRR_MR.GetNewName: String; +begin + Result := GetValue('NewName'); {do not localize} +end; + +procedure TIdRR_MR.SetNewName(const Value: String); +begin + SetValue('NewName', Value); {do not localize} +end; + +function TIdRR_MR.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'MR' + Chr(9) + NewName + EOL; {do not localize} +end; + +{ TIdRR_MX } + +function TIdRR_MX.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData, Tmp: TIdBytes; + Pref : UInt16; +begin + Tmp := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + Pref := IndyStrToInt(Preference); + RRData := ToBytes(GStack.HostToNetwork(Pref)); + Tmp := DomainNameToDNSStr(FormatQName(Exchange,AFullName)); + AppendBytes(RRData, Tmp); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_MX.Create; +begin + inherited CreateInit('MX', TypeCode_MX); {do not localize} + Exchange := ''; +end; + +function TIdRR_MX.GetExchang: String; +begin + Result := GetValue('EXCHANGE'); {do not localize} +end; + +function TIdRR_MX.GetPref: String; +begin + Result := GetValue('PREF'); {do not localize} +end; + +procedure TIdRR_MX.SetExchange(const Value: String); +begin + SetValue('EXCHANGE', Value); {do not localize} +end; + +procedure TIdRR_MX.SetPref(const Value: String); +begin + SetValue('PREF', Value); {do not localize} +end; + +function TIdRR_MX.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'MX' + Chr(9) {do not localize} + + Preference + ' ' + Exchange + EOL; {do not localize} +end; + +{ TIdRR_NS } + +function TIdRR_NS.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(NSDName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_NS.Create; +begin + inherited CreateInit('NS', TypeCode_NS); {do not localize} + NSDName := ''; +end; + +function TIdRR_NS.GetNS: String; +begin + Result := GetValue('NSDNAME'); {do not localize} +end; + +procedure TIdRR_NS.SetNS(const Value: String); +begin + SetValue('NSDNAME', Value); {do not localize} +end; + +function TIdRR_NS.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'NS' + Chr(9) + NSDName + EOL; {do not localize} +end; + +{ TIdRR_PTR } + +function TIdRR_PTR.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := DomainNameToDNSStr(PTRDName); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_PTR.Create; +begin + inherited CreateInit('PTR', TypeCode_PTR); {do not localize} + PTRDName := ''; +end; + +function TIdRR_PTR.GetPTRName: String; +begin + Result := GetValue('PTRDNAME'); {do not localize} +end; + +procedure TIdRR_PTR.SetPTRName(const Value: String); +begin + SetValue('PTRDNAME', Value); {do not localize} +end; + +function TIdRR_PTR.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'PTR' + Chr(9) + PTRDName + EOL; {do not localize} +end; + +{ TIdRR_SOA } + +function TIdRR_SOA.BinQueryRecord(AFullName: string): TIdBytes; +var + LMName, LRName, RRData: TIdBytes; + LIdx: Integer; +begin + // keep the compiler happy + LMName := nil; + LRName := nil; + RRData := nil; + + if Length(FAnswer) = 0 then begin + LMName := DomainNameToDNSStr(MName); + LRName := DomainNameToDNSStr(RName); + + SetLength(RRData, Length(LMName)+Length(LRName)+(SizeOf(UInt32)*5)); + + LIdx := 0; + IdBytesCopyBytes(LMName, RRData, LIdx); + IdBytesCopyBytes(LRName, RRData, LIdx); + + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(IndyStrToInt(Serial))), RRData, LIdx); + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(IndyStrToInt(Refresh))), RRData, LIdx); + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(IndyStrToInt(Retry))), RRData, LIdx); + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(IndyStrToInt(Expire))), RRData, LIdx); + IdBytesCopyUInt32(GStack.HostToNetwork(UInt32(IndyStrToInt(Minimum))), RRData, LIdx); + + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_SOA.Create; +begin + inherited CreateInit('SOA', TypeCode_SOA); {do not localize} + MName := ''; + RName := ''; + Serial := ''; + Refresh := ''; + Retry := ''; + Expire := ''; + Minimum := ''; +end; + +function TIdRR_SOA.GetExpire: String; +begin + Result := GetName('EXPIRE'); {do not localize} +end; + +function TIdRR_SOA.GetMin: String; +begin + Result := GetName('MINIMUM'); {do not localize} +end; + +function TIdRR_SOA.GetMName: String; +begin + Result := GetName('MNAME'); {do not localize} +end; + +function TIdRR_SOA.GetName(const CLabel: String): String; +begin + Result := GetValue(CLabel); +end; + +function TIdRR_SOA.GetRefresh: String; +begin + Result := GetName('REFRESH'); {do not localize} +end; + +function TIdRR_SOA.GetRetry: String; +begin + Result := GetName('RETRY'); {do not localize} +end; + +function TIdRR_SOA.GetRName: String; +begin + Result := GetName('RNAME'); {do not localize} +end; + +function TIdRR_SOA.GetSerial: String; +begin + Result := GetName('SERIAL'); {do not localize} +end; + +procedure TIdRR_SOA.SetExpire(const Value: String); +begin + SetName('EXPIRE', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetMin(const Value: String); +begin + SetName('MINIMUM', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetMName(const Value: String); +begin + SetName('MNAME', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetName(const CLabel: String; const Value: String); +begin + SetValue(CLabel, Value); +end; + +procedure TIdRR_SOA.SetRefresh(const Value: String); +begin + SetName('REFRESH', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetRetry(const Value: String); +begin + SetName('RETRY', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetRName(const Value: String); +begin + SetName('RNAME', Value); {do not localize} +end; + +procedure TIdRR_SOA.SetSerial(const Value: String); +begin + SetName('SERIAL', Value); {do not localize} +end; + +function TIdRR_SOA.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'SOA' + Chr(9) {do not localize} + + MName + ' ' + RName + ' ' + Serial + ' ' + Refresh + ' ' + Retry + ' ' {do not localize} + + Expire + ' ' + Minimum + EOL; {do not localize} +end; + +{ TIdRR_A } + +function TIdRR_A.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(Self.FAnswer) = 0 then begin + RRData := IPAddrToDNSStr(Address); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_A.Create; +begin + inherited CreateInit('A', TypeCode_A); {do not localize} + Address := ''; +end; + +function TIdRR_A.GetA: String; +begin + Result := GetValue('A'); {do not localize} +end; + +procedure TIdRR_A.SetA(const Value: String); +begin + SetValue('A', Value); {do not localize} +end; + +function TIdRR_A.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'A' + Chr(9) + Address + EOL; {do not localize} +end; + +{ TIdRR_AAAA } + +function TIdRR_AAAA.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + RRData := IPv6AAAAToDNSStr(Address); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_AAAA.Create; +begin + inherited CreateInit('AAAA', TypeCode_AAAA); {do not localize} + Address := ''; +end; + +function TIdRR_AAAA.GetA: String; +begin + Result := GetValue('AAAA'); {do not localize} +end; + +procedure TIdRR_AAAA.SetA(const Value: String); +begin + SetValue('AAAA', Value); {do not localize} +end; + +function TIdRR_AAAA.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'AAAA' + Chr(9) + Address + EOL; {do not localize} +end; + +{ TIdRR_TXT } + +function TIdRR_TXT.BinQueryRecord(AFullName: string): TIdBytes; +var + RRData: TIdBytes; +begin + RRData := nil; // keep the compiler happy + if Length(FAnswer) = 0 then begin + //Fix here, make the RRData being DNSStr. + //Fixed in 2005 Jan 25. + RRData := NormalStrToDNSStr(TXT); + FAnswer := FormatRecord(AFullName, RRData); + end; + Result := ToBytes(FAnswer, Length(FAnswer)); +end; + +constructor TIdRR_TXT.Create; +begin + inherited CreateInit('TXT', TypeCode_TXT); {do not localize} + TXT := ''; +end; + +function TIdRR_TXT.GetTXT: String; +begin + Result := GetValue('TXT'); {do not localize} +end; + +procedure TIdRR_TXT.SetTXT(const Value: String); +begin + SetValue('TXT', Value); {do not localize} +end; + +function TIdRR_TXT.TextRecord(AFullName: string): string; +begin + Result := FormatQNameFull(AFullName) + Chr(9) + 'IN' + Chr(9) + 'TXT' + Chr(9) {do not localize} + + '"' + TXT + '"' + EOL; {do not localize} +end; + +{ TIdRR_WKS } + +constructor TIdRR_WKS.Create; +begin + inherited CreateInit('WKS', TypeCode_WKS); {do not localize} +end; + +{ TIdRR_Error } + +constructor TIdRR_Error.Create; +begin + inherited CreateInit('', TypeCode_Error); {do not localize} +end; + +function ReplaceSpecString(Source, Target, NewString : string; ReplaceAll : boolean = True) : string; +var + FixingString, MiddleString, FixedString : string; +begin + if Target = NewString then begin + Result := Source; + end else begin + FixingString := Source; + MiddleString := ''; {do not localize} + FixedString := ''; {do not localize} + + if Pos(Target, Source) > 0 then begin + repeat + MiddleString := Fetch(FixingString, Target); + FixedString := FixedString + MiddleString + NewString; + until (Pos(Target, FixingString) = 0) or (not ReplaceAll); + Result := FixedString + FixingString; + end else begin + Result := Source; + end; + end; +end; + +function IsBig5(ch1, ch2:char) : boolean; +begin + // RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler + // may change characters >= #128 from their Ansi codepage value to their true + // Unicode codepoint value, depending on the codepage used for the source code. + // For instance, #128 may become #$20AC... + + if (not (((ch1 >= Char(161)) and (ch1 <= Char(254))) or + ((ch1 >= Char(142)) and (ch1 <= Char(160))) or + ((ch1 >= Char(129)) and (ch1 <= Char(141)))) ) or + (not (((ch2 >= #64) and (ch2 <= #126)) or + ((ch2 >= Char(161)) and (ch2 <= Char(254)))) ) then + begin + Result := False; + end else begin + Result := True; + end; +end; + +end. diff --git a/indy/Protocols/IdDNSResolver.pas b/indy/Protocols/IdDNSResolver.pas new file mode 100644 index 0000000..1fd2c69 --- /dev/null +++ b/indy/Protocols/IdDNSResolver.pas @@ -0,0 +1,1721 @@ +{ + $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$ + + + 4/19/2005 BTaylor + + Added support for SVR and NAPTR records. (Used for SIP/VOIP) (parts by Frank Shearar) + + Added TResultRecord.Section, .FilterBySection , .FilterByClass + DNS lookups can now be generated exactly the same as NsLookup. + + Improved .Assign support on many objects. QueryResult object+items can now be properly cloned. + + TIdDNSResolver.FDNSHeader was a public field, now it's a public readonly property, TIdDNSResolver.DNSHeader + + fixed TMXRecord.Parse bug, .Preference will now contain correct value. + + fixed TTextRecord.Parse issue. DomainKeys (yahoo's anti-spam method) can now be used. + + Minor cleanups/spelling errors fixed. + + + Rev 1.26 3/21/2005 10:36:20 PM VVassiliev + NextDNSLabel fix + TTextRecord.Parse fix + ClearInternalQuery before resolving + + + Rev 1.25 2/9/05 2:10:34 AM RLebeau + Removed compiler hint + + + Rev 1.24 2/8/05 6:17:14 PM RLebeau + Updated CreateQuery() to use Fetch() and AppendString() instead of Pos(), + ToBytes(), and AppendBytes() + + + Rev 1.23 10/26/2004 9:06:30 PM JPMugaas + Updated references. + + + Rev 1.22 2004.10.25 10:18:38 PM czhower + Removed unused var. + + + Rev 1.21 25/10/2004 15:55:28 ANeillans + Bug fix: + http://apps.atozedsoftware.com/cgi-bin/BBGIndy/BugBeGoneISAPI.dll/?item=122 + + Checked in for Dennies Chang + + + Rev 1.20 2004/7/19 U 09:40:52 DChang + 1. fix the TIdResolver.ParseAnswers, add 2 parameters for the function to + check if QueryResult should be clear or not, TIdResolver.FillResult is + modified at the same time. + + Fix AXFR procedure, fully support BIND 8 AXFR procedures. + + 2. Replace the original type indicator in TQueryResult.Add. + It can understand AAAA type correctly. + + 3. Add qtIXFR type for TIdDNSResover, add 2 parameters for + TIdDNSResolver.Resolver, add one parameter for TIdDNSResolver.CreateHeader. + + 4. Support query type CHAOS, but only for checking version.bind. (Check DNS + server version.) + + + Rev 1.19 7/12/2004 9:42:26 PM DSiders + Removed TODO for Address property. + + + Rev 1.18 7/12/2004 9:24:04 PM DSiders + Added TODOs for property name inconsistencies. + + + Rev 1.17 7/8/04 11:48:28 PM RLebeau + Tweaked TQueryResult.NextDNSLabel() + + + Rev 1.16 2004.05.20 1:39:30 PM czhower + Last of the IdStream updates + + + Rev 1.15 2004.04.08 3:57:28 PM czhower + Removal of bytes from buffer. + + + Rev 1.14 2004.03.01 9:37:04 PM czhower + Fixed name conflicts for .net + + + Rev 1.13 2/11/2004 5:47:26 AM JPMugaas + Can now assign a port for the DNS host as well as IPVersion. + + In addition, you can now use socks with TCP zone transfers. + + + Rev 1.12 2/11/2004 5:21:16 AM JPMugaas + Vladimir Vassiliev changes for removal of byte flipping. Network conversion + order conversion functions are used instead. + IPv6 addresses are returned in the standard form. + In WKS records, Address was changed to IPAddress to be consistant with other + record types. Address can also imply a hostname. + + + Rev 1.11 2/9/2004 11:27:36 AM JPMugaas + Some functions weren't working as expected. Renamed them to describe them + better. + + + Rev 1.10 2004.02.03 5:45:58 PM czhower + Name changes + + + Rev 1.9 11/13/2003 5:46:54 PM VVassiliev + DotNet + AAAA record fix + Add PTR for IPV6 + + + Rev 1.8 10/25/2003 06:51:54 AM JPMugaas + Updated for new API changes and tried to restore some functionality. + + + Rev 1.7 10/19/2003 11:57:32 AM DSiders + Added localization comments. + + + Rev 1.6 2003.10.12 3:50:38 PM czhower + Compile todos + + + Rev 1.5 2003/4/30 U 12:39:54 DChang + fix the TIdResolver.ParseAnswers, add 2 parameters for the function + to check if QueryResult should be clear or not, TIdResolver.FillResult + is modified at the same time. + fix AXFR procedure, fully support BIND 8 AXFR procedures. + + + Rev 1.4 4/28/2003 02:30:50 PM JPMugaas + reverted back to the old one as the new one checked will not compile, has + problametic dependancies on Contrs and Dialogs (both not permitted). + + + Rev 1.2 4/28/2003 07:00:10 AM JPMugaas + Should now compile. + + + Rev 1.0 11/14/2002 02:18:34 PM JPMugaas + Rev 1.3 04/26/2003 02:30:10 PM DenniesChang + + + IdDNSResolver. + + Started: sometime. + Finished: 2003/04/26 + + IdDNSResolver has integrate UDP and TCP tunnel to resolve then types defined in RFC 1035, + and AAAA, which is defined in RFC 1884, 1886. + + AXFR command, which is defined in RFC 1995, is also implemented in 2003/04/26 + + The resolver also does not support Chaos RR. Only IN RR are supported as of this time. + Part of code from Ray Malone + + +// Dennies Chang : Combine TIdDNSSyncResolver and TIdDNSCommResolver as TIdDNSResolver. +// 2003/04/26. +// Dennies Chang : Rename TIdDNSResolver as TIdDNSCommonResolver. 2003/04/23 +// Dennies Chang : Add TIdDNSSyncClient to implement AXFR command. 2003/04/15 +// Dennies Chang : Add atAAAA and TAAAARecord (2002 Oct.) +// Dennies Chang : Add TDNSHeader for IDHeader to maintain DNS Header, but not complete yet. +// SG 28/1/02: Changed the DNSStrToDomain function according to original Author of the old comp: Ray Malone +SG 10/07/01 Added support for qrStar query +VV 12/09/01 Added construction of reverse query (PTR) +DS 12/31/01 Corrected ReponsiblePerson spelling +VV 01/02/03 TQueryResult.DNSStrToDomain fix + + TODO : Add structure of IDHEADER IN FIGURE } + +unit IdDNSResolver; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, + IdBuffer, + IdComponent, + IdGlobal, IdExceptionCore, + IdNetworkCalculator, + IdGlobalProtocols, + IdDNSCommon, + IdTCPClient, + IdTCPConnection, + IdUDPClient; + +(*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *) +(*$HPPEMIT '#if !defined(UNICODE)' *) +(*$HPPEMIT '#pragma alias "@Iddnsresolver@TIdDNSResolver@SetPortA$qqrxus"="@Iddnsresolver@TIdDNSResolver@SetPort$qqrxus"' *) +(*$HPPEMIT '#else' *) +(*$HPPEMIT '#pragma alias "@Iddnsresolver@TIdDNSResolver@SetPortW$qqrxus"="@Iddnsresolver@TIdDNSResolver@SetPort$qqrxus"' *) +(*$HPPEMIT '#endif' *) +(*$HPPEMIT '#endif' *) + +type + { TODO : Solve problem with obsolete records } + TQueryRecordTypes = ( + qtA, qtNS, qtMD, qtMF, + qtName, qtSOA, qtMB, qtMG, + qtMR, qtNull, qtWKS, qtPTR, + qtHINFO, qtMINFO, qtMX, qtTXT, + //qtRP, qtAfsdb, qtX25, qtISDN, + qtRT, qtNSAP, qtNSAP_PTR, qtSIG, + //qtKEY, qtPX, qtQPOS, + qtAAAA, + //qtLOC, qtNXT, qtR31, qtR32, + qtService, + //qtR34, + qtNAPTR, + //qtKX, + qtCERT, qtV6Addr, qtDName, qtR40, + qtOptional, qtIXFR, qtAXFR, qtSTAR); + + + {Marked by Dennies Chang at 2004/7/14. + {TXFRTypes = (xtAXFR, xtIXFR); + } +const + + // Lookup table for query record values. + QueryRecordCount = 30; + QueryRecordValues: array [0..QueryRecordCount] of UInt16 = ( + 1,2,3,4, + 5,6,7,8, + 9,10,11,12, + 13,14,15,16, + //17,18,19,20, + 21,22,23,24, + //25,26,27, + 28, + //29,30,31,32, + 33, + //34, + 35, + //36, + 37,38,39,40, + 41, 251, 252, 255); + QueryRecordTypes: Array [0..QueryRecordCount] of TQueryRecordTypes = ( + qtA, qtNS, qtMD, qtMF, + qtName, qtSOA, qtMB, qtMG, + qtMR, qtNull, qtWKS, qtPTR, + qtHINFO, qtMINFO, qtMX, qtTXT, + //qtRP, qtAfsdb, qtX25, qtISDN, + qtRT, qtNSAP, qtNSAP_PTR, qtSIG, + //qtKEY, qtPX, qtQPOS, + qtAAAA, + //qtLOC, qtNXT, qtR31, qtR32, + qtService, + //qtR34, + qtNAPTR, + //qtKX, + qtCERT, qtV6Addr, qtDName, qtR40, + qtOptional, qtIXFR, qtAXFR, qtSTAR); + +type + TQueryType = set of TQueryRecordTypes; + + TResultSection = (rsAnswer, rsNameServer, rsAdditional); + TResultSections = set of TResultSection; + + TResultRecord = class(TCollectionItem) // Rename to REsourceRecord + protected + FRecType: TQueryRecordTypes; + FRecClass: UInt16; + FName: string; + FTTL: UInt32; + FRDataLength: Integer; + FRData: TIdBytes; + FSection: TResultSection; + public + procedure Assign(Source: TPersistent); override; + // Parse the data (descendants only) + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); virtual; + { TODO : This needs to change (to what? why?) } + property RecType: TQueryRecordTypes read FRecType; + property RecClass: UInt16 read FRecClass; + property Name: string read FName; + property TTL: UInt32 read FTTL; + property RDataLength: Integer read FRDataLength; + property RData: TIdBytes read FRData; + property Section: TResultSection read FSection; + end; + + TResultRecordClass = class of TResultRecord; + + TRDATARecord = class(TResultRecord) + protected + FIPAddress: String; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + property IPAddress: string read FIPAddress; + end; + + TARecord = class(TRDATARecord) + end; + + TAAAARecord = class (TResultRecord) + protected + FAddress: string; + public + //TODO: implement AssignTo instead of Assign. (why?) + procedure Assign(Source: TPersistent); override; + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + // + property Address : string read FAddress; + end; + + TWKSRecord = Class(TResultRecord) + protected + FByteCount: integer; + FData: TIdBytes; + FIPAddress: String; + FProtocol: UInt16; + // + function GetABit(AIndex: Integer): UInt8; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + // + property IPAddress: String read FIPAddress; + property Protocol: UInt16 read FProtocol; + property BitMap[index: integer]: UInt8 read GetABit; + property ByteCount: integer read FByteCount; + end; + + TMXRecord = class(TResultRecord) + protected + FExchangeServer: string; + FPreference: UInt16; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + + property ExchangeServer: string read FExchangeServer; + property Preference: UInt16 read FPreference; + end; + + TTextRecord = class(TResultRecord) + protected + FText: TStrings; + public + constructor Create(Collection: TCollection); override; + destructor Destroy; override; + procedure Assign(Source: TPersistent); override; + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + Property Text: TStrings read FText; + end; + + TErrorRecord = class(TResultRecord) + end; + + THINFORecord = Class(TTextRecord) + protected + FCPU: String; + FOS: String; + public + procedure Assign(Source: TPersistent); override; + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + property CPU: String read FCPU; + property OS: String read FOS; + end; + + TMINFORecord = Class(TResultRecord) + protected + FResponsiblePerson: String; + FErrorMailbox: String; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + property ResponsiblePersonMailbox: String read FResponsiblePerson; + property ErrorMailbox: String read FErrorMailbox; + end; + + TSOARecord = class(TResultRecord) + protected + FSerial: UInt32; + FMinimumTTL: UInt32; + FRefresh: UInt32; + FRetry: UInt32; + FMNAME: string; + FRNAME: string; + FExpire: UInt32; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + + property Primary: string read FMNAME; + property ResponsiblePerson: string read FRNAME; + property Serial: UInt32 read FSerial; + property Refresh: UInt32 read FRefresh; + property Retry: UInt32 read FRetry; + property Expire: UInt32 read FExpire; + + property MinimumTTL: UInt32 read FMinimumTTL; + end; + + TNAMERecord = class(TResultRecord) + protected + FHostName: string; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + property HostName: string read FHostName; + end; + + TNSRecord = class(TNAMERecord) + end; + + TCNRecord = class(TNAMERecord) + end; + + TSRVRecord = class(TResultRecord) + private + FService: string; + FProtocol: string; + FPriority: integer; + FWeight: integer; + FPort: integer; + FTarget: string; + FOriginalName: string; + function IsValidIdent(const aStr:string):Boolean; + function CleanIdent(const aStr:string):string; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + property OriginalName:string read FOriginalName; + property Service: string read FService; + property Protocol: string read FProtocol; + property Priority: integer read FPriority; + property Weight: integer read FWeight; + property Port: integer read FPort; + property Target: string read FTarget; + end; + + TNAPTRRecord = class(TResultRecord) + private + FOrder: integer; + FPreference: integer; + FFlags: string; + FService: string; + FRegExp: string; + FReplacement: string; + public + procedure Parse(CompleteMessage: TIdBytes; APos: Integer); override; + procedure Assign(Source: TPersistent); override; + + property Order:integer read fOrder; + property Preference:integer read fPreference; + property Flags:string read fFlags; + property Service:string read fService; + property RegExp:string read fRegExp; + property Replacement:string read fReplacement; + end; + + TQueryResult = class(TCollection) + protected + FDomainName: String; + FQueryClass: UInt16; + FQueryType: UInt16; + FQueryPointerList: TStringList; + procedure SetItem(Index: Integer; Value: TResultRecord); + function GetItem(Index: Integer): TResultRecord; + public + constructor Create; + destructor Destroy; override; + procedure Assign(Source: TPersistent); override; + function Add(Answer: TIdBytes; var APos: Integer): TResultRecord; + procedure Clear; reintroduce; + procedure FilterBySection(const AKeep: TResultSections=[rsAnswer]); + procedure FilterByClass(const AKeep: TResultRecordClass); + + Property QueryClass: UInt16 read FQueryClass; + Property QueryType: UInt16 read FQueryType; + Property DomainName: String read FDomainName; + + property Items[Index: Integer]: TResultRecord read GetItem write SetItem; default; + end; + + TPTRRecord = Class(TNAMERecord) + end; + + //TIdTCPConnection looks odd for something that's supposed to be UDP. + //However, DNS uses TCP for zone-transfers. + TIdDNSResolver = class(TIdTCPConnection) + protected + FAllowRecursiveQueries: boolean; + FInternalQuery: TIdBytes; + FQuestionLength: Integer; + FHost: string; + FIPVersion: TIdIPVersion; + FPort: TIdPort; + FQueryResult: TQueryResult; + FQueryType: TQueryType; + FWaitingTime: integer; + FPlainTextResult: TIdBytes; + FDNSHeader : TDNSHeader; + + procedure SetInternalQuery(const Value: TIdBytes); + procedure SetPlainTextResult(const Value: TIdBytes); + procedure InitComponent; override; + + procedure SetIPVersion(const AValue: TIdIPVersion); virtual; + procedure SetPort(const AValue: TIdPort); virtual; + public + property DNSHeader:TDNSHeader read FDNSHeader; + procedure ClearInternalQuery; + destructor Destroy; override; + procedure ParseAnswers(DNSHeader: TDNSHeader; Answer: TIdBytes; ResetResult: Boolean = True); + procedure CreateQuery(ADomain: string; SOARR : TIdRR_SOA; QueryClass:integer = Class_IN); + procedure FillResult(AResult: TIdBytes; checkID : boolean = true; + ResetResult : boolean = true); + procedure FillResultWithOutCheckId(AResult: TIdBytes); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use FillResult() with checkID=False'{$ENDIF};{$ENDIF} + procedure Resolve(ADomain: string; SOARR : TIdRR_SOA = nil; QClass: integer = Class_IN); + property QueryResult: TQueryResult read FQueryResult; + property InternalQuery: TIdBytes read FInternalQuery write SetInternalQuery; + property PlainTextResult: TIdBytes read FPlainTextResult write SetPlainTextResult; + published + property QueryType : TQueryType read FQueryType write FQueryType; + // TODO: rename to ReadTimeout? + // Dennies's comment : it's ok, that's just a name. + property WaitingTime : integer read FWaitingTime write FWaitingTime; + property AllowRecursiveQueries : boolean read FAllowRecursiveQueries write FAllowRecursiveQueries; + property Host : string read FHost write FHost; + property Port : TIdPort read FPort write SetPort default IdPORT_DOMAIN; + property IPVersion: TIdIPVersion read FIPVersion write SetIPVersion; + end; + +function DNSStrToDomain(const DNSStr: TIdBytes; var VPos: Integer): string; +function NextDNSLabel(const DNSStr: TIdBytes; var VPos: Integer): string; + +implementation + +uses + IdBaseComponent, + IdResourceStringsProtocols, + IdStack, SysUtils; + +// SG 28/1/02: Changed that function according to original Author of the old comp: Ray Malone +function DNSStrToDomain(const DNSStr: TIdBytes; var VPos: Integer): string; +var + LabelStr : String; + Len : Integer; + SavedIdx : Integer; + B : Byte; + PackSize: Integer; +begin + Result := ''; {Do not Localize} + PackSize := Length(DNSStr); + SavedIdx := -1; + + while VPos < PackSize do // name field ends with nul byte + begin + Len := DNSStr[VPos]; + + // RLebeau 5/4/2009: sometimes the first entry of a domain's record is + // not defined, so account for that here at the top of the loop instead + // of at the bottom, otherwise a Range Check error can occur when + // trying to access the non-existant data... + if Len = 0 then begin + Break; + end; + + while (Len and $C0) = $C0 do // {!!0.01} added loop for pointer + begin // that points to a pointer. Removed >63 hack. Am I really that stupid? + if SavedIdx < 0 then begin + SavedIdx := Succ(VPos); // it is important to return to original index spot + end; + // when we go down more than 1 level. + B := Len and $3F; // strip first two bits ($C) from first byte of offset pos + VPos := GStack.NetworkToHost(TwoByteToUInt16(B, DNSStr[VPos + 1]));// + 1; // add one to index for delphi string index //VV + Len := DNSStr[VPos]; // if len is another $Cx we will (while) loop again + end; + + Assert(VPos < PackSize, GetErrorStr(2, 2)); // loop screwed up. This very very unlikely now could be removed. + + LabelStr := BytesToString(DNSStr, VPos+1, Len); + Inc(VPos, 1+Len); + + if Pred(VPos) > PackSize then begin // len byte was corrupted puting us past end of packet + raise EIdDnsResolverError.Create(GetErrorStr(2, 3)); + end; + + Result := Result + LabelStr + '.'; // concat and add period. {Do not Localize} + end; + + if TextEndsWith(Result, '.') then begin // remove final period {Do not Localize} + SetLength(Result, Length(Result) - 1); + end; + + if SavedIdx >= 0 then begin + VPos := SavedIdx; // restore original Idx +1 + end; + + Inc(VPos); // set to first char of next item in the resource +end; + +function NextDNSLabel(const DNSStr: TIdBytes; var VPos: Integer): string; +var + LabelLength: Byte; +begin + if Length(DNSStr) > VPos then begin + LabelLength := DNSStr[VPos]; + Inc(VPos); + //VV Shouldn't be pointers in Text messages + if LabelLength > 0 then begin + Result := BytesToString(DNSStr, VPos, LabelLength); + Inc(VPos, LabelLength); + Exit; + end; + end; + Result := ''; {Do not Localize} +end; + +{ TARecord } + +procedure TRDATARecord.Assign(Source: TPersistent); +begin + inherited Assign(Source); + if Source is TRDATARecord then begin + FIPAddress := TRDATARecord(Source).IPAddress; + end; +end; + +procedure TRDATARecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + if Length(RData) > 0 then begin + FIPAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(OrdFourByteToUInt32(RData[0], RData[1], RData[2], RData[3]))); + end; +end; + +{ TMXRecord } + +procedure TMXRecord.Assign(Source: TPersistent); +var + LSource: TMXRecord; +begin + inherited Assign(Source); + if Source is TMXRecord then + begin + LSource := TMXRecord(Source); + FExchangeServer := LSource.ExchangeServer; + FPreference := LSource.Preference; + end; +end; + +{ TCNAMERecord } + +procedure TNAMERecord.Assign(Source: TPersistent); +begin + inherited Assign(Source); + if Source is TNAMERecord then begin + FHostName := TNAMERecord(Source).HostName; + end; +end; + +{ TQueryResult } + +function TQueryResult.Add(Answer: TIdBytes; var APos: Integer): TResultRecord; +var + RRName: String; + RR_type, RR_Class: UInt16; + RR_TTL: UInt32; + RD_Length: UInt16; + RData: TIdBytes; +begin + // extract the RR data + RRName := DNSStrToDomain(Answer, APos); + RR_Type := GStack.NetworkToHost( TwoByteToUInt16(Answer[APos], Answer[APos + 1])); + RR_Class := GStack.NetworkToHost(TwoByteToUInt16(Answer[APos + 2], Answer[APos + 3])); + RR_TTL := GStack.NetworkToHost(OrdFourByteToUInt32(Answer[APos + 4], Answer[APos + 5], Answer[APos + 6], Answer[APos + 7])); + RD_Length := GStack.NetworkToHost(TwoByteToUInt16(Answer[APos + 8], Answer[APos + 9])); + RData := Copy(Answer, APos + 10, RD_Length); + // remove what we have read from the buffer + // Read the record type + // Dennies Chang had modified this part to indicate type by RR_type + // because RR_type is integer, we can use TypeCode which is defined + // in IdDNSCommon to select all record type. + case RR_Type of + TypeCode_A ://qtA: + begin + Result := TARecord.Create(Self); + end; + TypeCode_NS : //qtNS: + begin + Result := TNSRecord.Create(Self); + end; + TypeCode_MX ://qtMX: + begin + Result := TMXRecord.Create(Self); + end; + TypeCode_CName : // qtName: + begin + Result := TNAMERecord.Create(Self); + end; + TypeCode_SOA : //qtSOA: + begin + Result := TSOARecord.Create(Self); + end; + TypeCode_HINFO : //qtHINFO: + begin + Result := THINFORecord.Create(Self); + end; + TypeCode_TXT ://qtTXT: + begin + Result := TTextRecord.Create(Self); + end; + TypeCode_WKS ://qtWKS: + begin + Result := TWKSRecord.Create(Self); + end; + TypeCode_PTR :// qtPTR: + begin + Result := TPTRRecord.Create(Self); + end; + TypeCode_MINFO ://qtMINFO: + begin + Result := TMINFORecord.Create(Self); + end; + TypeCode_AAAA : //qtAAAA: + begin + Result := TAAAARecord.Create(Self); + end; + TypeCode_Service : //qtService + begin + Result := TSRVRecord.Create(Self); + end; + TypeCode_NAPTR : //qtNAPTR + begin + Result := TNAPTRRecord.Create(Self); + end; + else begin + // Unsupported query type, return generic record + Result := TResultRecord.Create(Self); + end; + end; // case + // Set the "general purprose" options + if Assigned(Result) then + begin + //if RR_Type <= High(QueryRecordTypes) then + // modified in 2004 7/15. + case RR_Type of + TypeCode_A: Result.FRecType := qtA; + TypeCode_NS: Result.FRecType := qtNS; + TypeCode_MD: Result.FRecType := qtMD; + TypeCode_MF: Result.FRecType := qtMF; + TypeCode_CName: Result.FRecType := qtName; + TypeCode_SOA: Result.FRecType := qtSOA; + TypeCode_MB: Result.FRecType := qtMB; + TypeCode_MG: Result.FRecType := qtMG; + TypeCode_MR: Result.FRecType := qtMR; + TypeCode_NULL: Result.FRecType := qtNull; + TypeCode_WKS: Result.FRecType := qtWKS; + TypeCode_PTR: Result.FRecType := qtPTR; + TypeCode_HINFO: Result.FRecType := qtHINFO; + TypeCode_MINFO: Result.FRecType := qtMINFO; + TypeCode_MX: Result.FRecType := qtMX; + TypeCode_TXT: Result.FRecType := qtTXT; + //TypeCode_NSAP: Result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1]; + //TypeCode_NSAP_PTR: Result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1]; + TypeCode_AAAA: Result.FRecType := qtAAAA; + //TypeCode_LOC: Result.FRecType := QueryRecordTypes[Ord(RR_Type) - 1]; + TypeCode_Service:Result.FRecType := qtService; + TypeCode_NAPTR: Result.FRecType := qtNAPTR; + TypeCode_AXFR: Result.FRecType := qtAXFR; + //TypeCode_STAR: Result.FRecType := qtSTAR; + end; + + result.FRecClass := RR_Class; + result.FName := RRName; + result.FTTL := RR_TTL; + Result.FRData := Copy(RData, 0{1}, RD_Length); + Result.FRDataLength := RD_Length; + // Parse the result + // Since the DNS message can be compressed, we need to have the whole message to parse it, in case + // we encounter a pointer + //Result.Parse(Copy(Answer, 0{1}, APos + 9 + RD_Length), APos + 10); + Result.Parse(Answer, APos + 10); + end; + // Set the new position + inc(APos, RD_Length + 10); +end; + +constructor TQueryResult.Create; +begin + inherited Create(TResultRecord); + FQueryPointerList := TStringList.Create; +end; + +destructor TQueryResult.Destroy; +begin + FreeAndNil(FQueryPointerList); + inherited Destroy; +end; + +function TQueryResult.GetItem(Index: Integer): TResultRecord; +begin + Result := TResultRecord(inherited GetItem(Index)); +end; + +procedure TQueryResult.SetItem(Index: Integer; Value: TResultRecord); +begin + inherited SetItem(Index, Value); +end; + +{ TResultRecord } + +procedure TResultRecord.Assign(Source: TPersistent); +var + LSource: TResultRecord; +begin + if Source is TResultRecord then + begin + LSource := TResultRecord(Source); + FRecType := LSource.RecType; + FRecClass := LSource.RecClass; + FName := LSource.Name; + FTTL := LSource.TTL; + FRDataLength := LSource.RDataLength; + FRData := Copy(LSource.RData, 0, Length(LSource.RData)); + FSection := LSource.Section; + end else begin + inherited Assign(Source); + end; +end; + +procedure TResultRecord.Parse; +begin +end; + +{ TNAMERecord } + +procedure TNAMERecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + FHostName := DNSStrToDomain(CompleteMessage, APos); +end; + +{ TQueryResult } + +procedure TQueryResult.Clear; +begin + inherited Clear; + FQueryPointerList.Clear; +end; + +procedure TQueryResult.Assign(Source: TPersistent); +//TCollection.Assign doesn't create correct Item class. +var + i: Integer; + LRec: TResultRecord; + LNew: TResultRecord; +begin + if Source is TQueryResult then + begin + BeginUpdate; + try + Clear; + for i := 0 to TQueryResult(Source).Count-1 do + begin + LRec := TQueryResult(Source).Items[i]; + LNew := TResultRecordClass(LRec.ClassType).Create(Self); + try + LNew.Assign(LRec); + except + FreeAndNil(LNew); + raise; + end; + end; + finally + EndUpdate; + end; + end else begin + inherited Assign(Source); + end; +end; + +{ TMXRecord } + +procedure TMXRecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + FPreference := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos + 1])); + Inc(APos, 2); + FExchangeServer := DNSStrToDomain(CompleteMessage, APos); +end; + +{ TTextRecord } + +procedure TTextRecord.Assign(Source: TPersistent); +begin + inherited Assign(Source); + if Source is TTextRecord then begin + FText.Assign(TTextRecord(Source).Text); + end; +end; + +constructor TTextRecord.Create(Collection: TCollection); +begin + inherited Create(Collection); + FText := TStringList.Create; +end; + +destructor TTextRecord.Destroy; +begin + FreeAndNil(FText); + inherited Destroy; +end; + +//the support for long text values is required for DomainKeys, +//which has an encoded public key +procedure TTextRecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +var + LStart: Integer; + Buffer: string; +begin + FText.Clear; + + LStart := APos; + while APos < (LStart+RDataLength) do + begin + Buffer := NextDNSLabel(CompleteMessage, APos); + if Buffer <> '' then begin {Do not Localize} + FText.Add(Buffer); + end; + end; + + inherited Parse(CompleteMessage, APos); +end; + +{ TSOARecord } + +procedure TSOARecord.Assign(Source: TPersistent); +var + LSource: TSOARecord; +begin + inherited Assign(Source); + if Source is TSOARecord then begin + LSource := TSOARecord(Source); + FSerial := LSource.Serial; + FMinimumTTL := LSource.MinimumTTL; + FRefresh := LSource.Refresh; + FRetry := LSource.Retry; + FMNAME := LSource.FMNAME; + FRNAME := LSource.FRNAME; + FExpire := LSource.Expire; + end; +end; + +procedure TSOARecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + + FMNAME := DNSStrToDomain(CompleteMessage, APos); + FRNAME := DNSStrToDomain(CompleteMessage, APos); + + FSerial := GStack.NetworkToHost(OrdFourByteToUInt32(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3])); + Inc(APos, 4); + + FRefresh := GStack.NetworkToHost( OrdFourByteToUInt32(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3])); + Inc(APos, 4); + + FRetry := GStack.NetworkToHost( OrdFourByteToUInt32(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3])); + Inc(APos, 4); + + FExpire := GStack.NetworkToHost( OrdFourByteToUInt32(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3])); + Inc(APos, 4); + + FMinimumTTL := GStack.NetworkToHost( OrdFourByteToUInt32(CompleteMessage[APos], CompleteMessage[APos + 1], CompleteMessage[APos + 2], CompleteMessage[APos + 3])); +end; + +{ TWKSRecord } + +procedure TWKSRecord.Assign(Source: TPersistent); +var + LSource: TWKSRecord; +begin + inherited Assign(Source); + if Source is TWKSRecord then begin + LSource := TWKSRecord(Source); + FIPAddress := LSource.IPAddress; + FProtocol := LSource.Protocol; + FByteCount := LSource.ByteCount; + FData := Copy(LSource.FData, 0, Length(LSource.FData)); + end; +end; + +function TWKSRecord.GetABit(AIndex: Integer): UInt8; +begin + Result := FData[AIndex]; +end; + +procedure TWKSRecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + FIPAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(OrdFourByteToUInt32(RData[0], RData[1], RData[2], RData[3]))); + FProtocol := UInt16(RData[4]); + FData := ToBytes(RData, Length(RData)-5, 5); +end; + +{ TMINFORecord } + +procedure TMINFORecord.Assign(Source: TPersistent); +var + LSource: TMINFORecord; +begin + inherited Assign(Source); + if Source is TMINFORecord then + begin + LSource := TMINFORecord(Source); + FResponsiblePerson := LSource.ResponsiblePersonMailbox; + FErrorMailbox := LSource.ErrorMailbox; + end; +end; + +procedure TMINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + FResponsiblePerson := DNSStrToDomain(CompleteMessage, APos); + FErrorMailbox := DNSStrToDomain(CompleteMessage, APos); +end; + +{ THINFORecord } + +procedure THINFORecord.Assign(Source: TPersistent); +var + LSource: THINFORecord; +begin + inherited Assign(Source); + if Source is THINFORecord then + begin + LSource := THINFORecord(Source); + FCPU := LSource.CPU; + FOS := LSource.OS; + end; +end; + +procedure THINFORecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + FCPU := NextDNSLabel(CompleteMessage, APos); + FOS := NextDNSLabel(CompleteMessage, APos); +end; + + +{ TAAAARecord } + +procedure TAAAARecord.Assign(Source: TPersistent); +begin + inherited Assign(Source); + if Source is TAAAARecord then begin + FAddress := TAAAARecord(Source).Address; + end; +end; + +procedure TAAAARecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +var + FIP6 : TIdIPv6Address; + i : Integer; +begin + inherited Parse(CompleteMessage, APos); + if Length(RData) >= 15 then begin + BytesToIPv6(RData, FIP6); + for i := 0 to 7 do begin + FIP6[i] := GStack.NetworkToHost(FIP6[i]); + end; + FAddress := IPv6AddressToStr(FIP6); + end; +end; + +{ TIdDNSResolver } + +procedure TIdDNSResolver.ClearInternalQuery; +begin + SetLength(FInternalQuery, 0); + FQuestionLength := 0; +end; + +procedure TIdDNSResolver.CreateQuery(ADomain: string; SOARR : TIdRR_SOA; + QueryClass:integer=1); + + function DoDomainName(ADNS : String): TIdBytes; + var + BufStr : String; + LLen : Byte; + begin + SetLength(Result, 0); + while Length(ADNS) > 0 do begin + BufStr := Fetch(ADNS, '.'); {Do not Localize} + LLen := Length(BufStr); + AppendByte(Result, LLen); + AppendString(Result, BufStr, LLen); + end; + end; + + function DoHostAddressV6(const ADNS: String): TIdBytes; + var + IPV6Str, IPV6Ptr: string; + i: Integer; + begin + if not IsValidIPv6(ADNS) then begin + raise EIdDnsResolverError.CreateFmt(RSQueryInvalidIpV6, [aDNS]); + end; + IPV6Str := ConvertToCanonical6IP(ADNS); + IPV6Ptr := ''; {Do not Localize} + for i := Length(IPV6Str) downto 1 do begin + if IPV6Str[i] <> ':' then begin {Do not Localize} + IPV6Ptr := IPV6Ptr + IPV6Str[i] + '.'; {Do not Localize} + end; + end; + IPV6Ptr := IPV6Ptr + 'IP6.INT'; {Do not Localize} + Result := DoDomainName(IPV6Ptr); + end; + + function DoHostAddress(const ADNS: String): TIdBytes; + var + BufStr, First, Second, Third, Fourth: String; + LLen: Byte; + begin { DoHostAddress } + if Pos(':', ADNS) > 0 then begin {Do not Localize} + Result := DoHostAddressV6(ADNS); + end else begin + SetLength(Result, 0); + BufStr := ADNS; + + First := Fetch(BufStr, '.'); + Second := Fetch(BufStr, '.'); + Third := Fetch(BufStr, '.'); + Fourth := BufStr; + + LLen := Length(Fourth); + AppendByte(Result, LLen); + AppendString(Result, Fourth, LLen); + + LLen := Length(Third); + AppendByte(Result, LLen); + AppendString(Result, Third, LLen); + + LLen := Length(Second); + AppendByte(Result, LLen); + AppendString(Result, Second, LLen); + + LLen := Length(First); + AppendByte(Result, LLen); + AppendString(Result, First, LLen); + + AppendByte(Result, 7); + AppendString(Result, 'in-addr', 7); {do not localize} + + AppendByte(Result, 4); + AppendString(Result, 'arpa', 4); {do not localize} + end; + end; + +var + ARecType: TQueryRecordTypes; + iQ: Integer; + AQuestion, AAuthority: TIdBytes; + TempBytes: TIdBytes; + w : UInt16; +begin + SetLength(TempBytes, 2); + SetLength(AAuthority, 0); + FDNSHeader.ID := Random(65535); + + FDNSHeader.ClearByteCode; + FDNSHeader.Qr := 0; + FDNSHeader.OpCode := 0; + FDNSHeader.ANCount := 0; + FDNSHeader.NSCount := 0; + FDNSHeader.ARCount := 0; + //do not reverse the bytes because this is a bit set + FDNSHeader.RD := UInt16(FAllowRecursiveQueries); + + // Iterate thru questions + { TODO : Optimize for non-double loop } + if (QueryType * [qtAXFR, qtIXFR]) <> [] then + begin + iQ := 1; // if exec AXFR, there can be only one Question. + if qtIXFR in QueryType then begin + // if exec IXFR, we must include a SOA record in Authority Section (RFC 1995) + if not Assigned(SOARR) then begin + raise EIdDnsResolverError.Create(GetErrorStr(7, 3)); + end; + AAuthority := SOARR.BinQueryRecord(''); + FDNSHeader.AA := 1; + end; + end else + begin + iQ := 0; + for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do begin + if ARecType in QueryType then begin + Inc(iQ); + end; + end; + end; + + FDNSHeader.QDCount := iQ; + if FDNSHeader.QDCount = 0 then begin + ClearInternalQuery; + Exit; + end; + + InternalQuery := FDNSHeader.GenerateBinaryHeader; + + if qtAXFR in QueryType then begin + if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize} + (IndyPos('IP6.INT', UpperCase(ADomain)) > 0) then {do not localize} + begin + AppendBytes(AQuestion, DoHostAddress(ADomain)); + AppendByte(AQuestion, 0); + end else + begin + AppendBytes(AQuestion, DoDomainName(ADomain)); + AppendByte(AQuestion, 0); + end; + //we do this in a round about manner because HostToNetwork will not always + //work the same + w := 252; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); // Type = AXFR + w := QueryClass; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); + end + else if qtIXFR in QueryType then begin + if (IndyPos('IN-ADDR', UpperCase(ADomain)) > 0) or {Do not Localize} + (IndyPos('IP6.INT', UpperCase(ADomain)) > 0) then {do not localize} + begin + AppendBytes(AQuestion, DoHostAddress(ADomain)); + AppendByte(AQuestion, 0); + end else + begin + AppendBytes(AQuestion, DoDomainName(ADomain)); + AppendByte(AQuestion, 0); + end; + //we do this in a round about manner because HostToNetwork will not always + //work the same + w := 251; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); // Type = IXFR + w := QueryClass; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); + end else + begin + for ARecType := Low(TQueryRecordTypes) to High(TQueryRecordTypes) do begin + if ARecType in QueryType then begin + // Create the question + if (ARecType = qtPTR) and + (IndyPos('IN-ADDR', UpperCase(ADomain)) = 0) and {Do not Localize} + (IndyPos('IP6.INT', UpperCase(ADomain)) = 0) then {do not localize} + begin + AppendBytes(AQuestion, DoHostAddress(ADomain)); + end else begin + AppendBytes(AQuestion, DoDomainName(ADomain)); + end; + AppendByte(AQuestion, 0); + w := QueryRecordValues[Ord(ARecType)]; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); + w := QueryClass; + w := GStack.HostToNetwork(w); + UInt16ToTwoBytes(w, TempBytes, 0); + AppendBytes(AQuestion, TempBytes); + end; + end; + end; + AppendBytes(FInternalQuery, AQuestion); + FQuestionLength := Length(FInternalQuery); + FDNSHeader.ParseQuery(FInternalQuery); +end; + +destructor TIdDNSResolver.Destroy; +begin + FreeAndNil(FQueryResult); + FreeAndNil(FDNSHeader); + inherited Destroy; +end; + +procedure TIdDNSResolver.FillResult(AResult: TIdBytes; CheckID: Boolean = True; + ResetResult: Boolean = True); +var + ReplyId: UInt16; + NAnswers: UInt16; +begin + { TODO : Check bytes received } + // Check to see if the reply is the one waited for + if Length(AResult) < 12 then begin + raise EIdDnsResolverError.Create(GetErrorStr(5, 29)); + end; +{ + if Length(AResult) < Self.FQuestionLength then begin + raise EIdDnsResolverError.Create(GetErrorStr(5, 30)); + end; +} + + if CheckID then begin + ReplyId := GStack.NetworkToHost(TwoByteToUInt16(AResult[0], AResult[1])); + if ReplyId <> FDNSHeader.Id then begin + raise EIdDnsResolverError.Create(GetErrorStr(4, FDNSHeader.id)); + end; + end; + FDNSHeader.ParseQuery(AResult); + + if FDNSHeader.RCode <> 0 then begin + raise EIdDnsResolverError.Create(GetRCodeStr(FDNSHeader.RCode)); + end; + + NAnswers := FDNSHeader.ANCount + FDNSHeader.NSCount + FDNSHeader.ARCount; + if NAnswers > 0 then begin + // Move Pointer to Start of answers + if Length(AResult) > 12 then begin + ParseAnswers(FDNSHeader, AResult, ResetResult); + end; + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +procedure TIdDNSResolver.FillResultWithOutCheckId(AResult: TIdBytes); +{$I IdDeprecatedImplBugOn.inc} +var + NAnswers: UInt16; +begin + if FDNSHeader.ParseQuery(AResult) <> 0 then begin + raise EIdDnsResolverError.Create(GetErrorStr(5, 29)); + end; + + { + if FDNSHeader.RCode <> 0 then begin + raise EIdDnsResolverError.Create(GetRCodeStr(FDNSHeader.RCode)); + end; + } + + NAnswers := FDNSHeader.ANCount + FDNSHeader.NSCount + FDNSHeader.ARCount; + if NAnswers > 0 then begin + // Move Pointer to Start of answers + if Length(AResult) > 12 then begin + ParseAnswers(FDNSHeader, AResult); + end; + end; +end; + +procedure TQueryResult.FilterBySection(const AKeep: TResultSections); +var + i: Integer; +begin + for i := Count-1 downto 0 do + begin + if not (Items[i].Section in AKeep) then begin + Delete(i); + end; + end; +end; + +procedure TQueryResult.FilterByClass(const AKeep: TResultRecordClass); +var + i: Integer; +begin + for i := Count-1 downto 0 do + begin + if not (Items[i] is AKeep) then begin + Delete(i); + end; + end; +end; + +procedure TIdDNSResolver.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_DOMAIN; + FQueryResult := TQueryResult.Create; + FDNSHeader := TDNSHeader.Create; + FAllowRecursiveQueries := true; + Self.WaitingTime := 5000; +end; + +procedure TIdDNSResolver.ParseAnswers(DNSHeader: TDNSHeader; Answer: TIdBytes; + ResetResult: Boolean = True); +var + i: integer; + APos: Integer; +begin + if ResetResult then begin + QueryResult.Clear; + end; + + APos := 12; //13; // Header is 12 byte long we need next byte + // if QDCount = 1, we need to process Question first. + + if DNSHeader.QDCount = 1 then + begin + // first, get the question + // extract the domain name + QueryResult.FDomainName := DNSStrToDomain(Answer, APos); + // get the query type + QueryResult.FQueryType := TwoByteToUInt16(Answer[APos], Answer[APos + 1]); + Inc(APos, 2); + // get the Query Class + QueryResult.FQueryClass := TwoByteToUInt16(Answer[APos], Answer[APos + 1]); + Inc(APos, 2); + end; + + for i := 1 to DNSHeader.ANCount do begin + QueryResult.Add(Answer, APos).FSection := rsAnswer; + end; + + for i := 1 to DNSHeader.NSCount do begin + QueryResult.Add(Answer, APos).FSection := rsNameServer; + end; + + for i := 1 to DNSHeader.ARCount do begin + QueryResult.Add(Answer, APos).FSection := rsAdditional; + end; + +end; + +procedure TIdDNSResolver.Resolve(ADomain: string; SOARR : TIdRR_SOA = nil; + QClass: integer = Class_IN); +var + UDP_Tunnel : TIdUDPClient; + TCP_Tunnel : TIdTCPClient; + LRet: Integer; + LResult: TIdBytes; + BytesReceived: Integer; +begin + if ADomain <> '' then begin + ClearInternalQuery; + end; + + // Resolve queries the DNS for the records contained in the + if FQuestionLength = 0 then begin + if qtIXFR in QueryType then begin + CreateQuery(ADomain, SOARR, QClass); + end else begin + CreateQuery(ADomain, nil, QClass) + end; + end; + + if FQuestionLength = 0 then begin + raise EIdDnsResolverError.CreateFmt(RSQueryInvalidQueryCount, [0]); + end; + + if qtAXFR in QueryType then begin + // AXFR + TCP_Tunnel := TIdTCPClient.Create; + try + TCP_Tunnel.Host := Host; + TCP_Tunnel.Port := Port; + TCP_Tunnel.IPVersion := IPVersion; + TCP_Tunnel.IOHandler := IOHandler; + + try + TCP_Tunnel.Connect; + try + TCP_Tunnel.IOHandler.Write(Int16(FQuestionLength)); + TCP_Tunnel.IOHandler.Write(InternalQuery); + + QueryResult.Clear; + + LRet := TCP_Tunnel.IOHandler.ReadInt16; + SetLength(LResult, LRet); + TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet); + PlainTextResult := LResult; + + if LRet > 4 then begin + FillResult(LResult, False, False); + if QueryResult.Count = 0 then begin + raise EIdDnsResolverError.Create(GetErrorStr(2,3)); + end; + end else begin + raise EIdDnsResolverError.Create(RSDNSTimeout); + end; + finally + TCP_Tunnel.Disconnect; + end; + except + on EIdConnectTimeout do begin + SetLength(FPlainTextResult, 0); + IndyRaiseOuterException(EIdDNSResolverError.Create(RSDNSTimeout)); + end; + on EIdConnectException do begin + SetLength(FPlainTextResult, 0); + IndyRaiseOuterException(EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed)); + end; + end; + finally + FreeAndNil(TCP_Tunnel); + end; + end + else if qtIXFR in QueryType then begin + // IXFR + TCP_Tunnel := TIdTCPClient.Create; + try + TCP_Tunnel.Host := Host; + TCP_Tunnel.Port := Port; + TCP_Tunnel.IPVersion := IPVersion; + TCP_Tunnel.IOHandler := IOHandler; + + { Thanks RLebeau, you fix a lot of codes which I do not spend time to do - Dennies Chang. } + + try + TCP_Tunnel.Connect; + try + TCP_Tunnel.IOHandler.Write(Int16(FQuestionLength)); + TCP_Tunnel.IOHandler.Write(InternalQuery); + + QueryResult.Clear; + + LRet := TCP_Tunnel.IOHandler.ReadInt16; + SetLength(LResult, LRet); + TCP_Tunnel.IOHandler.ReadBytes(LResult, LRet); + PlainTextResult := LResult; + + if LRet > 4 then begin + FillResult(LResult, False, False); + if QueryResult.Count = 0 then begin + raise EIdDnsResolverError.Create(GetErrorStr(2,3)); + end; + end else begin + raise EIdDnsResolverError.Create(RSDNSTimeout); + end; + finally + TCP_Tunnel.Disconnect; + end; + except + on EIdConnectTimeout do begin + SetLength(FPlainTextResult, 0); + IndyRaiseOuterException(EIdDNSResolverError.Create(RSDNSTimeout)); + end; + on EIdConnectException do begin + SetLength(FPlainTextResult, 0); + IndyRaiseOuterException(EIdDNSResolverError.Create(RSTunnelConnectToMasterFailed)); + end; + end; + finally + FreeAndNil(TCP_Tunnel); + end; + end + else begin + UDP_Tunnel := TIdUDPClient.Create; + try + UDP_Tunnel.Host := Host; + UDP_Tunnel.Port := Port; + UDP_Tunnel.IPVersion := IPVersion; + + UDP_Tunnel.SendBuffer(InternalQuery); + + SetLength(LResult, 8192); + BytesReceived := UDP_Tunnel.ReceiveBuffer(LResult, WaitingTime); + SetLength(LResult, BytesReceived); + + if Length(LResult) > 0 then begin + PlainTextResult := LResult; + end else begin + SetLength(FPlainTextResult, 0); + end; + finally + FreeAndNil(UDP_Tunnel); + end; + + if Length(LResult) > 4 then begin + FillResult(LResult); + if QueryResult.Count = 0 then begin + raise EIdDnsResolverError.Create(GetErrorStr(2,3)); + end; + end else begin + raise EIdDnsResolverError.Create(RSDNSTimeout); + end; + end; +end; + +procedure TIdDNSResolver.SetInternalQuery(const Value: TIdBytes); +begin + FQuestionLength := Length(Value); + FInternalQuery := Copy(Value, 0, FQuestionLength); + Self.FDNSHeader.ParseQuery(Value); +end; + +procedure TIdDNSResolver.SetIPVersion(const AValue: TIdIPVersion); +begin + FIPVersion := AValue; +end; + +procedure TIdDNSResolver.SetPlainTextResult(const Value: TIdBytes); +begin + FPlainTextResult := Copy(Value, 0, Length(Value)); +end; + +procedure TIdDNSResolver.SetPort(const AValue: TIdPort); +begin + FPort := AValue; +end; + +procedure TSRVRecord.Assign(Source: TPersistent); +var + LSource: TSRVRecord; +begin + inherited Assign(Source); + if Source is TSRVRecord then + begin + LSource := TSRVRecord(Source); + FService := LSource.Service; + FProtocol := LSource.Protocol; + FPriority := LSource.Priority; + FWeight := LSource.Weight; + FPort := LSource.Port; + FTarget := LSource.Target; + end; +end; + +function TSRVRecord.CleanIdent(const aStr: string): string; +begin + Result := Copy(aStr, 2, MaxInt); +end; + +function TSRVRecord.IsValidIdent(const AStr: string): Boolean; +begin + Result := (Length(AStr) > 1) and TextStartsWith(AStr, '_'); {Do not Localize} +end; + +procedure TSRVRecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +var + LName, LService, LProtocol: string; +begin + inherited Parse(CompleteMessage, APos); + + FOriginalName := FName; + + //this is to split: _sip._udp.example.com + LName := FName; + LService := Fetch(LName, '.', True, False); + LProtocol := Fetch(LName,'.', True, False); + if IsValidIdent(LService) and IsValidIdent(LProtocol) and (LName <> '') then + begin + FService := CleanIdent(LService); + FProtocol := CleanIdent(LProtocol); + FName := LName; + end; + + FPriority := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos+1])); + Inc(APos, 2); + + FWeight := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos+1])); + Inc(APos, 2); + + FPort := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos+1])); + Inc(APos, 2); + + FTarget := DNSStrToDomain(CompleteMessage, APos); +end; + +procedure TNAPTRRecord.Assign(Source: TPersistent); +var + LSource: TNAPTRRecord; +begin + inherited Assign(Source); + if Source is TNAPTRRecord then + begin + LSource := TNAPTRRecord(Source); + FOrder := LSource.Order; + FPreference := LSource.Preference; + FFlags := LSource.FFlags; + FService := LSource.Service; + FRegExp := LSource.RegExp; + FReplacement := LSource.Replacement; + end; +end; + +procedure TNAPTRRecord.Parse(CompleteMessage: TIdBytes; APos: Integer); +begin + inherited Parse(CompleteMessage, APos); + + FOrder := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos+1])); + Inc(APos, 2); + + FPreference := GStack.NetworkToHost(TwoByteToUInt16(CompleteMessage[APos], CompleteMessage[APos+1])); + Inc(APos, 2); + + FFlags := NextDNSLabel(CompleteMessage, APos); + FService := NextDNSLabel(CompleteMessage, APos); + FRegExp := NextDNSLabel(CompleteMessage, APos); + FReplacement := DNSStrToDomain(CompleteMessage, APos); +end; + +end. diff --git a/indy/Protocols/IdDNSServer.pas b/indy/Protocols/IdDNSServer.pas new file mode 100644 index 0000000..42b5040 --- /dev/null +++ b/indy/Protocols/IdDNSServer.pas @@ -0,0 +1,4165 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.40 3/4/2005 12:35:32 PM JPMugaas + Removed some compiler warnings. + + + Rev 1.39 2/9/2005 4:35:06 AM JPMugaas + Should compile. + + + Rev 1.38 2/8/05 6:13:02 PM RLebeau + Updated to use new AppendString() function in IdGlobal unit + + Updated TIdDNS_ProcessThread.CompleteQuery() to use CopyTId...() functions + instead of ToBytes() and AppendBytes(). + + + Rev 1.37 2005/1/25 U 12:25:26 DChang + Modify UpdateTree method, make the NS record can be save in the lower level + node. + + + Rev 1.36 2005/1/5 U 04:21:06 DChang Version: 1.36 + Fix parsing procedure while processing TXT record, in pass version, double + quota will not be processed, but now, any charector between 2 double quotas + will be treated as TXT message. + + + Rev 1.35 2004/12/15 U 12:05:26 DChang Version: 1.35 + 1. Move UpdateTree to public section. + 2. add DoUDPRead of TIdDNSServer. + 3. Fix TIdDNS_ProcessThread.CompleteQuery and + InternalQuery to fit Indy 10 Core. + + + Rev 1.34 12/2/2004 4:23:50 PM JPMugaas + Adjusted for changes in Core. + + + Rev 1.33 2004.10.27 9:17:46 AM czhower + For TIdStrings + + + Rev 1.32 10/26/2004 9:06:32 PM JPMugaas + Updated references. + + + Rev 1.31 2004.10.26 1:06:26 PM czhower + Further fixes for aliaser + + + Rev 1.30 2004.10.26 12:01:32 PM czhower + Resolved alias conflict. + + + Rev 1.29 9/15/2004 4:59:52 PM DSiders + Added localization comments. + + + Rev 1.28 22/07/2004 18:14:22 ANeillans + Fixed compile error. + + + Rev 1.27 7/21/04 2:38:04 PM RLebeau + Removed redundant string copying in TIdDNS_ProcessThread constructor and + procedure QueryDomain() method + + Removed local variable from TIdDNS_ProcessThread.SendData(), not needed + + + Rev 1.26 2004/7/21 U 06:37:48 DChang + Fix compile error in TIdDNS_ProcessThread.SendData, and mark a case statment + to comments in TIdDNS_ProcessThread.SaveToCache. + + + Rev 1.25 2004/7/19 U 09:55:52 DChang + 1. Move all textmoderecords to IdDNSCommon.pas + 2. Making DNS Server load the domain definition file while DNS Server + component is active. + 3. Add a new event : OnAfterCacheSaved + 4. Add Full name condition to indicate if a domain is empty + (ConvertDNtoString) + 5. Make Query request processed with independent thread. + 6. Rewrite TIdDNSServer into multiple thread mode, all queries will search + and assemble the answer, and then share the TIdSocketHandle to send answer + back. + 7. Add version information in TIdDNSServer, so class CHAOS can be taken, but + only for the label : "version.bind.". + 8. Fix TIdRR_TXT.BinQueryRecord, to make sure it can be parsed in DNS client. + 9. Modify the AXFR function, reduce the response data size and quantity. + 10. Move all TIdTextModeResourceRecord and derived classes to IdDNSCommon.pas + + + Rev 1.24 7/8/04 11:43:54 PM RLebeau + Updated TIdDNS_TCPServer.DoConnect() to use new BytesToString() parameters + + + Rev 1.23 7/7/04 1:45:16 PM RLebeau + Compiler fixes + + + Rev 1.22 6/29/04 1:43:30 PM RLebeau + Bug fixes for various property setters + + + Rev 1.21 2004.05.20 1:39:32 PM czhower + Last of the IdStream updates + + + Rev 1.20 2004.03.01 9:37:06 PM czhower + Fixed name conflicts for .net + + + Rev 1.19 2004.02.07 5:03:32 PM czhower + .net fixes. + + + Rev 1.18 2/7/2004 5:39:44 AM JPMugaas + IdDNSServer should compile in both DotNET and WIn32. + + + Rev 1.17 2004.02.03 5:45:58 PM czhower + Name changes + + + Rev 1.16 1/22/2004 8:26:40 AM JPMugaas + Ansi* calls changed. + + + Rev 1.15 1/21/2004 2:12:48 PM JPMugaas + InitComponent + + + Rev 1.14 12/7/2003 8:07:26 PM VVassiliev + string -> TIdBytes + + + Rev 1.13 2003.10.24 10:38:24 AM czhower + UDP Server todos + + + Rev 1.12 10/19/2003 12:16:30 PM DSiders + Added localization comments. + + + Rev 1.11 2003.10.12 3:50:40 PM czhower + Compile todos + + + Rev 1.10 2003/5/14 W 01:17:36 DChang + Fix a flag named denoted in the function which check if a domain correct. + Update the logic of UpdateTree functions (make them unified). + Update the TextRecord function of all TIdRR_ classes, it checks if the RRName + the same as FullName, if RRName = FullName, it will not append the Fullname + to RRName. + + + Rev 1.9 2003/5/10 W 01:09:42 DChang + Patch the domainlist update when axfr action. + + + Rev 1.8 2003/5/9 W 10:03:36 DChang + Modify the sequence of records. To make sure when we resolve MX record, the + mail host A record can be additional record section. + + + Rev 1.7 2003/5/8 U 08:11:34 DChang + Add TIdDNSMap, TIdDomainNameServerMapping to monitor primary DNS, and + detecting if the primary DNS record changed, it will update automatically if + necessary. + + + Rev 1.6 2003/5/2 U 03:39:38 DChang + Fix all compile warnings and hints. + + + Rev 1.5 4/29/2003 08:26:30 PM DenniesChang + Fix TIdDNSServer Create, the older version miss to create the FBindings. + fix AXFR procedure, fully support BIND 8 AXFR procedures. + + Rev 1.4 4/28/2003 02:30:58 PM JPMugaas + reverted back to the old one as the new one checked will not compile, has + problametic dependancies on Contrs and Dialogs (both not permitted). + + Rev 1.3 04/28/2003 01:15:10 AM DenniesChang + + + Rev 1.2 4/28/2003 07:00:18 AM JPMugaas + Should now compile. + + + Rev 1.0 11/14/2002 02:18:42 PM JPMugaas + + // Ver: 2003-04-28-0115 + // Combine TCP, UDP Tunnel into single TIdDNSServer component. + // Update TIdDNSServer from TIdUDPServer to TComponent. + + // Ver: 2003-04-26-1810 + // Add AXFR command. + + // Ver: 2002-10-30-1253 + // Add TIdRR_AAAA class, RFC 1884 (Ipv6 AAAA) + // and add the coresponding fix in TIdDNSServer, but left + // external search option for future. + + // Ver: 2002-07-10-1610 + // Add a new event : OnAfterSendBack to handle all + // data logged after query result is sent back to + // the client. + + // Ver: 2002-05-27-0910 + // Add a check function in SOA loading function. + + // Ver: 2002-04-25-1530 + // IdDNSServer. Ver: 2002-03-12-0900 + + + // To-do: RFC 2136 Zone transfer must be implemented. + + + // Add FindHandedNodeByName to pass the TIdDNTreeNode Object back. + // Append a blank char when ClearQuota, to avoid the possible of + // losting a field. + // Add IdDNTree.SaveToFile + // Fix SOA RRName assignment. + // Fix PTRName RRName assignment. + // Fix TIdDNTreeNode RemoveChild + + // IdDNSServer. Ver: 2002-02-26-1420 + // Convert the DN Tree Node type, earlier verison just + // store the A, PTR in the upper domain node, current + // version save SOA and its subdomain in upper node. + // + // Moreover, move Cached_Tree, Handed_Tree to public + // section, for using convinent. + // + // I forget return CName data, fixed. + // Seperate the seaching of Cache and handled tree into 2 + // parts with a flag. + + + //IdDNSServer. Ver: 2002-02-24-1715 + // Move TIdDNSServer protected property RootDNS_NET to public + + + //IdDNSServer. Ver: 2002-02-23-1800 + + Original Programmer: Dennies Chang + No Copyright. Code is given to the Indy Pit Crew. + + This DNS Server supports only IN record, but not Chaos system. + Most of resource records in DNS server was stored with text mode, + event the TREE structure, it's just for convininet. + + Why I did it with this way is tring to increase the speed for + implementation, with Delphi/Kylix internal class and object, + we can promise the compatible in Windows and Linux. + + Started: Jan. 20, 2002. + First Finished: Feb. 23, 2002. + + RFC 1035 WKS record is not implemented. + + ToDO: Load Master File automaticlly when DNS Server Active. + ToDO: patch WKS record data type. + ToDO: prepare a Tree Editor for DNS Server Construction. (optional) +} +unit IdDNSServer; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdContainers, + IdAssignedNumbers, + IdSocketHandle, + IdIOHandlerSocket, + IdGlobal, + IdGlobalProtocols, + IdBaseComponent, + IdComponent, + IdContext, + IdUDPBase, + IdResourceStrings, + IdExceptionCore, + IdDNSResolver, + IdUDPServer, + IdCustomTCPServer, + IdStackConsts, + IdThread, + IdDNSCommon; + +type + TIdDomainExpireCheckThread = class(TIdThread) + protected + FInterval: UInt32; + FSender: TObject; + FTimerEvent: TNotifyEvent; + FBusy : Boolean; + FDomain : string; + FHost : string; + // + procedure Run; override; + procedure TimerEvent; + end; + + // forward declaration. + TIdDNSMap = class; + TIdDNS_UDPServer = class; + + // This class is to record the mapping of Domain and its primary DNS IP + TIdDomainNameServerMapping = class(TObject) + private + FHost: string; + FDomainName: string; + FBusy : Boolean; + FInterval: UInt32; + FList: TIdDNSMap; + procedure SetHost(const Value: string); + procedure SetInterval(const Value: UInt32); + protected + CheckScheduler : TIdDomainExpireCheckThread; + property Interval : UInt32 read FInterval write SetInterval; + property List : TIdDNSMap read FList write FList; + public + constructor Create(AList : TIdDNSMap); + destructor Destroy; override; + //You can not make methods and properties published in this class. + //If you want to make properties publishes, this has to derrive from TPersistant + //and be used by TPersistant in a published property. + // published + procedure SyncAndUpdate(Sender : TObject); + property Host : string read FHost write SetHost; + property DomainName : string read FDomainName write FDomainName; + end; + + TIdDNSMap = class(TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}) + private + FServer: TIdDNS_UDPServer; + {$IFNDEF HAS_GENERICS_TObjectList} + function GetItem(Index: Integer): TIdDomainNameServerMapping; + procedure SetItem(Index: Integer; const Value: TIdDomainNameServerMapping); + {$ENDIF} + procedure SetServer(const Value: TIdDNS_UDPServer); + public + constructor Create(Server: TIdDNS_UDPServer); + {$IFNDEF USE_OBJECT_ARC} + destructor Destroy; override; + {$ENDIF} + property Server : TIdDNS_UDPServer read FServer write SetServer; + {$IFNDEF HAS_GENERICS_TObjectList} + property Items[Index: Integer]: TIdDomainNameServerMapping read GetItem write SetItem; default; + {$ENDIF} + end; + + TIdMWayTreeNodeClass = class of TIdMWayTreeNode; + // TODO: derive from TObjectList instead and remove SubTree member? + TIdMWayTreeNode = class(TObject) + private + SubTree : TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}; + FFundmentalClass: TIdMWayTreeNodeClass; + function GetTreeNode(Index: Integer): TIdMWayTreeNode; + procedure SetFundmentalClass(const Value: TIdMWayTreeNodeClass); + procedure SetTreeNode(Index: Integer; const Value: TIdMWayTreeNode); + public + constructor Create(NodeClass : TIdMWayTreeNodeClass); virtual; + destructor Destroy; override; + property FundmentalClass : TIdMWayTreeNodeClass read FFundmentalClass write SetFundmentalClass; + property Children[Index : Integer] : TIdMWayTreeNode read GetTreeNode write SetTreeNode; + function AddChild : TIdMWayTreeNode; + function InsertChild(Index : Integer) : TIdMWayTreeNode; + procedure RemoveChild(Index : Integer); + end; + + TIdDNTreeNode = class(TIdMWayTreeNode) + private + FCLabel : String; + FRRs: TIdTextModeRRs; + FChildIndex: TStrings; + FParentNode: TIdDNTreeNode; + FAutoSortChild: Boolean; + procedure SetCLabel(const Value: String); + procedure SetRRs(const Value: TIdTextModeRRs); + function GetNode(Index: integer): TIdDNTreeNode; + procedure SetNode(Index: integer; const Value: TIdDNTreeNode); + procedure SetChildIndex(const Value: TStrings); + function GetFullName: string; + function ConvertToDNString : string; + function DumpAllBinaryData(var RecordCount:integer) : TIdBytes; + public + property ParentNode : TIdDNTreeNode read FParentNode write FParentNode; + property CLabel : String read FCLabel write SetCLabel; + property RRs : TIdTextModeRRs read FRRs write SetRRs; + property Children[Index : Integer] : TIdDNTreeNode read GetNode write SetNode; + property ChildIndex : TStrings read FChildIndex write SetChildIndex; + property AutoSortChild : Boolean read FAutoSortChild write FAutoSortChild; + property FullName : string read GetFullName; + + constructor Create(AParentNode : TIdDNTreeNode); reintroduce; + destructor Destroy; override; + function AddChild : TIdDNTreeNode; + function InsertChild(Index : Integer) : TIdDNTreeNode; + procedure RemoveChild(Index : Integer); + procedure SortChildren; + procedure Clear; + procedure SaveToFile(Filename : String); + function IndexByLabel(CLabel : String): Integer; + function IndexByNode(ANode : TIdDNTreeNode) : Integer; + end; + + TIdDNS_TCPServer = class(TIdCustomTCPServer) + protected + FAccessList: TStrings; + FAccessControl: Boolean; + // + procedure DoConnect(AContext: TIdContext); override; + procedure InitComponent; override; + procedure SetAccessList(const Value: TStrings); + public + destructor Destroy; override; + published + property AccessList : TStrings read FAccessList write SetAccessList; + property AccessControl : boolean read FAccessControl write FAccessControl; + end; + + TIdDNS_ProcessThread = class(TIdThread) + protected + FMyBinding: TIdSocketHandle; + FMainBinding: TIdSocketHandle; + FMyData: TStream; + FData : TIdBytes; + FServer: TIdDNS_UDPServer; + procedure SetMyBinding(const Value: TIdSocketHandle); + procedure SetMyData(const Value: TStream); + procedure SetServer(const Value: TIdDNS_UDPServer); + procedure ComposeErrorResult(var VFinal: TIdBytes; OriginalHeader: TDNSHeader; + OriginalQuestion : TIdBytes; ErrorStatus: Integer); + function CombineAnswer(Header : TDNSHeader; const EQuery, Answer : TIdBytes): TIdBytes; + procedure InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16; + var Answer: TIdBytes; IfMainQuestion: Boolean; IsSearchCache: Boolean = False; + IsAdditional: Boolean = False; IsWildCard : Boolean = False; + WildCardOrgName: string = ''); + procedure ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader; + Question: TIdBytes; var Answer: TIdBytes); + function CompleteQuery(DNSHeader: TDNSHeader; Question: string; + OriginalQuestion: TIdBytes; var Answer : TIdBytes; QType, QClass : UInt16; + DNSResolver : TIdDNSResolver) : string; + procedure SaveToCache(ResourceRecord : TIdBytes; QueryName : string; OriginalQType : UInt16); + function SearchTree(Root : TIdDNTreeNode; QName : String; QType : UInt16): TIdDNTreeNode; + + procedure Run; override; + procedure QueryDomain; + procedure SendData; + public + property MyBinding : TIdSocketHandle read FMyBinding write SetMyBinding; + property MyData: TStream read FMyData write SetMyData; + property Server : TIdDNS_UDPServer read FServer write SetServer; + + constructor Create(ACreateSuspended: Boolean = True; Data : TIdBytes = nil; + MainBinding : TIdSocketHandle = nil; Binding : TIdSocketHandle = nil; + Server : TIdDNS_UDPServer = nil); reintroduce; overload; + + destructor Destroy; override; + end; + + TIdDNSBeforeQueryEvent = procedure(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; var ADNSQuery: TIdBytes) of object; + TIdDNSAfterQueryEvent = procedure(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode: string; Query : TIdBytes) of object; + TIdDNSAfterCacheSaved = procedure(CacheRoot : TIdDNTreeNode) of object; + + TIdDNS_UDPServer = class(TIdUDPServer) + private + FBusy: Boolean; + protected + FAutoUpdateZoneInfo: Boolean; + FZoneMasterFiles: TStrings; + FRootDNS_NET: TStrings; + FCacheUnknowZone: Boolean; + FCached_Tree: TIdDNTreeNode; + FHanded_Tree: TIdDNTreeNode; + FHanded_DomainList: TStrings; + FAutoLoadMasterFile: Boolean; + FOnAfterQuery: TIdDNSAfterQueryEvent; + FOnBeforeQuery: TIdDNSBeforeQueryEvent; + FCS: TIdCriticalSection; + FOnAfterSendBack: TIdDNSAfterQueryEvent; + FOnAfterCacheSaved: TIdDNSAfterCacheSaved; + FGlobalCS: TIdCriticalSection; + FDNSVersion: string; + FofferDNSVersion: Boolean; + + procedure DoBeforeQuery(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; + var ADNSQuery : TIdBytes); dynamic; + + procedure DoAfterQuery(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; + var QueryResult : TIdBytes; var ResultCode : String; Query : TIdBytes); dynamic; + + procedure DoAfterSendBack(ABinding: TIdSocketHandle; ADNSHeader: TDNSHeader; + var QueryResult : TIdBytes; var ResultCode : String; Query : TIdBytes); dynamic; + + procedure DoAfterCacheSaved(CacheRoot : TIdDNTreeNode); dynamic; + + procedure SetZoneMasterFiles(const Value: TStrings); + procedure SetRootDNS_NET(const Value: TStrings); + procedure SetHanded_DomainList(const Value: TStrings); + procedure InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16; + var Answer: TIdBytes; IfMainQuestion: boolean; IsSearchCache: Boolean = False; + IsAdditional: Boolean = False; IsWildCard : Boolean = False; + WildCardOrgName: string = ''); + procedure ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader; + Question: TIdBytes; var Answer: TIdBytes); + //modified in May 2004 by Dennies Chang. + //procedure SaveToCache(ResourceRecord : string); + procedure SaveToCache(ResourceRecord : TIdBytes; QueryName : string; OriginalQType : UInt16); + //procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TResultRecord); overload; + //MoveTo Public section for RaidenDNSD. + + procedure InitComponent; override; + // Hide this property temporily, this property is prepared to maintain the + // TTL expired record auto updated; + property AutoUpdateZoneInfo : boolean read FAutoUpdateZoneInfo write FAutoUpdateZoneInfo; + property CS: TIdCriticalSection read FCS; + procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); override; + public + destructor Destroy; override; + function AXFR(Header : TDNSHeader; Question : string; var Answer : TIdBytes) : string; + function CompleteQuery(DNSHeader: TDNSHeader; Question: string; + OriginalQuestion: TIdBytes; var Answer : TIdBytes; QType, QClass : UInt16; + DNSResolver : TIdDNSResolver) : string; {$IFDEF HAS_DEPRECATED}deprecated;{$ENDIF} + function LoadZoneFromMasterFile(MasterFileName : String) : boolean; + function LoadZoneStrings(FileStrings: TStrings; Filename : String; + TreeRoot : TIdDNTreeNode): Boolean; + function SearchTree(Root : TIdDNTreeNode; QName : String; QType : UInt16): TIdDNTreeNode; + procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TIdTextModeResourceRecord); overload; + function FindNodeFullName(Root : TIdDNTreeNode; QName : String; QType : UInt16) : string; + function FindHandedNodeByName(QName : String; QType : UInt16) : TIdDNTreeNode; + procedure UpdateTree(TreeRoot : TIdDNTreeNode; RR : TResultRecord); overload; + + property RootDNS_NET : TStrings read FRootDNS_NET write SetRootDNS_NET; + property Cached_Tree : TIdDNTreeNode read FCached_Tree {write SetCached_Tree}; + property Handed_Tree : TIdDNTreeNode read FHanded_Tree {write SetHanded_Tree}; + property Busy : Boolean read FBusy; + property GlobalCS : TIdCriticalSection read FGlobalCS; + published + property DefaultPort default IdPORT_DOMAIN; + property AutoLoadMasterFile : Boolean read FAutoLoadMasterFile write FAutoLoadMasterFile Default False; + + //property AutoUpdateZoneInfo : boolean read FAutoUpdateZoneInfo write SetAutoUpdateZoneInfo; + property ZoneMasterFiles : TStrings read FZoneMasterFiles write SetZoneMasterFiles; + property CacheUnknowZone : Boolean read FCacheUnknowZone write FCacheUnknowZone default False; + property Handed_DomainList : TStrings read FHanded_DomainList write SetHanded_DomainList; + property DNSVersion : string read FDNSVersion write FDNSVersion; + property offerDNSVersion : Boolean read FofferDNSVersion write FofferDNSVersion; + + property OnBeforeQuery : TIdDNSBeforeQueryEvent read FOnBeforeQuery write FOnBeforeQuery; + property OnAfterQuery : TIdDNSAfterQueryEvent read FOnAfterQuery write FOnAfterQuery; + property OnAfterSendBack : TIdDNSAfterQueryEvent read FOnAfterSendBack write FOnAfterSendBack; + property OnAfterCacheSaved : TIdDNSAfterCacheSaved read FOnAfterCacheSaved write FOnAfterCacheSaved; + end; + + TIdDNSServer = class(TIdComponent) + protected + FActive: Boolean; + FTCPACLActive: Boolean; + FServerType: TDNSServerTypes; + FTCPTunnel: TIdDNS_TCPServer; + FUDPTunnel: TIdDNS_UDPServer; + FAccessList: TStrings; + FBindings: TIdSocketHandles; + procedure SetAccessList(const Value: TStrings); + procedure SetActive(const Value: Boolean); + procedure SetTCPACLActive(const Value: Boolean); + procedure SetBindings(const Value: TIdSocketHandles); + procedure TimeToUpdateNodeData(Sender : TObject); + procedure InitComponent; override; + public + BackupDNSMap : TIdDNSMap; + + destructor Destroy; override; + procedure CheckIfExpire(Sender: TObject); + published + property Active : Boolean read FActive write SetActive; + property AccessList : TStrings read FAccessList write SetAccessList; + property Bindings: TIdSocketHandles read FBindings write SetBindings; + + property TCPACLActive : Boolean read FTCPACLActive write SetTCPACLActive; + property ServerType: TDNSServerTypes read FServerType write FServerType; + property TCPTunnel : TIdDNS_TCPServer read FTCPTunnel write FTCPTunnel; + property UDPTunnel : TIdDNS_UDPServer read FUDPTunnel write FUDPTunnel; + end; + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + {$IFNDEF NEXTGEN} + System.Contnrs, + {$ENDIF} + System.SyncObjs, + System.Types, + {$ENDIF} + IdException, + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + IdIOHandler, + IdStack, + SysUtils; + +{Common Utilities} + +function CompareItems(Item1, Item2: {$IFDEF HAS_GENERICS_TObjectList}TIdMWayTreeNode{$ELSE}TObject{$ENDIF}): Integer; +var + LObj1, LObj2 : TIdDNTreeNode; +begin + LObj1 := Item1 as TIdDNTreeNode; + LObj2 := Item2 as TIdDNTreeNode; + Result := CompareStr(LObj1.CLabel, LObj2.CLabel); +end; + +// TODO: move to IdGlobal.pas +function PosBytes(const SubBytes, SBytes: TIdBytes): Integer; +var + LSubLen, LBytesLen, I: Integer; +begin + LSubLen := Length(SubBytes); + LBytesLen := Length(SBytes); + if (LSubLen > 0) and (LBytesLen >= LSubLen) then + begin + for Result := 0 to LBytesLen-LSubLen do + begin + if SBytes[Result] = SubBytes[0] then + begin + for I := 1 to LSubLen-1 do + begin + if SBytes[Result+I] <> SubBytes[I] then begin + Break; + end; + end; + if I = LSubLen then begin + Exit; + end; + end; + end; + end; + Result := -1; +end; + +// TODO: move to IdGlobal.pas +function FetchBytes(var AInput: TIdBytes; const ADelim: TIdBytes; + const ADelete: Boolean = IdFetchDeleteDefault): TIdBytes; +var + LPos: integer; +begin + LPos := PosBytes(ADelim, AInput); + if LPos = -1 then begin + Result := AInput; + if ADelete then begin + SetLength(AInput, 0); + end; + end + else begin + Result := ToBytes(AInput, LPos); + if ADelete then begin + //slower Delete(AInput, 1, LPos + Length(ADelim) - 1); + RemoveBytes(AInput, LPos + Length(ADelim)); + end; + end; +end; + +{ TIdMWayTreeNode } + +function TIdMWayTreeNode.AddChild: TIdMWayTreeNode; +begin + Result := FundmentalClass.Create(FundmentalClass); + try + SubTree.Add(Result); + except + FreeAndNil(Result); + raise; + end; +end; + +constructor TIdMWayTreeNode.Create(NodeClass : TIdMWayTreeNodeClass); +begin + inherited Create; + FundmentalClass := NodeClass; + SubTree := TIdObjectList{$IFDEF HAS_GENERICS_TObjectList}{$ENDIF}.Create; +end; + +destructor TIdMWayTreeNode.Destroy; +begin + FreeAndNil(SubTree); + inherited Destroy; +end; + +function TIdMWayTreeNode.GetTreeNode(Index: Integer): TIdMWayTreeNode; +begin + Result := {$IFDEF HAS_GENERICS_TObjectList}SubTree.Items[Index]{$ELSE}TIdMWayTreeNode(SubTree.Items[Index]){$ENDIF}; +end; + +function TIdMWayTreeNode.InsertChild(Index: Integer): TIdMWayTreeNode; +begin + Result := FundmentalClass.Create(FundmentalClass); + try + SubTree.Insert(Index, Result); + except + FreeAndNil(Result); + raise; + end; +end; + +procedure TIdMWayTreeNode.RemoveChild(Index: Integer); +begin + SubTree.Delete(Index); +end; + +procedure TIdMWayTreeNode.SetFundmentalClass(const Value: TIdMWayTreeNodeClass); +begin + FFundmentalClass := Value; +end; + +procedure TIdMWayTreeNode.SetTreeNode(Index: Integer; const Value: TIdMWayTreeNode); +begin + {$IFNDEF USE_OBJECT_ARC} + SubTree.Items[Index].Free; + {$ENDIF} + SubTree.Items[Index] := Value; +end; + +{ TIdDNTreeNode } + +function TIdDNTreeNode.AddChild: TIdDNTreeNode; +begin + Result := TIdDNTreeNode.Create(Self); + try + SubTree.Add(Result); + except + FreeAndNil(Result); + raise; + end; +end; + +procedure TIdDNTreeNode.Clear; +var + I : Integer; +begin + for I := SubTree.Count - 1 downto 0 do begin + RemoveChild(I); + end; +end; + +function TIdDNTreeNode.ConvertToDNString: string; +var + Count : Integer; +begin + Result := '$ORIGIN ' + FullName + EOL; {do not localize} + + for Count := 0 to RRs.Count-1 do begin + Result := Result + RRs.Items[Count].TextRecord(FullName); + end; + + for Count := 0 to FChildIndex.Count-1 do begin + Result := Result + Children[Count].ConvertToDNString; + end; +end; + +constructor TIdDNTreeNode.Create(AParentNode : TIdDNTreeNode); +begin + inherited Create(TIdDNTreeNode); + FRRs := TIdTextModeRRs.Create; + FChildIndex := TStringList.Create; + FParentNode := AParentNode; +end; + +destructor TIdDNTreeNode.Destroy; +begin + FreeAndNil(FRRs); + FreeAndNil(FChildIndex); + inherited Destroy; +end; + +function TIdDNTreeNode.DumpAllBinaryData(var RecordCount: Integer): TIdBytes; +var + Count, ChildCount : integer; + MyString, ChildString : TIdBytes; +begin + SetLength(ChildString, 0); + SetLength(MyString, 0); + Inc(RecordCount, RRs.Count + 1); + + for Count := 0 to RRs.Count -1 do + begin + AppendBytes(MyString, RRs.Items[Count].BinQueryRecord(FullName)); + end; + + for Count := 0 to FChildIndex.Count -1 do + begin + // RLebeau: should ChildCount be set to 0 each time? + AppendBytes(ChildString, Children[Count].DumpAllBinaryData(ChildCount)); + Inc(RecordCount, ChildCount); + end; + + if RRs.Count > 0 then begin + if RRs.Items[0] is TIdRR_SOA then begin + AppendBytes(MyString, RRs.Items[0].BinQueryRecord(FullName)); + Inc(RecordCount); + end; + end; + + Result := MyString; + AppendBytes(Result, ChildString); + + if RRs.Count > 0 then begin + AppendBytes(Result, RRs.Items[0].BinQueryRecord(FullName)); + end; +end; + +function TIdDNTreeNode.GetFullName: string; +begin + if ParentNode = nil then begin + if CLabel = '.' then begin + Result := ''; + end else begin + Result := CLabel; + end; + end else begin + Result := CLabel + '.' + ParentNode.FullName; + end; +end; + +function TIdDNTreeNode.GetNode(Index: Integer): TIdDNTreeNode; +begin + Result := TIdDNTreeNode(SubTree.Items[Index]); +end; + +function TIdDNTreeNode.IndexByLabel(CLabel: String): Integer; +begin + Result := FChildIndex.IndexOf(CLabel); +end; + +function TIdDNTreeNode.IndexByNode(ANode: TIdDNTreeNode): Integer; +begin + Result := SubTree.IndexOf(ANode); +end; + +function TIdDNTreeNode.InsertChild(Index: Integer): TIdDNTreeNode; +begin + Result := TIdDNTreeNode.Create(Self); + try + SubTree.Insert(Index, Result); + except + FreeAndNil(Result); + raise; + end; +end; + +procedure TIdDNTreeNode.RemoveChild(Index: Integer); +begin + SubTree.Remove(SubTree.Items[Index]); + FChildIndex.Delete(Index); +end; + +procedure TIdDNTreeNode.SaveToFile(Filename: String); +var + DNSs : TStrings; +begin + DNSs := TStringList.Create; + try + DNSs.Add(ConvertToDNString); + ToDo('SaveToFile() method of TIdDNTreeNode class is not implemented yet'); {do not localized} +// DNSs.SaveToFile(Filename); + finally + FreeAndNil(DNSs); + end; +end; + +procedure TIdDNTreeNode.SetChildIndex(const Value: TStrings); +begin + FChildIndex.Assign(Value); +end; + +procedure TIdDNTreeNode.SetCLabel(const Value: String); +begin + FCLabel := Value; + if ParentNode <> nil then begin + ParentNode.ChildIndex.Insert(ParentNode.SubTree.IndexOf(Self), Value); + end; + if AutoSortChild then begin + SortChildren; + end; +end; + +procedure TIdDNTreeNode.SetNode(Index: Integer; const Value: TIdDNTreeNode); +begin + SubTree.Items[Index] := Value; +end; + +procedure TIdDNTreeNode.SetRRs(const Value: TIdTextModeRRs); +begin + FRRs.Assign(Value); +end; + +procedure TIdDNTreeNode.SortChildren; +begin + SubTree.BubbleSort(CompareItems); + TStringList(FChildIndex).Sort; +end; + +{ TIdDNSServer } + +{$I IdDeprecatedImplBugOff.inc} +function TIdDNS_UDPServer.CompleteQuery(DNSHeader : TDNSHeader; Question: string; + OriginalQuestion: TIdBytes; var Answer: TIdBytes; QType, QClass: UInt16; + DNSResolver : TIdDNSResolver): string; +{$I IdDeprecatedImplBugOn.inc} +var + IsMyDomains : Boolean; + LAnswer: TIdBytes; + WildQuestion, TempDomain : string; +begin + // QClass = 1 => IN, we support only "IN" class now. + // QClass = 2 => CS, + // QClass = 3 => CH, + // QClass = 4 => HS. + + if QClass <> 1 then begin + Result := cRCodeQueryNotImplement; + Exit; + end; + + TempDomain := LowerCase(Question); + IsMyDomains := (Handed_DomainList.IndexOf(TempDomain) > -1); + if not IsMyDomains then begin + Fetch(TempDomain, '.'); + IsMyDomains := (Handed_DomainList.IndexOf(TempDomain) > -1); + end; + + if IsMyDomains then begin + InternalSearch(DNSHeader, Question, QType, LAnswer, True, False, False); + Answer := LAnswer; + + if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then + begin + InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, False, True); + AppendBytes(Answer, LAnswer); + end; + + WildQuestion := Question; + Fetch(WildQuestion, '.'); + WildQuestion := '*.' + WildQuestion; + InternalSearch(DNSHeader, WildQuestion, QType, LAnswer, True, False, False, True, Question); + AppendBytes(Answer, LAnswer); + + if Length(Answer) > 0 then begin + Result := cRCodeQueryOK; + end else begin + Result := cRCodeQueryNotFound; + end; + end else + begin + InternalSearch(DNSHeader, Question, QType, Answer, True, True, False); + + if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then + begin + InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, True, False); + AppendBytes(Answer, LAnswer); + end; + + if Length(Answer) > 0 then begin + Result := cRCodeQueryCacheOK; + Exit; + end; + + InternalSearch(DNSHeader, Question, TypeCode_Error, Answer, True, True, False); + if BytesToString(Answer) = 'Error' then begin {do not localize} + Result := cRCodeQueryCacheFindError; + Exit; + end; + + ExternalSearch(DNSResolver, DNSHeader, OriginalQuestion, Answer); + if Length(Answer) > 0 then begin + Result := cRCodeQueryReturned; + end else begin + Result := cRCodeQueryNotImplement; + end; + end +end; + +procedure TIdDNS_UDPServer.InitComponent; +begin + inherited InitComponent; + + FRootDNS_NET := TStringList.Create; + FRootDNS_NET.Add('209.92.33.150'); // nic.net {do not localize} + FRootDNS_NET.Add('209.92.33.130'); // nic.net {do not localize} + FRootDNS_NET.Add('203.37.255.97'); // apnic.net {do not localize} + FRootDNS_NET.Add('202.12.29.131'); // apnic.net {do not localize} + FRootDNS_NET.Add('12.29.20.2'); // nanic.net {do not localize} + FRootDNS_NET.Add('204.145.119.2'); // nanic.net {do not localize} + FRootDNS_NET.Add('140.111.1.2'); // a.twnic.net.tw {do not localize} + + FCached_Tree := TIdDNTreeNode.Create(nil); + FCached_Tree.AutoSortChild := True; + FCached_Tree.CLabel := '.'; + + FHanded_Tree := TIdDNTreeNode.Create(nil); + FHanded_Tree.AutoSortChild := True; + FHanded_Tree.CLabel := '.'; + + FHanded_DomainList := TStringList.Create; + FZoneMasterFiles := TStringList.Create; + + DefaultPort := IdPORT_DOMAIN; + FCS := TIdCriticalSection.Create; + FGlobalCS := TIdCriticalSection.Create; + FBusy := False; +end; + +destructor TIdDNS_UDPServer.Destroy; +begin + FreeAndNil(FCached_Tree); + FreeAndNil(FHanded_Tree); + FreeAndNil(FRootDNS_NET); + FreeAndNil(FHanded_DomainList); + FreeAndNil(FZoneMasterFiles); + FreeAndNil(FCS); + FreeAndNil(FGlobalCS); + inherited Destroy; +end; + +procedure TIdDNS_UDPServer.DoAfterQuery(ABinding: TIdSocketHandle; + ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode : String; + Query : TIdBytes); +begin + if Assigned(FOnAfterQuery) then begin + FOnAfterQuery(ABinding, ADNSHeader, QueryResult, ResultCode, Query); + end; +end; + +procedure TIdDNS_UDPServer.DoBeforeQuery(ABinding: TIdSocketHandle; + ADNSHeader: TDNSHeader; var ADNSQuery: TIdBytes); +begin + if Assigned(FOnBeforeQuery) then begin + FOnBeforeQuery(ABinding, ADNSHeader, ADNSQuery); + end; +end; + +procedure TIdDNS_UDPServer.ExternalSearch(ADNSResolver : TIdDNSResolver; + Header: TDNSHeader; Question: TIdBytes; var Answer: TIdBytes); +var + Server_Index : Integer; + MyDNSResolver : TIdDNSResolver; +begin + if RootDNS_NET.Count = 0 then begin + Exit; + end; + Server_Index := 0; + if ADNSResolver = nil then begin + MyDNSResolver := TIdDNSResolver.Create(Self); + MyDNSResolver.WaitingTime := 5000; + end else begin + MyDNSResolver := ADNSResolver; + end; + try + repeat + MyDNSResolver.Host := RootDNS_NET.Strings[Server_Index]; + try + MyDNSResolver.InternalQuery := Question; + MyDNSResolver.Resolve(''); + Answer := MyDNSResolver.PlainTextResult; + except + // Todo: Create DNS server interal resolver error. + on EIdDnsResolverError do begin + //Empty Event, for user to custom the event handle. + end; + on EIdSocketError do begin + end; + + else + begin + end; + end; + + Inc(Server_Index); + until (Server_Index >= RootDNS_NET.Count) or (Length(Answer) > 0); + finally + if ADNSResolver = nil then begin + FreeAndNil(MyDNSResolver); + end; + end; +end; + +function TIdDNS_UDPServer.FindHandedNodeByName(QName: String; QType: UInt16): TIdDNTreeNode; +begin + Result := SearchTree(Handed_Tree, QName, QType); +end; + +function TIdDNS_UDPServer.FindNodeFullName(Root: TIdDNTreeNode; QName: String; QType : UInt16): string; +var + MyNode : TIdDNTreeNode; +begin + MyNode := SearchTree(Root, QName, QType); + if MyNode <> nil then begin + Result := MyNode.FullName; + end else begin + Result := ''; + end; +end; + +function TIdDNS_UDPServer.LoadZoneFromMasterFile(MasterFileName: String): Boolean; +var + FileStrings : TStrings; +begin + {MakeTagList;} + Result := FileExists(MasterFileName); + + if Result then begin + FileStrings := TStringList.Create; + try + Todo('LoadZoneFromMasterFile() method of TIdDNS_UDPServer class is not implemented yet'); {do not localize} +// FileStrings.LoadFromFile(MasterFileName); + Result := LoadZoneStrings(FileStrings, MasterFileName, Handed_Tree); + finally + FreeAndNil(FileStrings); + end; + end; + {FreeTagList;} +end; + +function TIdDNS_UDPServer.LoadZoneStrings(FileStrings: TStrings; Filename : String; + TreeRoot : TIdDNTreeNode): Boolean; +var + TagList : TStrings; + + function IsMSDNSFileName(theFileName : String; var DN: string) : Boolean; + var + namepart : TStrings; + Fullname : string; + Count : Integer; + begin + Fullname := theFilename; + repeat + if Pos('\', Fullname) > 0 then begin + Fetch(Fullname, '\'); + end; + until Pos('\', Fullname) = 0; + + namepart := TStringList.Create; + try + repeat + namepart.Add(Fetch(Fullname, '.')); + until Fullname = ''; + + Result := namepart.Strings[namepart.Count-1] = 'dns'; {do not localize} + if Result then begin + Count := 0; + DN := namepart.Strings[Count]; + repeat + Inc(Count); + if Count <= namepart.Count -2 then begin + DN := DN + '.' + namepart.Strings[Count]; + end; + until Count >= (namepart.Count-2); + end; + finally + FreeAndNil(namepart); + end; + end; + + procedure MakeTagList; + begin + TagList := TStringList.Create; + try + TagList.Add(cAAAA); + TagList.Add(cA); + TagList.Add(cNS); + TagList.Add(cMD); + TagList.Add(cMF); + TagList.Add(cCName); + TagList.Add(cSOA); + TagList.Add(cMB); + TagList.Add(cMG); + TagList.Add(cMR); + TagList.Add(cNULL); + TagList.Add(cWKS); + TagList.Add(cPTR); + TagList.Add(cHINFO); + TagList.Add(cMINFO); + TagList.Add(cMX); + TagList.Add(cTXT); + + // The Following Tags are used in master file, but not Resource Record. + TagList.Add(cOrigin); + TagList.Add(cInclude); + //TagList.Add(cAt); + except + FreeAndNil(TagList); + raise; + end; + end; + + procedure FreeTagList; + begin + FreeAndNil(TagList); + end; + + function ClearDoubleQutoa(Strs : TStrings): Boolean; + var + SSCount : Integer; + Mark, Found : Boolean; + begin + SSCount := 0; + Mark := False; + + while SSCount <= (Strs.Count-1) do begin + Found := Pos('"', Strs.Strings[SSCount]) > 0; + while Found do begin + Mark := Mark xor Found; + Strs.Strings[SSCount] := ReplaceSpecString(Strs.Strings[SSCount], '"', '', False); + Found := Pos('"', Strs.Strings[SSCount]) > 0; + end; + + if not Mark then begin + Inc(SSCount); + end else begin + Strs.Strings[SSCount] := Strs.Strings[SSCount] + ' ' + Strs.Strings[SSCount + 1]; + Strs.Delete(SSCount + 1); + end; + end; + + Result := not Mark; + end; + + function IsValidMasterFile : Boolean; + var + EachLinePart : TStrings; + CurrentLineNum, TagField, Count : Integer; + LineData, DataBody, {Comment,} FPart, LTag : string; + Denoted, Stop, PassQuota : Boolean; + begin + EachLinePart := TStringList.Create; + try + CurrentLineNum := 0; + Stop := False; + // Check Denoted; + Denoted := false; + + if FileStrings.Count > 0 then begin + repeat + LineData := Trim(FileStrings.Strings[CurrentLineNum]); + DataBody := Fetch(LineData, ';'); + //Comment := LineData; + PassQuota := Pos('(', DataBody) = 0; + + // Split each item into TStrings. + repeat + if not PassQuota then begin + Inc(CurrentLineNum); + LineData := Trim(FileStrings.Strings[CurrentLineNum]); + DataBody := DataBody + ' ' + Fetch(LineData, ';'); + PassQuota := Pos(')', DataBody) > 0; + end; + until PassQuota or (CurrentLineNum > (FileStrings.Count-1)); + + Stop := not PassQuota; + + if not Stop then begin + EachLinePart.Clear; + DataBody := ReplaceSpecString(DataBody, '(', ''); + DataBody := ReplaceSpecString(DataBody, ')', ''); + + repeat + DataBody := Trim(DataBody); + FPart := Fetch(DataBody, #9); + + repeat + FPart := Trim(FPart); + LTag := Fetch(FPart,' '); + + if (LTag <> '') and (LTag <> '(') and (LTag <> ')') then begin + EachLinePart.Add(LTag); + end; + until FPart = ''; + until DataBody = ''; + + if not Denoted then begin + if EachLinePart.Count > 1 then begin + Denoted := (EachLinePart.Strings[0] = cOrigin) or (EachLinePart.IndexOf(cSOA) <> -1); + end else begin + Denoted := False; + end; + end; + + // Check Syntax; + if not ((EachLinePart.Count > 0) and (EachLinePart.Strings[0] = cOrigin)) then + begin + if not Denoted then begin + if EachLinePart.Count > 0 then begin + Stop := (EachLinePart.Count > 0) and (EachLinePart.IndexOf(cSOA) = -1); + end else begin + Stop := False; + end; + end else begin + //TagField := -1; + //FieldCount := 0; + + // Search Tag Named 'IN'; + TagField := EachLinePart.IndexOf('IN'); {do not localize} + + if TagField = -1 then begin + Count := 0; + repeat + if EachLinePart.Count > 0 then begin + TagField := TagList.IndexOf(EachLinePart.Strings[Count]); + end; + Inc(Count); + until (Count >= EachLinePart.Count -1) or (TagField <> -1); + + if TagField <> -1 then begin + TagField := Count; + end; + end else begin + if TagList.IndexOf(EachLinePart.Strings[TagField + 1]) = -1 then begin + TagField := -1; + end else begin + Inc(TagField); + end; + end; + + if TagField > -1 then begin + case TagList.IndexOf(EachLinePart.Strings[TagField]) of + // Check ip + TypeCode_A : Stop := not IsValidIP(EachLinePart.Strings[TagField + 1]); + // Check ip v6 + 0 : Stop := not IsValidIPv6(EachLinePart.Strings[TagField + 1]); + + // Check Domain Name + TypeCode_CName, TypeCode_NS, TypeCode_MR, + TypeCode_MD, TypeCode_MB, TypeCode_MG, + TypeCode_MF: Stop := not IsHostName(EachLinePart.Strings[TagField + 1]); + + // Can be anything + TypeCode_TXT, TypeCode_NULL: Stop := False; + + // Must be FQDN. + TypeCode_PTR: Stop := not IsFQDN(EachLinePart.Strings[TagField + 1]); + + // HINFO should has 2 fields : CPU and OS. but TStrings + // is 0 base, so that we have to minus one + TypeCode_HINFO: + begin + Stop := not (ClearDoubleQutoa(EachLinePart) and + ((EachLinePart.Count - TagField - 1) = 2)); + end; + + // Check RMailBX and EMailBX but TStrings + // is 0 base, so that we have to minus one + TypeCode_MINFO: + begin + Stop := ((EachLinePart.Count - TagField - 1) <> 2); + if not Stop then begin + Stop := not (IsHostName(EachLinePart.Strings[TagField + 1]) and + IsHostName(EachLinePart.Strings[TagField + 2])); + end; + end; + + // Check Pref(Numeric) and Exchange. but TStrings + // is 0 base, so that we have to minus one + TypeCode_MX: + begin + Stop := ((EachLinePart.Count - TagField - 1) <> 2); + if not Stop then begin + Stop := not (IsNumeric(EachLinePart.Strings[TagField + 1]) and + IsHostName(EachLinePart.Strings[TagField + 2])); + end; + end; + + // TStrings is 0 base, so that we have to minus one + TypeCode_SOA: + begin + Stop := ((EachLinePart.Count - TagField - 1) <> 7); + if not Stop then begin + Stop := not (IsHostName(EachLinePart.Strings[TagField + 1]) and + IsHostName(EachLinePart.Strings[TagField + 2]) and + IsNumeric(EachLinePart.Strings[TagField + 3]) and + IsNumeric(EachLinePart.Strings[TagField + 4]) and + IsNumeric(EachLinePart.Strings[TagField + 5]) and + IsNumeric(EachLinePart.Strings[TagField + 6]) and + IsNumeric(EachLinePart.Strings[TagField + 7]) + ); + end; + end; + + TypeCode_WKS: Stop := ((EachLinePart.Count - TagField) = 1); + end; + end else begin + if EachLinePart.Count > 0 then + Stop := True; + end; + end; + end; + end; + Inc(CurrentLineNum); + until (CurrentLineNum > (FileStrings.Count-1)) or Stop; + end; + Result := not Stop; + finally + FreeAndNil(EachLinePart); + end; + end; + + function LoadMasterFile : Boolean; + var + Checks, EachLinePart, DenotedDomain : TStrings; + CurrentLineNum, TagField, Count, LastTTL : Integer; + LineData, DataBody, FPart, LTag, LText, + RName, LastDenotedDomain, LastTag, NewDomain, SingleHostName {CH: , PrevDNTag} : string; + Stop, PassQuota, Found {, canChangPrevDNTag } : Boolean; + LLRR_A : TIdRR_A; + LLRR_AAAA : TIdRR_AAAA; + LLRR_NS : TIdRR_NS; + LLRR_MB : TIdRR_MB; + LLRR_Name : TIdRR_CName; + LLRR_SOA : TIdRR_SOA; + LLRR_MG : TIdRR_MG; + LLRR_MR : TIdRR_MR; + LLRR_PTR : TIdRR_PTR; + LLRR_HINFO : TIdRR_HINFO; + LLRR_MINFO : TIdRR_MINFO; + LLRR_MX : TIdRR_MX; + LLRR_TXT : TIdRR_TXT; + begin + EachLinePart := TStringList.Create; + try + DenotedDomain := TStringList.Create; + try + CurrentLineNum := 0; + LastDenotedDomain := ''; + LastTag := ''; + NewDomain := ''; + // PrevDNTag := ''; + Stop := False; + //canChangPrevDNTag := True; + + if IsMSDNSFileName(FileName, LastDenotedDomain) then begin + //canChangPrevDNTag := False; + Filename := Uppercase(Filename); + end else begin + LastDenotedDomain := ''; + end; + + if FileStrings.Count > 0 then begin + repeat + LineData := Trim(FileStrings.Strings[CurrentLineNum]); + DataBody := Fetch(LineData, ';'); + // Comment := LineData; + PassQuota := Pos('(', DataBody) = 0; + + // Split each item into TStrings. + repeat + if not PassQuota then begin + Inc(CurrentLineNum); + LineData := Trim(FileStrings.Strings[CurrentLineNum]); + DataBody := DataBody + ' ' + Fetch(LineData, ';'); + PassQuota := Pos(')', DataBody) > 0; + end; + until PassQuota; + + EachLinePart.Clear; + DataBody := ReplaceSpecString(DataBody, '(', ''); + DataBody := ReplaceSpecString(DataBody, ')', ''); + repeat + DataBody := Trim(DataBody); + FPart := Fetch(DataBody, #9); + + repeat + FPart := Trim(FPart); + if Pos('"', FPart) = 1 then begin + Fetch(FPart, '"'); + LText := Fetch(FPart, '"'); + EachLinePart.Add(LText); + end; + + LTag := Fetch(FPart, ' '); + if (TagList.IndexOf(LTag) = -1) and (LTag <> 'IN') then begin {do not localize} + LTag := LowerCase(LTag); + end; + + if (LTag <> '') and (LTag <> '(') and (LTag <> ')') then begin + EachLinePart.Add(LTag); + end; + until FPart = ''; + until DataBody = ''; + + if EachLinePart.Count > 0 then begin + if EachLinePart.Strings[0] = cOrigin then begin + // One Domain is found. + NewDomain := EachLinePart.Strings[1]; + if TextEndsWith(NewDomain, '.') then begin + LastDenotedDomain := NewDomain; + NewDomain := ''; + end else begin + LastDenotedDomain := NewDomain + '.' + LastDenotedDomain; + NewDomain := ''; + end; + end else begin + // Search RR Type Tag; + Count := 0; + TagField := -1; + + repeat + Found := TagList.IndexOf(EachLinePart.Strings[Count]) > -1; + if Found then begin + TagField := Count; + end; + Inc(Count); + until Found or (Count > (EachLinePart.Count-1)); + + // To initialize LastTTL; + LastTTL := 86400; + if TagField > -1 then begin + case TagField of + 1 : + if EachLinePart.Strings[0] <> 'IN' then begin {do not localize} +// canChangPrevDNTag := True; + LastTag := EachLinePart.Strings[0]; + if EachLinePart.Strings[TagField] <> 'SOA' then begin {do not localize} + // PrevDNTag := ''; + end else begin + LastTTL := IndyStrToInt(EachLinePart.Strings[TagField + 6]); + end; +// end else begin +// canChangPrevDNTag := False; + end; + 2 : + if EachLinePart.Strings[1] = 'IN' then begin {do not localize} + LastTag := EachLinePart.Strings[0]; +// canChangPrevDNTag := True; + if EachLinePart.Strings[TagField] <> 'SOA' then begin {do not localize} + // PrevDNTag := ''; + end else begin + LastTTL := IndyStrToInt(EachLinePart.Strings[TagField + 6]); + end; + end else begin +// canChangPrevDNTag := False; + end; + else + begin +// canChangPrevDNTag := False; + LastTTL := 86400; + end; + end; + + //if (EachLinePart.Strings[0] = cAt) or (PrevDNTag = 'SOA') then + if EachLinePart.Strings[0] = cAt then begin + SingleHostName := LastDenotedDomain + end else begin + if LastTag = cAt then begin + LastTag := SingleHostName; + end; + if not TextEndsWith(LastTag, '.') then begin + SingleHostName := LastTag + '.' + LastDenotedDomain + end else begin + SingleHostName := LastTag; + end; + end; + + case TagList.IndexOf(EachLinePart.Strings[TagField]) of + // Check ip + TypeCode_A : + begin + LLRR_A := TIdRR_A.Create; + LLRR_A.RRName := SingleHostName; + LLRR_A.Address := EachLinePart.Strings[TagField + 1]; + LLRR_A.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_A); + // if canChangPrevDNTag then begin + // PrevDNTag := 'A'; + // end; + end; + + // Check IPv6 ip address 10/29,2002 + 0 : + begin + LLRR_AAAA := TIdRR_AAAA.Create; + LLRR_AAAA.RRName := SingleHostName; + LLRR_AAAA.Address := ConvertToValidv6IP(EachLinePart.Strings[TagField + 1]); + LLRR_AAAA.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_AAAA); + // if canChangPrevDNTag then begin + // PrevDNTag := 'AAAA'; {do not localize} + // end; + end; + + // Check Domain Name + TypeCode_CName: + begin + LLRR_Name := TIdRR_CName.Create; + LLRR_Name.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_Name.CName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_Name.CName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_Name.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_Name); + // if canChangPrevDNTag then begin + // PrevDNTag := 'CNAME'; {do not localize} + // end; + end; + + TypeCode_NS : + begin + LLRR_NS := TIdRR_NS.Create; + LLRR_NS.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_NS.NSDName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_NS.NSDName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_NS.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_NS); + // if canChangPrevDNTag then begin + // PrevDNTag := 'NS'; {do not localize} + // end; + end; + + TypeCode_MR : + begin + LLRR_MR := TIdRR_MR.Create; + LLRR_MR.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_MR.NewName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_MR.NewName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_MR.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_MR); + // if canChangPrevDNTag then begin + // PrevDNTag := 'MR'; {do not localize} + // end; + end; + + TypeCode_MD, TypeCode_MB, TypeCode_MF : + begin + LLRR_MB := TIdRR_MB.Create; + LLRR_MB.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_MB.MADName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_MB.MADName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_MB.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_MB); + // if canChangPrevDNTag then begin + // PrevDNTag := 'MF'; {do not localize} + // end; + end; + + TypeCode_MG : + begin + LLRR_MG := TIdRR_MG.Create; + LLRR_MG.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_MG.MGMName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_MG.MGMName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_MG.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_MG); + // if canChangPrevDNTag then begin + // PrevDNTag := 'MG'; {do not localize} + // end; + end; + + // Can be anything + TypeCode_TXT, TypeCode_NULL: + begin + LLRR_TXT := TIdRR_TXT.Create; + LLRR_TXT.RRName := SingleHostName; + LLRR_TXT.TXT := EachLinePart.Strings[TagField + 1]; + LLRR_TXT.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_TXT); + // if canChangPrevDNTag then begin + // PrevDNTag := 'TXT'; {do not localize} + // end; + end; + + // Must be FQDN. + TypeCode_PTR: + begin + LLRR_PTR := TIdRR_PTR.Create; + LLRR_PTR.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_PTR.PTRDName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_PTR.PTRDName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + LLRR_PTR.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_PTR); + // if canChangPrevDNTag then begin + // PrevDNTag := 'PTR'; {do not localize} + // end; + end; + + // HINFO should has 2 fields : CPU and OS. but TStrings + // is 0 base, so that we have to minus one + TypeCode_HINFO: + begin + ClearDoubleQutoa(EachLinePart); + + LLRR_HINFO := TIdRR_HINFO.Create; + LLRR_HINFO.RRName := SingleHostName; + LLRR_HINFO.CPU := EachLinePart.Strings[TagField + 1]; + LLRR_HINFO.OS := EachLinePart.Strings[TagField + 2]; + LLRR_HINFO.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_HINFO); + // if canChangPrevDNTag then begin + // PrevDNTag := 'HINFO'; {do not localize} + // end; + end; + + // Check RMailBX and EMailBX but TStrings + // is 0 base, so that we have to minus one + TypeCode_MINFO: + begin + LLRR_MINFO := TIdRR_MINFO.Create; + LLRR_MINFO.RRName := SingleHostName; + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_MINFO.Responsible_Mail := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_MINFO.Responsible_Mail := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + + if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin + LLRR_MINFO.ErrorHandle_Mail := EachLinePart.Strings[TagField + 2]; + end else begin + LLRR_MINFO.ErrorHandle_Mail := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain; + end; + + LLRR_MINFO.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_MINFO); + // if canChangPrevDNTag then begin + // PrevDNTag := 'MINFO'; {do not localize} + // end; + end; + + // Check Pref(Numeric) and Exchange. but TStrings + // is 0 base, so that we have to minus one + TypeCode_MX: + begin + LLRR_MX := TIdRR_MX.Create; + LLRR_MX.RRName := SingleHostName; + LLRR_MX.Preference := EachLinePart.Strings[TagField + 1]; + if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin + LLRR_MX.Exchange := EachLinePart.Strings[TagField + 2]; + end else begin + LLRR_MX.Exchange := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain; + end; + LLRR_MX.TTL := LastTTL; + + UpdateTree(TreeRoot, LLRR_MX); + // if canChangPrevDNTag then begin + // PrevDNTag := 'MX'; {do not localize} + // end; + end; + + // TStrings is 0 base, so that we have to minus one + TypeCode_SOA: + begin + LLRR_SOA := TIdRR_SOA.Create; + + if TextEndsWith(EachLinePart.Strings[TagField + 1], '.') then begin + LLRR_SOA.MName := EachLinePart.Strings[TagField + 1]; + end else begin + LLRR_SOA.MName := EachLinePart.Strings[TagField + 1] + '.' + LastDenotedDomain; + end; + + //LLRR_SOA.RRName:= LLRR_SOA.MName; + if (SingleHostName = '') and (LastDenotedDomain = '') then begin + {$IFDEF STRING_IS_UNICODE} + LastDenotedDomain := String(LLRR_SOA.MName); // explicit convert to Unicode + {$ELSE} + LastDenotedDomain := LLRR_SOA.MName; + {$ENDIF} + Fetch(LastDenotedDomain, '.'); + SingleHostName := LastDenotedDomain; + end; + LLRR_SOA.RRName := SingleHostName; + + // Update the Handed List + { + if Handed_DomainList.IndexOf(LLRR_SOA.MName) = -1 then begin + Handed_DomainList.Add(LLRR_SOA.MName); + end; + } + if Handed_DomainList.IndexOf(LLRR_SOA.RRName) = -1 then begin + Handed_DomainList.Add(LLRR_SOA.RRName); + end; + + { + if DenotedDomain.IndexOf(LLRR_SOA.MName) = -1 then begin + DenotedDomain.Add(LLRR_SOA.MName); + end; + LastDenotedDomain := LLRR_SOA.MName; + } + + if DenotedDomain.IndexOf(LLRR_SOA.RRName) = -1 then begin + DenotedDomain.Add(LLRR_SOA.RRName); + end; + //LastDenotedDomain := LLRR_SOA.RRName; + + if TextEndsWith(EachLinePart.Strings[TagField + 2], '.') then begin + LLRR_SOA.RName := EachLinePart.Strings[TagField + 2]; + end else begin + LLRR_SOA.RName := EachLinePart.Strings[TagField + 2] + '.' + LastDenotedDomain; + end; + + Checks := TStringList.Create; + try + {$IFDEF STRING_IS_UNICODE} + RName := String(LLRR_SOA.RName); // explicit convert to Unicode + {$ELSE} + RName := LLRR_SOA.RName; + {$ENDIF} + + while RName <> '' do begin + Checks.Add(Fetch(RName, '.')); + end; + + RName := ''; + For Count := 0 to Checks.Count -1 do begin + if Checks.Strings[Count] <> '' then begin + RName := RName + Checks.Strings[Count] + '.'; + end; + end; + + LLRR_SOA.RName := RName; + finally + FreeAndNil(Checks); + end; + + LLRR_SOA.Serial := EachLinePart.Strings[TagField + 3]; + LLRR_SOA.Refresh := EachLinePart.Strings[TagField + 4]; + LLRR_SOA.Retry := EachLinePart.Strings[TagField + 5]; + LLRR_SOA.Expire := EachLinePart.Strings[TagField + 6]; + LLRR_SOA.Minimum := EachLinePart.Strings[TagField + 7]; + LastTTL := IndyStrToInt(LLRR_SOA.Expire); + LLRR_SOA.TTL := LastTTL; + UpdateTree(TreeRoot, LLRR_SOA); + + // if canChangPrevDNTag then begin + // PrevDNTag := 'SOA'; {do not localize} + // end; + end; + + TypeCode_WKS: + begin + // if canChangPrevDNTag then begin + // PrevDNTag := 'WKS'; {do not localize} + // end; + end; + end; + end; + end; // if EachLinePart.Count == 0 => Only Comment + end; + Inc(CurrentLineNum); + until (CurrentLineNum > (FileStrings.Count -1)); + end; + Result := not Stop; + finally + FreeAndNil(DenotedDomain); + end; + finally + FreeAndNil(EachLinePart); + end; + end; + +begin + MakeTagList; + try + Result := IsValidMasterFile; + // IsValidMasterFile is used in local, so I design with not + // any parameter. + if Result then begin + Result := LoadMasterFile; + end; + finally + FreeTagList; + end; +end; + +procedure TIdDNS_UDPServer.SaveToCache(ResourceRecord: TIdBytes; QueryName : string; OriginalQType : UInt16); +var + TempResolver : TIdDNSResolver; + Count : Integer; +begin + TempResolver := TIdDNSResolver.Create(nil); + try + // RLebeau: FillResultWithOutCheckId() is deprecated, but not using FillResult() + // here yet because it validates the DNSHeader.RCode, and I do not know if that + // is needed here. I don't want to break this logic... + TempResolver.FillResultWithOutCheckId(ResourceRecord); + if TempResolver.DNSHeader.ANCount > 0 then begin + for Count := 0 to TempResolver.QueryResult.Count - 1 do begin + UpdateTree(Cached_Tree, TempResolver.QueryResult.Items[Count]); + end; + end; + finally + FreeAndNil(TempResolver); + end; +end; + +function TIdDNS_UDPServer.SearchTree(Root: TIdDNTreeNode; QName: String; QType : UInt16): TIdDNTreeNode; +var + RRIndex : integer; + NodeCursor : TIdDNTreeNode; + NameLabels : TStrings; + OneNode, FullName : string; + Found : Boolean; +begin + Result := nil; + NameLabels := TStringList.Create; + try + FullName := QName; + NodeCursor := Root; + Found := False; + + repeat + OneNode := Fetch(FullName, '.'); + if OneNode <> '' then begin + NameLabels.Add(OneNode); + end; + until FullName = ''; + + repeat + if QType <> TypeCode_SOA then begin + RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]); + if RRIndex <> -1 then begin + NameLabels.Delete(NameLabels.Count - 1); + NodeCursor := NodeCursor.Children[RRIndex]; + + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + end else begin + Found := NameLabels.Count = 0; + end; + end else begin + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + if not Found then begin + NameLabels.Clear; + end; + end else begin + NameLabels.Clear; + end; + end; + end else begin + RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]); + if RRIndex <> -1 then begin + NameLabels.Delete(NameLabels.Count - 1); + NodeCursor := NodeCursor.Children[RRIndex]; + + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + end else begin + Found := NameLabels.Count = 0; + end; + end else begin + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + if not Found then begin + NameLabels.Clear; + end; + end else begin + NameLabels.Clear; + end; + end; + end; + until (NameLabels.Count = 0) or Found; + + if Found then begin + Result := NodeCursor; + end; + finally + FreeAndNil(NameLabels); + end; +end; + +procedure TIdDNS_UDPServer.SetHanded_DomainList(const Value: TStrings); +begin + FHanded_DomainList.Assign(Value); +end; + +procedure TIdDNS_UDPServer.SetRootDNS_NET(const Value: TStrings); +begin + FRootDNS_NET.Assign(Value); +end; + +procedure TIdDNS_UDPServer.SetZoneMasterFiles(const Value: TStrings); +begin + FZoneMasterFiles.Assign(Value); +end; + +procedure TIdDNS_UDPServer.UpdateTree(TreeRoot: TIdDNTreeNode; RR: TResultRecord); +var + NameNode : TStrings; + RRName, APart : String; + Count, NodeIndex : Integer; + NodeCursor : TIdDNTreeNode; + LRR_A : TIdRR_A; + LRR_AAAA : TIdRR_AAAA; + LRR_NS : TIdRR_NS; + LRR_MB : TIdRR_MB; + LRR_Name : TIdRR_CName; + LRR_SOA : TIdRR_SOA; + LRR_MG : TIdRR_MG; + LRR_MR : TIdRR_MR; + LRR_PTR : TIdRR_PTR; + LRR_HINFO : TIdRR_HINFO; + LRR_MINFO : TIdRR_MINFO; + LRR_MX : TIdRR_MX; + LRR_TXT : TIdRR_TXT; +begin + RRName := RR.Name; + + NameNode := TStringList.Create; + try + repeat + APart := Fetch(RRName, '.'); + if APart <> '' then begin + NameNode.Add(APart); + end; + until RRName = ''; + + NodeCursor := TreeRoot; + RRName := RR.Name; + if not TextEndsWith(RRName, '.') then begin + RRName := RRName + '.'; + end; + if (RR.RecType <> qtSOA) and (Handed_DomainList.IndexOf(LowerCase(RRName)) = -1) and (RR.RecType <> qtNS) then begin + for Count := NameNode.Count-1 downto 1 do begin + NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]); + if NodeIndex = -1 then begin + NodeCursor := NodeCursor.AddChild; + NodeCursor.AutoSortChild := True; + NodeCursor.CLabel := NameNode.Strings[Count]; + end else begin + NodeCursor := NodeCursor.Children[NodeIndex]; + end; + end; + RRName := NameNode.Strings[0]; + end else begin + for Count := NameNode.Count-1 downto 0 do begin + NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]); + RRName := NameNode.Strings[Count]; + if NodeIndex = -1 then begin + NodeCursor := NodeCursor.AddChild; + //NodeCursor.CLabel := RRName; + NodeCursor.AutoSortChild := True; + NodeCursor.CLabel := RRName; + end else begin + NodeCursor := NodeCursor.Children[NodeIndex]; + end; + end; + RRName := RR.Name; + end; + + NodeCursor.RRs.ItemNames.Add(RRName); + + case RR.RecType of + qtA : + begin + LRR_A := TIdRR_A.Create; + try + NodeCursor.RRs.Add(LRR_A); + except + LRR_A.Free; + raise; + end; + + LRR_A.RRName := RRName; + LRR_A.Address := TARecord(RR).IPAddress; + LRR_A.TTL := TARecord(RR).TTL; + + if LRR_A.ifAddFullName(NodeCursor.FullName) then begin + LRR_A.RRName := LRR_A.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtAAAA : + begin + LRR_AAAA := TIdRR_AAAA.Create; + try + NodeCursor.RRs.Add(LRR_AAAA); + except + LRR_AAAA.Free; + raise; + end; + + LRR_AAAA.RRName := RRName; + LRR_AAAA.Address := TAAAARecord(RR).Address; + LRR_AAAA.TTL := TAAAARecord(RR).TTL; + + if LRR_AAAA.ifAddFullName(NodeCursor.FullName) then begin + LRR_AAAA.RRName := LRR_AAAA.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtNS: + begin + LRR_NS := TIdRR_NS.Create; + try + NodeCursor.RRs.Add(LRR_NS); + except + LRR_NS.Free; + raise; + end; + + LRR_NS.RRName := RRName; + LRR_NS.NSDName := TNSRecord(RR).HostName; + LRR_NS.TTL := TNSRecord(RR).TTL; + + if LRR_NS.ifAddFullName(NodeCursor.FullName) then begin + LRR_NS.RRName := LRR_NS.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtMD, qtMF, qtMB: + begin + LRR_MB := TIdRR_MB.Create; + try + NodeCursor.RRs.Add(LRR_MB); + except + LRR_MB.Free; + raise; + end; + + LRR_MB.RRName := RRName; + LRR_MB.MADName := TNAMERecord(RR).HostName; + LRR_MB.TTL := TNAMERecord(RR).TTL; + + if LRR_MB.ifAddFullName(NodeCursor.FullName) then begin + LRR_MB.RRName := LRR_MB.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtName: + begin + LRR_Name := TIdRR_CName.Create; + try + NodeCursor.RRs.Add(LRR_Name); + except + LRR_Name.Free; + raise; + end; + + LRR_Name.RRName := RRName; + LRR_Name.CName := TNAMERecord(RR).HostName; + LRR_Name.TTL:= TNAMERecord(RR).TTL; + + if LRR_Name.ifAddFullName(NodeCursor.FullName) then begin + LRR_Name.RRName := LRR_Name.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtSOA: + begin + LRR_SOA := TIdRR_SOA.Create; + try + NodeCursor.RRs.Add(LRR_SOA); + except + LRR_SOA.Free; + raise; + end; + + LRR_SOA.RRName := RRName; + + LRR_SOA.MName := TSOARecord(RR).Primary; + LRR_SOA.RName := TSOARecord(RR).ResponsiblePerson; + LRR_SOA.Serial := IntToStr(TSOARecord(RR).Serial); + LRR_SOA.Minimum := IntToStr(TSOARecord(RR).MinimumTTL); + LRR_SOA.Refresh := IntToStr(TSOARecord(RR).Refresh); + LRR_SOA.Retry := IntToStr(TSOARecord(RR).Retry); + LRR_SOA.Expire := IntToStr(TSOARecord(RR).Expire); + LRR_SOA.TTL:= TSOARecord(RR).TTL; + + if LRR_SOA.ifAddFullName(NodeCursor.FullName) then begin + LRR_SOA.RRName := LRR_SOA.RRName + '.'+ NodeCursor.FullName; + end + else if not TextEndsWith(LRR_SOA.RRName, '.') then begin + LRR_SOA.RRName := LRR_SOA.RRName + '.'; + end; + end; + qtMG : + begin + LRR_MG := TIdRR_MG.Create; + try + NodeCursor.RRs.Add(LRR_MG); + except + LRR_MG.Free; + raise; + end; + + LRR_MG.RRName := RRName; + LRR_MG.MGMName := TNAMERecord(RR).HostName; + LRR_MG.TTL := TNAMERecord(RR).TTL; + + if LRR_MG.ifAddFullName(NodeCursor.FullName) then begin + LRR_MG.RRName := LRR_MG.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtMR : + begin + LRR_MR := TIdRR_MR.Create; + try + NodeCursor.RRs.Add(LRR_MR); + except + LRR_MR.Free; + raise; + end; + + LRR_MR.RRName := RRName; + LRR_MR.NewName := TNAMERecord(RR).HostName; + LRR_MR.TTL := TNAMERecord(RR).TTL; + + if LRR_MR.ifAddFullName(NodeCursor.FullName) then begin + LRR_MR.RRName := LRR_MR.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtWKS: + begin + end; + qtPTR: + begin + LRR_PTR := TIdRR_PTR.Create; + try + NodeCursor.RRs.Add(LRR_PTR); + except + LRR_PTR.Free; + raise; + end; + + LRR_PTR.RRName := RRName; + LRR_PTR.PTRDName := TPTRRecord(RR).HostName; + LRR_PTR.TTL := TPTRRecord(RR).TTL; + + if LRR_PTR.ifAddFullName(NodeCursor.FullName) then begin + LRR_PTR.RRName := LRR_PTR.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtHINFO: + begin + LRR_HINFO := TIdRR_HINFO.Create; + try + NodeCursor.RRs.Add(LRR_HINFO); + except + LRR_HINFO.Free; + raise; + end; + + LRR_HINFO.RRName := RRName; + LRR_HINFO.CPU := THINFORecord(RR).CPU; + LRR_HINFO.OS := THINFORecord(RR).OS; + LRR_HINFO.TTL := THINFORecord(RR).TTL; + + if LRR_HINFO.ifAddFullName(NodeCursor.FullName) then begin + LRR_HINFO.RRName := LRR_HINFO.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtMINFO: + begin + LRR_MINFO := TIdRR_MINFO.Create; + try + NodeCursor.RRs.Add(LRR_MINFO); + except + LRR_MINFO.Free; + raise; + end; + + LRR_MINFO.RRName := RRName; + LRR_MINFO.Responsible_Mail := TMINFORecord(RR).ResponsiblePersonMailbox; + LRR_MINFO.ErrorHandle_Mail := TMINFORecord(RR).ErrorMailbox; + LRR_MINFO.TTL := TMINFORecord(RR).TTL; + + if LRR_MINFO.ifAddFullName(NodeCursor.FullName) then begin + LRR_MINFO.RRName := LRR_MINFO.RRName + '.' + NodeCursor.FullName; + end; + end; + qtMX: + begin + LRR_MX := TIdRR_MX.Create; + try + NodeCursor.RRs.Add(LRR_MX); + except + LRR_MX.Free; + raise; + end; + + LRR_MX.RRName := RRName; + LRR_MX.Exchange := TMXRecord(RR).ExchangeServer; + LRR_MX.Preference := IntToStr(TMXRecord(RR).Preference); + LRR_MX.TTL := TMXRecord(RR).TTL; + + if LRR_MX.ifAddFullName(NodeCursor.FullName) then begin + LRR_MX.RRName := LRR_MX.RRName + '.'+ NodeCursor.FullName; + end; + end; + qtTXT, qtNULL: + begin + LRR_TXT := TIdRR_TXT.Create; + try + NodeCursor.RRs.Add(LRR_TXT); + except + LRR_TXT.Free; + raise; + end; + + LRR_TXT.RRName := RRName; + LRR_TXT.TXT := TTextRecord(RR).Text.Text; + LRR_TXT.TTL := TTextRecord(RR).TTL; + + if LRR_TXT.ifAddFullName(NodeCursor.FullName) then begin + LRR_TXT.RRName := LRR_TXT.RRName + '.'+ NodeCursor.FullName; + end; + end; + end; + finally + FreeAndNil(NameNode); + end; +end; + +procedure TIdDNS_UDPServer.UpdateTree(TreeRoot: TIdDNTreeNode; RR: TIdTextModeResourceRecord); +var + NameNode : TStrings; + RRName, APart : String; + Count, NodeIndex, RRIndex : Integer; + NodeCursor : TIdDNTreeNode; + LRR_AAAA : TIdRR_AAAA; + LRR_A : TIdRR_A; + LRR_NS : TIdRR_NS; + LRR_MB : TIdRR_MB; + LRR_Name : TIdRR_CName; + LRR_SOA : TIdRR_SOA; + LRR_MG : TIdRR_MG; + LRR_MR : TIdRR_MR; + LRR_PTR : TIdRR_PTR; + LRR_HINFO : TIdRR_HINFO; + LRR_MINFO : TIdRR_MINFO; + LRR_MX : TIdRR_MX; + LRR_TXT : TIdRR_TXT; + LRR_Error : TIdRR_Error; +begin + RRName := RR.RRName; + + NameNode := TStringList.Create; + try + repeat + APart := Fetch(RRName, '.'); + if APart <> '' then begin + NameNode.Add(APart); + end; + until RRName = ''; + + NodeCursor := TreeRoot; + RRName := RR.RRName; + if not TextEndsWith(RRName, '.') then begin + RR.RRName := RR.RRName + '.'; + end; + + // VC: in2002-02-24-1715, it just denoted TIdRR_A and TIdRR_PTR, + // but that make search a domain name RR becoming complex, + // therefor I replace it with all RRs but not TIdRR_SOA + // SOA should own independent node. + if (not (RR is TIdRR_SOA)) and (Handed_DomainList.IndexOf(LowerCase(RR.RRName)) = -1) then begin + for Count := NameNode.Count - 1 downto 1 do begin + NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]); + if NodeIndex = -1 then begin + NodeCursor := NodeCursor.AddChild; + NodeCursor.AutoSortChild := True; + NodeCursor.CLabel := NameNode.Strings[Count]; + end else begin + NodeCursor := NodeCursor.Children[NodeIndex]; + end; + end; + RRName := NameNode.Strings[0]; + end else begin + for Count := NameNode.Count -1 downto 0 do begin + NodeIndex := NodeCursor.ChildIndex.IndexOf(NameNode.Strings[Count]); + RRName := NameNode.Strings[Count]; + if NodeIndex = -1 then begin + NodeCursor := NodeCursor.AddChild; + NodeCursor.AutoSortChild := True; + NodeCursor.CLabel := RRName; + end else begin + NodeCursor := NodeCursor.Children[NodeIndex]; + end; + end; + RRName := RR.RRName; + end; + + RRIndex := NodeCursor.RRs.ItemNames.IndexOf(RRName); + if RRIndex = -1 then begin + NodeCursor.RRs.ItemNames.Add(RRName); + end else begin + repeat + Inc(RRIndex); + if RRIndex > NodeCursor.RRs.ItemNames.Count -1 then begin + RRIndex := -1; + Break; + end; + if NodeCursor.RRs.ItemNames.Strings[RRIndex] <> RRName then begin + Break; + end; + until RRIndex > (NodeCursor.RRs.ItemNames.Count-1); + + if RRIndex = -1 then begin + NodeCursor.RRs.ItemNames.Add(RRName); + end else begin + NodeCursor.RRs.ItemNames.Insert(RRIndex, RRName); + end; + end; + + case RR.TypeCode of + TypeCode_Error : + begin + LRR_Error := TIdRR_Error(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_Error); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_Error); + end; + end; + TypeCode_A : + begin + LRR_A := TIdRR_A(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_A); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_A); + end; + end; + TypeCode_AAAA : + begin + LRR_AAAA := TIdRR_AAAA(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_AAAA); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_AAAA); + end; + end; + TypeCode_NS: + begin + LRR_NS := TIdRR_NS(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_NS); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_NS); + end; + end; + TypeCode_MF: + begin + LRR_MB := TIdRR_MB(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_MB); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_MB); + end; + end; + TypeCode_CName: + begin + LRR_Name := TIdRR_CName(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_Name); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_Name); + end; + end; + TypeCode_SOA: + begin + LRR_SOA := TIdRR_SOA(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_SOA); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_SOA); + end; + end; + TypeCode_MG : + begin + LRR_MG := TIdRR_MG(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_MG); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_MG); + end; + end; + TypeCode_MR : + begin + LRR_MR := TIdRR_MR(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_MR); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_MR); + end; + end; + TypeCode_WKS: + begin + end; + TypeCode_PTR: + begin + LRR_PTR := TIdRR_PTR(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_PTR); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_PTR); + end; + end; + TypeCode_HINFO: + begin + LRR_HINFO := TIdRR_HINFO(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_HINFO); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_HINFO); + end; + end; + TypeCode_MINFO: + begin + LRR_MINFO := TIdRR_MINFO(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_MINFO); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_MINFO); + end; + end; + TypeCode_MX: + begin + LRR_MX := TIdRR_MX(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_MX); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_MX); + end; + end; + TypeCode_TXT, TypeCode_NULL: + begin + LRR_TXT := TIdRR_TXT(RR); + if RRIndex = -1 then begin + NodeCursor.RRs.Add(LRR_TXT); + end else begin + NodeCursor.RRs.Insert(RRIndex, LRR_TXT); + end; + end; + end; + finally + FreeAndNil(NameNode); + end; +end; + +procedure TIdDNS_UDPServer.DoAfterSendBack(ABinding: TIdSocketHandle; + ADNSHeader: TDNSHeader; var QueryResult: TIdBytes; var ResultCode: String; + Query : TIdBytes); +begin + if Assigned(FOnAfterSendBack) then begin + FOnAfterSendBack(ABinding, ADNSHeader, QueryResult, ResultCode, Query); + end; +end; + +function TIdDNS_UDPServer.AXFR(Header : TDNSHeader; Question: string; var Answer: TIdBytes): string; +var + TargetNode : TIdDNTreeNode; + IsMyDomains : Boolean; + RRcount : Integer; + Temp: TIdBytes; +begin + Question := LowerCase(Question); + + IsMyDomains := Handed_DomainList.IndexOf(Question) > -1; + if not IsMyDomains then begin + Fetch(Question, '.'); + IsMyDomains := Handed_DomainList.IndexOf(Question) > -1; + end; + + // Is my domain, go for searching the node. + TargetNode := nil; + SetLength(Answer, 0); + Header.ANCount := 0; + if IsMyDomains then begin + TargetNode := SearchTree(Handed_Tree, Question, TypeCode_SOA); + end; + if IsMyDomains and (TargetNode <> nil) then begin + // combine the AXFR Data(So many) + + RRCount := 0; + Answer := TargetNode.DumpAllBinaryData(RRCount); + Header.ANCount := RRCount; + + Header.QR := iQr_Answer; + Header.AA := iAA_Authoritative; + Header.RCode := iRCodeNoError; + Header.QDCount := 0; + Header.ARCount := 0; + Header.TC := 0; + Temp := Header.GenerateBinaryHeader; + AppendBytes(Temp, Answer); + Answer := Temp; + + Result := cRCodeQueryOK; + end else begin + Header.QR := iQr_Answer; + Header.AA := iAA_Authoritative; + Header.RCode := iRCodeNameError; + Header.QDCount := 0; + Header.ARCount := 0; + Header.TC := 0; + + Answer := Header.GenerateBinaryHeader; + Result := cRCodeQueryNotFound; + end; +end; + +procedure TIdDNS_UDPServer.InternalSearch(Header: TDNSHeader; QName: string; + QType : UInt16; var Answer: TIdBytes; IfMainQuestion : Boolean; + IsSearchCache : Boolean = False; IsAdditional : Boolean = False; + IsWildCard : Boolean = False; WildCardOrgName : string = ''); +var + MoreAddrSearch : TStrings; + TargetNode : TIdDNTreeNode; + Server_Index, RRIndex, Count : Integer; + LocalAnswer, TempBytes, TempAnswer: TIdBytes; + temp_QName, temp: string; + AResult: TIdBytes; + Stop, Extra, IsMyDomains, ifAdditional : Boolean; + LDNSResolver : TIdDNSResolver; + + procedure CheckMoreAddrSearch(const AStr: String); + begin + if (not IsValidIP(AStr)) and IsHostName(AStr) then begin + MoreAddrSearch.Add(AStr); + end; + end; + +begin + SetLength(Answer, 0); + SetLength(Aresult, 0); + // Search the Handed Tree first. + MoreAddrSearch := TStringList.Create; + try + Extra := False; + //Pushed := False; + + if not IsSearchCache then begin + TargetNode := SearchTree(Handed_Tree, QName, QType); + + if TargetNode <> nil then begin //Assemble the Answer. + RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName)); + if RRIndex = -1 then begin + { below are added again by Dennies Chang in 2004/7/15 + { According RFC 1035, a full domain name must be tailed by a '.', + { but in normal behavior, user will not input '.' in last + { position of the full name. So we have to compare both of the + { cases. } + if TextEndsWith(QName, '.') then begin + QName := Copy(QName, 1, Length(QName)-1); + end; + + RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName)); + { above are added again by Dennies Chang in 2004/7/15} + + if RRIndex = -1 then begin + QName := Fetch(QName, '.'); + RRIndex := TargetNode.RRs.ItemNames.IndexOf(LowerCase(QName)); + end; + { marked by Dennies Chang in 2004/7/15 + QName:= Fetch(QName, '.'); + RRIndex := TargetNode.RRs.ItemNames.IndexOf(IndyLowerCase(QName)); + } + end; + + repeat + temp_QName := QName; + SetLength(LocalAnswer, 0); + + if RRIndex <> -1 then begin + case QType of + TypeCode_A: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_A then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_AAAA: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_AAAA then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_NS: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_NS).NSDName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MD: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MF: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_CName: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_CName then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_CName).CName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_SOA: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_SOA then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).MName); + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).RName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MB: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MG: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MG then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MG).MGMName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MR: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MR then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MR).NewName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_NULL: + begin + { + if TargetNode.RRs.Items[RRIndex] is TIdRR_NULL then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + } + end; + TypeCode_WKS: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_WKS then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_PTR: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_PTR then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_HINFO: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_HINFO then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MINFO: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MINFO then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MX: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MX then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MX).Exchange); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_TXT: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_TXT then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_STAR: + begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + + if IsWildCard and (Length(LocalAnswer) > 0) then begin + { + temp := DomainNameToDNSStr(QName+'.'+TargetNode.FullName); + Fetch(LocalAnswer, temp); + } + TempBytes := DomainNameToDNSStr(TargetNode.FullName); + FetchBytes(LocalAnswer, TempBytes); + TempBytes := DomainNameToDNSStr(WildCardOrgName); + AppendBytes(TempBytes, LocalAnswer); + LocalAnswer := TempBytes; + //LocalAnswer := DomainNameToDNSStr(WildCardOrgName) + LocalAnswer; + end; + + if Length(LocalAnswer) > 0 then begin + AppendBytes(Answer, LocalAnswer); + if ((not Extra) and (not IsAdditional)) or (QType = TypeCode_AAAA) then begin + if (TargetNode.RRs.Items[RRIndex] is TIdRR_NS) then begin + if IfMainQuestion then begin + Header.ANCount := Header.ANCount + 1; + end else begin + Header.NSCount := Header.NSCount + 1; + end; + end + else if IfMainQuestion then begin + Header.ANCount := Header.ANCount + 1; + end else begin + Header.ARCount := Header.ARCount + 1; + end; + end + else if IsAdditional then begin + Header.ARCount := Header.ARCount + 1; + end + else begin + Header.ANCount := Header.ANCount + 1; + end; + + Header.Qr := iQr_Answer; + Header.AA := iAA_Authoritative; + Header.RCode := iRCodeNoError; + end; + + if RRIndex < (TargetNode.RRs.ItemNames.Count-1) then begin + Stop := False; + Inc(RRIndex); + end else begin + Stop := True; + end; + end else begin + Stop := True; + end; + + if QName = temp_QName then begin + temp_QName := ''; + end; + until (RRIndex = -1) or + (not ((not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], QName)) xor + (not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], Fetch(temp_QName, '.'))))) + or Stop; + + // Finish the Loop, but n record is found, we need to search if + // there is a widechar record in its subdomain. + // Main, Cache, Additional, Wildcard + if Length(Answer) > 0 then begin + InternalSearch(Header, '*.' + QName, QType, LocalAnswer, IfMAinQuestion, False, False, True, QName); + if LocalAnswer <> nil then begin + AppendBytes(Answer, LocalAnswer); + end; + end; + end else begin // Node can't be found. + MoreAddrSearch.Clear; + end; + + if MoreAddrSearch.Count > 0 then begin + for Count := 0 to MoreAddrSearch.Count -1 do begin + Server_Index := 0; + if Handed_DomainList.Count > 0 then begin + repeat + IsMyDomains := IndyPos( + LowerCase(Handed_DomainList.Strings[Server_Index]), + LowerCase(MoreAddrSearch.Strings[Count])) > 0; + Inc(Server_Index); + until IsMyDomains or (Server_Index > (Handed_DomainList.Count-1)); + end else begin + IsMyDomains := False; + end; + + if IsMyDomains then begin + //ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA); + // modified by Dennies Chang in 2004/7/15. + ifAdditional := (QType <> TypeCode_CName); + + //Search A record first. + // Main, Cache, Additional, Wildcard + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, False, ifAdditional, False); + { modified by Dennies Chang in 2004/7/15. + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, + LocalAnswer, True, ifAdditional, True); + } + + if Length(LocalAnswer) = 0 then begin + temp := MoreAddrSearch.Strings[Count]; + Fetch(temp, '.'); + temp := '*.' + temp; + InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]); + { marked by Dennies Chang in 2004/7/15. + InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, ifAdditional, True, True, MoreAddrSearch.Strings[Count]); + } + end; + + TempAnswer := LocalAnswer; + + // Search for AAAA also. + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True); + { marked by Dennies Chang in 2004/7/15. + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, ifAdditional, True); + } + + if Length(LocalAnswer) = 0 then begin + temp := MoreAddrSearch.Strings[Count]; + Fetch(temp, '.'); + temp := '*.' + temp; + InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]); + { marked by Dennies Chang in 2004/7/15. + InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, ifAdditional, True, True, MoreAddrSearch.Strings[Count]); + } + end; + + AppendBytes(TempAnswer, LocalAnswer); + LocalAnswer := TempAnswer; + end else begin + // Need add AAAA Search in future. + //QType := TypeCode_A; + LDNSResolver := TIdDNSResolver.Create(Self); + try + Server_Index := 0; + repeat + LDNSResolver.Host := RootDNS_NET.Strings[Server_Index]; + LDNSResolver.QueryType := [qtA]; + LDNSResolver.Resolve(MoreAddrSearch.Strings[Count]); + AResult := LDNSResolver.PlainTextResult; + Header.ARCount := Header.ARCount + LDNSResolver.QueryResult.Count; + until (Server_Index >= (RootDNS_NET.Count-1)) or (Length(AResult) > 0); + + AppendBytes(LocalAnswer, AResult, 12); + finally + FreeAndNil(LDNSResolver); + end; + end; + + if Length(LocalAnswer) > 0 then begin + AppendBytes(Answer, LocalAnswer); + end; + //Answer := LocalAnswer; + end; + end; + end else begin + //Search the Cache Tree; + { marked by Dennies Chang in 2004/7/15. + { it's mark for querying cache only. + { if Length(Answer) = 0 then begin } + TargetNode := SearchTree(Cached_Tree, QName, QType); + if TargetNode <> nil then begin + //Assemble the Answer. + { modified by Dennies Chang in 2004/7/15} + if (QType in [TypeCode_A, TypeCode_PTR, TypeCode_AAAA, TypeCode_Error, TypeCode_CName]) then begin + QName := Fetch(QName, '.'); + end; + + RRIndex := TargetNode.RRs.ItemNames.IndexOf(QName); + + repeat + temp_QName := QName; + SetLength(LocalAnswer, 0); + + if RRIndex <> -1 then begin + // TimeOut, update the record. + if CompareDate(Now, StrToDateTime(TargetNode.RRs.Items[RRIndex].TimeOut)) = 1 then begin + SetLength(LocalAnswer, 0); + end else begin + case QType of + TypeCode_Error: + begin + AppendString(Answer, 'Error'); {do not localize} + end; + TypeCode_A: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_A then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_AAAA: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_AAAA then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_NS: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_NS).NSDName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MD: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MF: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_CName: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_CName then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_CName).CName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_SOA: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_SOA then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).MName); + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_SOA).RName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MB: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MB then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MB).MADName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MG: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MG then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MG).MGMName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MR: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MR then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MR).NewName); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_NULL: + begin + { + if TargetNode.RRs.Items[RRIndex] is TIdRR_NULL then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + } + end; + TypeCode_WKS: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_WKS then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_PTR: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_PTR then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_HINFO: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_HINFO then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MINFO: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MINFO then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_MX: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_MX then begin + CheckMoreAddrSearch((TargetNode.RRs.Items[RRIndex] as TIdRR_MX).Exchange); + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_TXT: + begin + if TargetNode.RRs.Items[RRIndex] is TIdRR_TXT then begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + TypeCode_STAR: + begin + LocalAnswer := TargetNode.RRs.Items[RRIndex].BinQueryRecord(TargetNode.FullName); + end; + end; + end; + + if BytesToString(LocalAnswer) = 'Error' then begin {do not localize} + Stop := True; + end else begin + if Length(LocalAnswer) > 0 then begin + AppendBytes(Answer, LocalAnswer); + if TargetNode.RRs.Items[RRIndex] is TIdRR_NS then begin + if IfMainQuestion then begin + Header.ANCount := Header.ANCount + 1; + end else begin + Header.NSCount := Header.NSCount + 1; + end; + end + else if IfMainQuestion then begin + Header.ANCount := Header.ANCount + 1; + end + else begin + Header.ARCount := Header.ARCount + 1; + end; + + Header.Qr := iQr_Answer; + Header.AA := iAA_NotAuthoritative; + Header.RCode := iRCodeNoError; + end; + + if RRIndex < (TargetNode.RRs.ItemNames.Count-1) then begin + Stop := False; + Inc(RRIndex); + end else begin + Stop := True; + end; + end; + end else begin + Stop := True; + end; + until (RRIndex = -1) or + (not ((not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], QName)) xor + (not TextIsSame(TargetNode.RRs.ItemNames.Strings[RRIndex], Fetch(temp_QName, '.'))))) + or Stop; + + end; + + // Search MoreAddrSearch it's added in 2004/7/15, but the need is + // found in 2004 Feb. + if MoreAddrSearch.Count > 0 then begin + for Count := 0 to MoreAddrSearch.Count -1 do begin + Server_Index := 0; + if Handed_DomainList.Count > 0 then begin + repeat + IsMyDomains := IndyPos( + LowerCase(Handed_DomainList.Strings[Server_Index]), + LowerCase(MoreAddrSearch.Strings[Count])) > 0; + Inc(Server_Index); + until IsMyDomains or (Server_Index > (Handed_DomainList.Count-1)); + end else begin + IsMyDomains := False; + end; + + if IsMyDomains then begin + ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA); + + //Search A record first. + // Main, Cache, Additional, Wildcard + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, False, ifAdditional, False); + + if Length(LocalAnswer) = 0 then begin + temp := MoreAddrSearch.Strings[Count]; + Fetch(temp, '.'); + temp := '*.' + temp; + InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]); + end; + + TempAnswer := LocalAnswer; + + // Search for AAAA also. + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True); + + if Length(LocalAnswer) = 0 then begin + temp := MoreAddrSearch.Strings[Count]; + Fetch(temp, '.'); + temp := '*.' + temp; + InternalSearch(Header, temp, TypeCode_AAAA, LocalAnswer, True, False, ifAdditional, True, MoreAddrSearch.Strings[Count]); + end; + + AppendBytes(TempAnswer, LocalAnswer); + LocalAnswer := TempAnswer; + end else begin + // Cache + TempAnswer := LocalAnswer; + ifAdditional := (QType <> TypeCode_A) or (QType <> TypeCode_AAAA); + + //Search A record first. + // Main, Cache, Additional, Wildcard + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_A, LocalAnswer, True, True, ifAdditional, False); + + if Length(LocalAnswer) = 0 then begin + temp := MoreAddrSearch.Strings[Count]; + Fetch(temp, '.'); + temp := '*.' + temp; + InternalSearch(Header, temp, TypeCode_A, LocalAnswer, True, True, ifAdditional, True, MoreAddrSearch.Strings[Count]); + end; + + AppendBytes(TempAnswer, LocalAnswer); + LocalAnswer := TempAnswer; + + // Search for AAAA also. + InternalSearch(Header, MoreAddrSearch.Strings[Count], TypeCode_AAAA, LocalAnswer, True, True, ifAdditional, True); + + if Length(LocalAnswer) > 0 then begin + AppendBytes(TempAnswer, LocalAnswer); + LocalAnswer := TempAnswer; + end; + + Answer := LocalAnswer; + end; + end; + end; + end; + finally + FreeAndNil(MoreAddrSearch); + end; +end; + +{ TIdDNSServer } + +procedure TIdDNSServer.CheckIfExpire(Sender: TObject); +begin +end; + +procedure TIdDNSServer.InitComponent; +begin + inherited InitComponent; + FAccessList := TStringList.Create; + FUDPTunnel := TIdDNS_UDPServer.Create(Self); + FTCPTunnel := TIdDNS_TCPServer.Create(Self); + + FBindings := TIdSocketHandles.Create(Self); + FTCPTunnel.DefaultPort := IdPORT_DOMAIN; + FUDPTunnel.DefaultPort := IdPORT_DOMAIN; + ServerType := stPrimary; + BackupDNSMap := TIdDNSMap.Create(FUDPTunnel); +end; + +destructor TIdDNSServer.Destroy; +begin + FreeAndNil(FAccessList); + FreeAndNil(FUDPTunnel); + FreeAndNil(FTCPTunnel); + FreeAndNil(FBindings); + FreeAndNil(BackupDNSMap); + inherited Destroy; +end; + +procedure TIdDNSServer.SetAccessList(const Value: TStrings); +begin + FAccessList.Assign(Value); + FTCPTunnel.AccessList.Assign(Value); +end; + +procedure TIdDNSServer.SetActive(const Value: Boolean); +var + Count : Integer; + DNSMap : TIdDomainNameServerMapping; +begin + FActive := Value; + FUDPTunnel.Active := Value; + if ServerType = stSecondary then begin + TCPTunnel.Active := False; + // TODO: should this loop only be run if Value=True? + for Count := 0 to BackupDNSMap.Count-1 do begin + DNSMap := BackupDNSMap.Items[Count]; + DNSMap.CheckScheduler.Start; + end; + end else begin + TCPTunnel.Active := Value; + end; +end; + +procedure TIdDNSServer.SetBindings(const Value: TIdSocketHandles); +begin + FBindings.Assign(Value); + FUDPTunnel.Bindings.Assign(Value); + FTCPTunnel.Bindings.Assign(Value); +end; + +procedure TIdDNSServer.SetTCPACLActive(const Value: Boolean); +begin + FTCPACLActive := Value; + TCPTunnel.AccessControl := Value; + + if Value then begin + FTCPTunnel.FAccessList.Assign(FAccessList); + end else begin + FTCPTunnel.FAccessList.Clear; + end; +end; + +procedure TIdDNSServer.TimeToUpdateNodeData(Sender: TObject); +var + Resolver : TIdDNSResolver; + Count : Integer; +begin + Resolver := TIdDNSResolver.Create(Self); + try + Resolver.Host := UDPTunnel.RootDNS_NET.Strings[0]; + Resolver.QueryType := [qtAXFR]; + + Resolver.Resolve((Sender as TIdDNTreeNode).FullName); + + for Count := 0 to Resolver.QueryResult.Count-1 do begin + UDPTunnel.UpdateTree(UDPTunnel.Handed_Tree, Resolver.QueryResult.Items[Count]); + end; + finally + FreeAndNil(Resolver); + end; +end; + +{ TIdDNS_TCPServer } + +procedure TIdDNS_TCPServer.InitComponent; +begin + inherited InitComponent; + FAccessList := TStringList.Create; +end; + +destructor TIdDNS_TCPServer.Destroy; +begin + FreeAndNil(FAccessList); + inherited Destroy; +end; + +procedure TIdDNS_TCPServer.DoConnect(AContext: TIdContext); +var + Answer, Data, Question: TIdBytes; + QName, QLabel, QResult, PeerIP : string; + LData, QPos, LLength : Integer; + TestHeader : TDNSHeader; + + procedure GenerateAXFRData; + begin + TestHeader := TDNSHeader.Create; + try + TestHeader.ParseQuery(Data); + if TestHeader.QDCount > 0 then begin + // parse the question. + QPos := 13; + QLabel := ''; + QName := ''; + + repeat + LLength := Byte(Data[QPos]); + Inc(QPos); + QLabel := BytesToString(Data, QPos, LLength); + Inc(QPos, LLength); + QName := QName + QLabel + '.'; + until (QPos >= LData) or (Data[QPos] = 0); + + Question := Copy(Data, 13, Length(Data)-12); + QResult := TIdDNSServer(Owner).UDPTunnel.AXFR(TestHeader, QName, Answer); + end; + finally + FreeAndNil(TestHeader); + end; + end; + + procedure GenerateAXFRRefuseData; + begin + TestHeader := TDNSHeader.Create; + try + TestHeader.ParseQuery(Data); + TestHeader.Qr := iQr_Answer; + TestHeader.RCode := iRCodeRefused; + Answer := TestHeader.GenerateBinaryHeader; + finally + FreeAndNil(TestHeader); + end; + end; + +begin + inherited DoConnect(AContext); + + LData := AContext.Connection.IOHandler.ReadInt16; + SetLength(Data, 0); + + // RLebeau - why not use ReadBuffer() here? + // Dennies - Sure, in older version, my concern is for real time generate system + // might not generate the data with correct data size we expect. + AContext.Connection.IOHandler.ReadBytes(Data, LData); + {for Count := 1 to LData do begin + AppendByte(Data, AThread.Connection.IOHandler.ReadByte); + end; + } + + // PeerIP is ip address. + PeerIP := AContext.Binding.PeerIP; + if AccessControl and (AccessList.IndexOf(PeerIP) = -1) then begin + GenerateAXFRRefuseData; + end else begin + GenerateAXFRData; + end; + + if Length(Answer) > 32767 then begin + SetLength(Answer, 32767); + end; + + AContext.Connection.IOHandler.Write(Int16(Length(Answer))); + AContext.Connection.IOHandler.Write(Answer); +end; + +procedure TIdDNS_TCPServer.SetAccessList(const Value: TStrings); +begin + FAccessList.Assign(Value); +end; + +{ TIdDomainExpireCheckThread } + +procedure TIdDomainExpireCheckThread.Run; +var + LInterval, LStep: Integer; +begin + LInterval := FInterval; + while LInterval > 0 do begin + LStep := IndyMin(LInterval, 500); + IndySleep(LStep); + Dec(LInterval, LStep); + if Terminated then begin + Exit; + end; + if Assigned(FTimerEvent) then begin + Synchronize(TimerEvent); + end; + end; +end; + +procedure TIdDomainExpireCheckThread.TimerEvent; +begin + if Assigned(FTimerEvent) then begin + FTimerEvent(FSender); + end; +end; + +{ TIdDomainNameServerMapping } + +constructor TIdDomainNameServerMapping.Create(AList : TIdDNSMap); +begin + inherited Create; + + CheckScheduler := TIdDomainExpireCheckThread.Create; + CheckScheduler.FInterval := 100000; + CheckScheduler.FSender := Self; + CheckScheduler.FDomain := DomainName; + CheckScheduler.FHost := Host; + CheckScheduler.FTimerEvent := SyncAndUpdate; + + FList := List; + FBusy := False; +end; + +destructor TIdDomainNameServerMapping.Destroy; +begin + //Self.CheckScheduler.TerminateAndWaitFor; + CheckScheduler.Terminate; + FreeAndNil(CheckScheduler); + inherited Destroy; +end; + +procedure TIdDomainNameServerMapping.SetHost(const Value: string); +begin + if (not IsValidIP(Value)) and (not IsValidIPv6(Value)) then begin + raise EIdDNSServerSettingException.Create(RSDNSServerSettingError_MappingHostError); + end; + FHost := Value; +end; + +procedure TIdDomainNameServerMapping.SetInterval(const Value: UInt32); +begin + FInterval := Value; + CheckScheduler.FInterval := Value; +end; + +procedure TIdDomainNameServerMapping.SyncAndUpdate(Sender: TObject); +//Todo - Dennies Chang should append axfr and update Tree. +var + Resolver : TIdDNSResolver; + RR : TResultRecord; + TNode : TIdDNTreeNode; + Server : TIdDNS_UDPServer; + NeedUpdated, NotThis : Boolean; + Count, TIndex : Integer; + RRName : string; +begin + if FBusy then begin + Exit; + end; + + FBusy := True; + try + Resolver := TIdDNSResolver.Create(nil); + try + Resolver.Host := Host; + Resolver.QueryType := [qtAXFR]; + + Resolver.Resolve(DomainName); + + if Resolver.QueryResult.Count = 0 then begin + raise EIdDNSServerSyncException.Create(RSDNSServerAXFRError_QuerySequenceError); + end; + + RR := Resolver.QueryResult.Items[0]; + if RR.RecType <> qtSOA then begin + raise EIdDNSServerSyncException.Create(RSDNSServerAXFRError_QuerySequenceError); + end; + + Server := List.Server; + Interval := TSOARecord(RR).Expire * 1000; + + { + //Update MyDomain + if Copy(RR.Name, Length(RR.Name),1) <> '.' then begin + RRName := RR.Name + '.'; + end; + } + + if Server.Handed_DomainList.IndexOf(RR.Name) = -1 then begin + Server.Handed_DomainList.Add(RR.Name); + end; + + TNode := Server.SearchTree(Server.Handed_Tree, RR.Name, TypeCode_SOA); + + if TNode = nil then begin + NeedUpdated := True; + end else begin + RRName := RRName; + RRName := Fetch(RRName, '.'); + TIndex := TNode.RRs.ItemNames.IndexOf(RR.Name); + NotThis := True; + + while (TIndex > -1) and (TIndex <= (TNode.RRs.Count-1)) and + (TNode.RRs.Items[TIndex].RRName = RR.Name) and NotThis do + begin + NotThis := not (TNode.RRs.Items[TIndex] is TIdRR_SOA); + Inc(TIndex); + end; + + if not NotThis then begin + Dec(TIndex); + NeedUpdated := (TNode.RRs.Items[TIndex] as TIdRR_SOA).Serial = IntToStr(TSOARecord(RR).Serial); + end else begin + NeedUpdated := True; + end; + end; + + if NeedUpdated then begin + if TNode <> nil then begin + Server.Handed_Tree.RemoveChild(Server.Handed_Tree.IndexByNode(TNode)); + end; + + for Count := 0 to Resolver.QueryResult.Count-1 do begin + RR := Resolver.QueryResult.Items[Count]; + Server.UpdateTree(Server.Handed_Tree, RR); + end; + end; + finally + FreeAndNil(Resolver); + end; + finally + FBusy := False; + end; +end; + +{ TIdDNSMap } + +constructor TIdDNSMap.Create(Server: TIdDNS_UDPServer); +begin + inherited Create; + FServer := Server; +end; + +{$IFNDEF USE_OBJECT_ARC} +destructor TIdDNSMap.Destroy; +var + I : Integer; + DNSMP : TIdDomainNameServerMapping; +begin + if Count > 0 then begin + for I := Count-1 downto 0 do begin + DNSMP := Items[I]; + FreeAndNil(DNSMP); + Delete(I); + end; + end; + inherited Destroy; +end; +{$ENDIF} + +{$IFNDEF HAS_GENERICS_TObjectList} +function TIdDNSMap.GetItem(Index: Integer): TIdDomainNameServerMapping; +begin + Result := TIdDomainNameServerMapping(inherited GetItem(Index)); +end; + +procedure TIdDNSMap.SetItem(Index: Integer; const Value: TIdDomainNameServerMapping); +begin + inherited SetItem(Index, Value); +end; +{$ENDIF} + +procedure TIdDNSMap.SetServer(const Value: TIdDNS_UDPServer); +begin + FServer := Value; +end; + +{ TIdDNS_ProcessThread } + +constructor TIdDNS_ProcessThread.Create(ACreateSuspended: Boolean; + Data: TIdBytes; MainBinding, Binding: TIdSocketHandle; + Server: TIdDNS_UDPServer); +begin + inherited Create(ACreateSuspended); + + FMyData := nil; + FData := Data; + + FMyBinding := Binding; + FMainBinding := MainBinding; + + FServer := Server; + FreeOnTerminate := True; +end; + +procedure TIdDNS_ProcessThread.ComposeErrorResult(var VFinal: TIdBytes; + OriginalHeader: TDNSHeader; OriginalQuestion : TIdBytes; + ErrorStatus: Integer); +begin + case ErrorStatus of + iRCodeQueryNotImplement : + begin + OriginalHeader.Qr := iQr_Answer; + OriginalHeader.RCode := iRCodeNotImplemented; + + VFinal := OriginalHeader.GenerateBinaryHeader; + AppendBytes(VFinal, OriginalQuestion, 12); + end; + iRCodeQueryNotFound : + begin + OriginalHeader.Qr := iQr_Answer; + OriginalHeader.RCode := iRCodeNameError; + OriginalHeader.ANCount := 0; + + VFinal := OriginalHeader.GenerateBinaryHeader; + //VFinal := VFinal; + end; + end; +end; + +destructor TIdDNS_ProcessThread.Destroy; +begin + FServer := nil; + FMainBinding := nil; + FMyBinding.CloseSocket; + FreeAndNil(FMyBinding); + FreeAndNil(FMyData); + inherited Destroy; +end; + +procedure TIdDNS_ProcessThread.QueryDomain; +var + QName, QLabel, RString : string; + Temp, ExternalQuery, Answer, FinalResult : TIdBytes; + DNSHeader_Processing : TDNSHeader; + QType, QClass : UInt16; + QPos, QLength, LLength : Integer; + ABinding: TIdSocketHandle; +begin + ExternalQuery := FData; + ABinding := MyBinding; + Temp := Copy(FData, 0, Length(FData)); + SetLength(FinalResult, 0); + QType := TypeCode_A; + + if Length(FData) >= 12 then begin + DNSHeader_Processing := TDNSHeader.Create; + try + // RLebeau: this does not make sense to me. ParseQuery() always returns + // 0 when the data length is >= 12 unless an exception is raised, which + // should only happen if the GStack object is invalid... + // + if DNSHeader_Processing.ParseQuery(ExternalQuery) <> 0 then begin + FServer.DoAfterQuery(ABinding, DNSHeader_Processing, Temp, RString, ExternalQuery); + AppendBytes(FinalResult, Temp); + end else begin + if DNSHeader_Processing.QDCount > 0 then begin + + QPos := 12; //13; Modified in Dec. 13, 2004 by Dennies + QLength := Length(ExternalQuery); + if QLength > 12 then begin + QName := ''; + repeat + SetLength(Answer, 0); + LLength := ExternalQuery[QPos]; + Inc(QPos); + QLabel := BytesToString(ExternalQuery, QPos, LLength); + Inc(QPos, LLength); + QName := QName + QLabel + '.'; + until (QPos >= QLength) or (ExternalQuery[QPos] = 0); + Inc(QPos); + + QType := GStack.NetworkToHost(TwoByteToUInt16(ExternalQuery[QPos], ExternalQuery[QPos + 1])); + Inc(QPos, 2); + QClass := GStack.NetworkToHost(TwoByteToUInt16(ExternalQuery[QPos], ExternalQuery[QPos + 1])); + FServer.DoBeforeQuery(ABinding, DNSHeader_Processing, Temp); + + RString := CompleteQuery(DNSHeader_Processing, QName, ExternalQuery, Answer, QType, QClass, nil); + + if RString = cRCodeQueryNotImplement then begin + ComposeErrorResult(FinalResult, DNSHeader_Processing, ExternalQuery, iRCodeQueryNotImplement); + end + else if (RString = cRCodeQueryReturned) then begin + FinalResult := Answer; + end + else if (RString = cRCodeQueryNotFound) or (RString = cRCodeQueryCacheFindError) then begin + ComposeErrorResult(FinalResult, DNSHeader_Processing, ExternalQuery, iRCodeQueryNotFound); + end + else begin + FinalResult := CombineAnswer(DNSHeader_Processing, ExternalQuery, Answer); + end; + + FServer.DoAfterQuery(ABinding, DNSHeader_Processing, FinalResult, RString, Temp); + //AppendString(FinalResult, Temp); + end; + end; + end; + finally + try + FData := FinalResult; + + FServer.DoAfterSendBack(ABinding, DNSHeader_Processing, FinalResult, RString, ExternalQuery); + + if (FServer.CacheUnknowZone) and + (RString <> cRCodeQueryCacheFindError) and + (RString <> cRCodeQueryCacheOK) and + (RString <> cRCodeQueryOK) and + (RString <> cRCodeQueryNotImplement) then + begin + FServer.SaveToCache(FinalResult, QName, QType); + FServer.DoAfterCacheSaved(Self.FServer.FCached_Tree); + end; + finally + FreeAndNil(DNSHeader_Processing); + end; + end; + end; +end; + +procedure TIdDNS_ProcessThread.Run; +begin + try + QueryDomain; + SendData; + finally + Stop; + Terminate; + end; +end; + +procedure TIdDNS_ProcessThread.SetMyBinding(const Value: TIdSocketHandle); +begin + FMyBinding := Value; +end; + +procedure TIdDNS_ProcessThread.SetMyData(const Value: TStream); +begin + FMyData := Value; +end; + +procedure TIdDNS_ProcessThread.SetServer(const Value: TIdDNS_UDPServer); +begin + FServer := Value; +end; + +function TIdDNS_ProcessThread.CombineAnswer(Header: TDNSHeader; const EQuery, Answer: TIdBytes): TIdBytes; +begin + Result := Header.GenerateBinaryHeader; + AppendBytes(Result, EQuery, 12); + AppendBytes(Result, Answer); +end; + +procedure TIdDNS_ProcessThread.ExternalSearch(ADNSResolver: TIdDNSResolver; Header: TDNSHeader; + Question: TIdBytes; var Answer: TIdBytes); +var + Server_Index : Integer; + MyDNSResolver : TIdDNSResolver; +begin + Server_Index := 0; + if ADNSResolver = nil then begin + MyDNSResolver := TIdDNSResolver.Create; + MyDNSResolver.WaitingTime := 2000; + end else + begin + MyDNSResolver := ADNSResolver; + end; + + try + repeat + MyDNSResolver.Host := FServer.RootDNS_NET.Strings[Server_Index]; + try + MyDNSResolver.InternalQuery := Question; + MyDNSResolver.Resolve(''); + Answer := MyDNSResolver.PlainTextResult; + except + // Todo: Create DNS server interal resolver error. + on EIdDnsResolverError do + begin + //Empty Event, for user to custom the event handle. + end; + on EIdSocketError do + begin + end; + + else + begin + end; + end; + + Inc(Server_Index); + until (Server_Index >= FServer.RootDNS_NET.Count) or (Length(Answer) > 0); + finally + if ADNSResolver = nil then begin + FreeAndNil(MyDNSResolver); + end; + end; +end; + +procedure TIdDNS_ProcessThread.InternalSearch(Header: TDNSHeader; QName: string; QType: UInt16; + var Answer: TIdBytes; IfMainQuestion: boolean; IsSearchCache: Boolean = False; + IsAdditional: boolean = false; IsWildCard : boolean = false; + WildCardOrgName: string = ''); +begin +end; + +procedure TIdDNS_ProcessThread.SaveToCache(ResourceRecord: TIdBytes; QueryName: string; OriginalQType: UInt16); +var + TempResolver : TIdDNSResolver; + Count : Integer; + TNode : TIdDNTreeNode; + RR_Err : TIdRR_Error; +begin + TempResolver := TIdDNSResolver.Create(nil); + try + // RLebeau: FillResultWithOutCheckId() is deprecated, but not using FillResult() + // here yet because it validates the DNSHeader.RCode, and I do not know if that + // is needed here. I don't want to break this logic... + TempResolver.FillResultWithOutCheckId(ResourceRecord); + + if TempResolver.DNSHeader.ANCount > 0 then begin + for Count := 0 to TempResolver.QueryResult.Count-1 do begin + FServer.UpdateTree(FServer.Cached_Tree, TempResolver.QueryResult.Items[Count]); + end; // for loop + end else begin + TNode := Self.SearchTree(FServer.Cached_Tree, QueryName, TypeCode_Error); + if TNode = nil then begin + RR_Err := TIdRR_Error.Create; + RR_Err.RRName := QueryName; + RR_Err.TTL := 600; + FServer.UpdateTree(FServer.Cached_Tree, RR_Err); + end; + end; + finally + FreeAndNil(TempResolver); + end; +end; + +function TIdDNS_ProcessThread.SearchTree(Root: TIdDNTreeNode; QName: String; QType: UInt16): TIdDNTreeNode; +var + RRIndex : integer; + NodeCursor : TIdDNTreeNode; + NameLabels : TStrings; + OneNode, FullName : string; + Found : Boolean; +begin + Result := nil; + NameLabels := TStringList.Create; + try + FullName := QName; + NodeCursor := Root; + Found := False; + + repeat + OneNode := Fetch(FullName, '.'); + if OneNode <> '' then begin + NameLabels.Add(OneNode); + end; + until FullName = ''; + + repeat + IndySleep(0); + if QType <> TypeCode_SOA then begin + RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]); + if RRIndex <> -1 then begin + NameLabels.Delete(NameLabels.Count - 1); + NodeCursor := NodeCursor.Children[RRIndex]; + + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + end else begin + Found := NameLabels.Count = 0; + end; + end + else if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + if not Found then begin + NameLabels.Clear; + end; + end + else begin + NameLabels.Clear; + end; + end else begin + RRIndex := NodeCursor.ChildIndex.IndexOf(NameLabels.Strings[NameLabels.Count - 1]); + if RRIndex <> -1 then begin + NameLabels.Delete(NameLabels.Count - 1); + NodeCursor := NodeCursor.Children[RRIndex]; + + if NameLabels.Count = 1 then begin + Found := NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1; + end else begin + Found := NameLabels.Count = 0; + end; + end + else if NameLabels.Count = 1 then begin + Found := (NodeCursor.RRs.ItemNames.IndexOf(NameLabels.Strings[0]) <> -1); + if not Found then begin + NameLabels.Clear; + end; + end + else begin + NameLabels.Clear; + end; + end; + until (NameLabels.Count = 0) or Found; + + if Found then begin + Result := NodeCursor; + end; + finally + FreeAndNil(NameLabels); + end; +end; + +function TIdDNS_ProcessThread.CompleteQuery(DNSHeader: TDNSHeader; + Question: string; OriginalQuestion: TIdBytes; var Answer : TIdBytes; + QType, QClass : UInt16; DNSResolver : TIdDNSResolver) : string; +var + IsMyDomains : boolean; + LAnswer, TempAnswer, RRData: TIdBytes; + WildQuestion, TempDomain : string; + LIdx: Integer; +begin + // QClass = 1 => IN, we support only "IN" class now. + // QClass = 2 => CS, + // QClass = 3 => CH, we suppor "CHAOS" class now, but only "version.bind." info. + // from 2004/6/28 + // QClass = 4 => HS. + RRData := nil; + TempAnswer := nil; + TempDomain := LowerCase(Question); + + case QClass of + Class_IN : + begin + IsMyDomains := FServer.Handed_DomainList.IndexOf(TempDomain) > -1; + if not IsMyDomains then begin + Fetch(TempDomain, '.'); + IsMyDomains := FServer.Handed_DomainList.IndexOf(TempDomain) > -1; + end; + + if IsMyDomains then begin + FServer.InternalSearch(DNSHeader, Question, QType, LAnswer, True, False, False); + Answer := LAnswer; + + if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then begin + FServer.InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, False, True); + if Length(LAnswer) > 0 then begin + AppendBytes(Answer, LAnswer); + end; + end; + + WildQuestion := Question; + Fetch(WildQuestion, '.'); + WildQuestion := '*.' + WildQuestion; + FServer.InternalSearch(DNSHeader, WildQuestion, QType, LAnswer, True, False, False, True, Question); + { + FServer.InternalSearch(DNSHeader, Question, QType, LAnswer, True, True, False); + } + if Length(LAnswer) > 0 then begin + AppendBytes(Answer, LAnswer); + end; + + if Length(Answer) > 0 then begin + Result := cRCodeQueryOK; + end else begin + Result := cRCodeQueryNotFound; + end; + end else begin + FServer.InternalSearch(DNSHeader, Question, QType, Answer, True, True, False); + + if (QType in [TypeCode_A, TypeCode_AAAA]) and (Length(Answer) = 0) then begin + FServer.InternalSearch(DNSHeader, Question, TypeCode_CNAME, LAnswer, True, True, False); + if Length(LAnswer) > 0 then begin + AppendBytes(Answer, LAnswer); + end; + end; + + if Length(Answer) > 0 then begin + Result := cRCodeQueryCacheOK; + end else begin + //QType := TypeCode_Error; + + FServer.InternalSearch(DNSHeader, Question, QType, Answer, True, True, False); + if BytesToString(Answer) = 'Error' then begin {do not localize} + Result := cRCodeQueryCacheFindError; + end else begin + FServer.ExternalSearch(DNSResolver, DNSHeader, OriginalQuestion, Answer); + + if Length(Answer) > 0 then begin + Result := cRCodeQueryReturned; + end else begin + Result := cRCodeQueryNotImplement; + end; + end; + end; + end; + end; + + Class_CHAOS : + begin + if TempDomain = 'version.bind.' then begin {do not localize} + if FServer.offerDNSVersion then begin + TempAnswer := DomainNameToDNSStr('version.bind.'); {do not localize} + RRData := NormalStrToDNSStr(FServer.DNSVersion); + + SetLength(LAnswer, Length(TempAnswer) + (SizeOf(UInt16)*3) + SizeOf(UInt32) + Length(RRData)); + CopyTIdBytes(TempAnswer, 0, LAnswer, 0, Length(TempAnswer)); + LIdx := Length(TempAnswer); + CopyTIdUInt16(GStack.HostToNetwork(UInt16(TypeCode_TXT)), LAnswer, LIdx); + Inc(LIdx, SizeOf(UInt16)); + CopyTIdUInt16(GStack.HostToNetwork(UInt16(Class_CHAOS)), LAnswer, LIdx); + Inc(LIdx, SizeOf(UInt16)); + CopyTIdUInt32(GStack.HostToNetwork(UInt32(86400)), LAnswer, LIdx); {do not localize} + Inc(LIdx, SizeOf(UInt32)); + CopyTIdUInt16(GStack.HostToNetwork(UInt16(Length(RRData))), LAnswer, LIdx); + Inc(LIdx, SizeOf(UInt16)); + CopyTIdBytes(RRData, 0, LAnswer, LIdx, Length(RRData)); + + Answer := LAnswer; + DNSHeader.ANCount := 1; + DNSHeader.AA := 1; + Result := cRCodeQueryOK; + end else begin + Result := cRCodeQueryNotImplement; + end; + end else begin + Result := cRCodeQueryNotImplement; + end; + end; + + else + begin + Result := cRCodeQueryNotImplement; + end; + end; +end; + +procedure TIdDNS_ProcessThread.SendData; +begin + FServer.GlobalCS.Enter; + try + FMainBinding.SendTo(FMyBinding.PeerIP, FMyBinding.PeerPort, FData, FMyBinding.IPVersion); + finally + FServer.GlobalCS.Leave; + end; +end; + +procedure TIdDNS_UDPServer.DoAfterCacheSaved(CacheRoot: TIdDNTreeNode); +begin + if Assigned(FOnAfterCacheSaved) then begin + FOnAfterCacheSaved(CacheRoot); + end; +end; + +procedure TIdDNS_UDPServer.DoUDPRead(AThread: TIdUDPListenerThread; + const AData: TIdBytes; ABinding: TIdSocketHandle); +var + PThread : TIdDNS_ProcessThread; + BBinding : TIdSocketHandle; + Binded : Boolean; +begin + inherited DoUDPRead(AThread, AData, ABinding); + + Binded := False; + + BBinding := TIdSocketHandle.Create(nil); + try + BBinding.SetPeer(ABinding.PeerIP, ABinding.PeerPort, ABinding.IPVersion); + BBinding.IP := ABinding.IP; + + repeat + try + BBinding.Port := 53; + BBinding.AllocateSocket(Id_SOCK_DGRAM); + Binded := True; + except + end; + until Binded; + + PThread := TIdDNS_ProcessThread.Create(True, AData, ABinding, BBinding, Self); + except + FreeAndNil(BBinding); + raise; + end; + + PThread.Start; +end; + +end. diff --git a/indy/Protocols/IdDateTimeStamp.pas b/indy/Protocols/IdDateTimeStamp.pas new file mode 100644 index 0000000..da0a615 --- /dev/null +++ b/indy/Protocols/IdDateTimeStamp.pas @@ -0,0 +1,1468 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 2004.02.03 5:45:04 PM czhower + Name changes + + Rev 1.2 1/21/2004 1:57:38 PM JPMugaas + InitComponent + + Rev 1.1 10/12/2003 2:01:46 PM BGooijen + Compiles in DotNet + + Rev 1.0 11/14/2002 02:16:44 PM JPMugaas + + 2002-Feb-07 Pete Mee + - Modified interface: GetAsRFC882 is now GetAsRFC822. ;-) + - Fixed GetAsTTimeStamp (was way out). + + 2001-Nov-10 Pete Mee + - Added SetFromDOSDateTime. + + 2001-Mar-29 Pete Mee + - Fixed bug in SetFromRFC822. As luck would have it, my PC has changed + to BST from GMT, so I caught the error. Change use of GMTToLocalDateTime + to StrInternetToDateTime. + + 2001-Mar-27 Pete Mee + - Added GetTimeZoneHour, GetTimeZoneMinutes, GetTimeZoneAsString and + corresponding properties, TimeZoneHour, TimeZoneMinutes and TimeZoneAsString. + - Added SetFromRFC822 and SetFromISO8601. + + 2001-Mar-26 Pete Mee + - Fixed bug in AddDays. Was adding an extra day in the wrong centuary. + - Fixed bug in AddDays. Was not altering the year with large additions. + + 2001-Mar-23 Pete Mee + - Fixed bug in SubtractMilliseconds. + - GetBeatOfDay is more accurate (based on milliseconds instead of seconds). + + 2001-Mar-21 Pete Mee + - Altered Day, Seond and Millisecond properties to use their respective + Set methods. + - Added SetTimeZone, Zero, ZeroTime and ZeroDate. + - Altered SetYear and SetDay to cope with the value 0. + + 2000-Sep-16 Pete Mee + - SetYear no longer accepts zero but instead simply exits. + + 2000-Aug-01 Pete Mee + - Fix bugs in AddDays & SubtractDays. Depending on the day of the year, the + calculations could have been incorrect. Now 'rounds off' to the nearest year + before any other calculation. + + 2000-Jul-28 Pete Mee + - Fix bugs in AddDays & SubtractDays. 3 days in 400 years lost, 1 day in 100 + years lost. + + 2000-May-11 Pete Mee + - Added GetAsRFC822, GetAsISO8601 + + 2000-May-03 Pete Mee + - Added detection of Day, Week and Month (various formats). + + 2000-May-02 Pete Mee + - Started TIdDateTimeStamp +} + +unit IdDateTimeStamp; + +{ + Development notes: + + The Calendar used is the Gregorian Calendar (modern western society). This + Calendar's use started somtime in the 1500s but wasn't adopted by some countries + until the early 1900s. No attempt is made to cope with any other Calendars. + + No attempt is made to cope with any Atomic time quantity less than a leap + year (i.e., an exact number of seconds per day and an exact number of days + per year / leap year - no leap seconds, no 1/4 days, etc). + + The implementation revolves around the Milliseconds, Seconds, Days and Years. + The heirarchy is as follows: + Milliseconds modify seconds. (0-999 Milliseconds) + Seconds modify days. (0-59 Seconds) + Days modify years. (1-365/366 Days) + Years modify years. (..., -2, -1, 1, ...) + + All other time units are translated into necessary component parts. I.e., + a week is 7 days, and hour is 3600 seconds, a minute is 60 seconds, etc... + + The implementation could be easily expanded to represent decades, centuries, + nanoseconds, and beyond in both directions. Milliseconds are included to + provide easy conversion from TTimeStamp and back (and hence TDateTime). The + current component is designed to give good functionality for the majority (if + not all) of Internet component requirements (including Swatch's Internet Time). + It is also not limited to the 2038 bug of many of today's OSs (32-bit signed + number of seconds from 1st Jan 1970 = 19th Jan 2038 03:14:07, or there abouts). + + NB: This implementation is factors slower than those of the TDateTime and + TTimeStamp components of standard Delphi. It's main use lies in the conversion + to / from ISO 8601 and RFC 822 formats as well as dates ranging beyond 2037 and + before 1970 (though TTimeStamp is capable here). It's also the only date component + I'm aware of that complies with RFC 2550 "Y10K and Beyond"... one of those RFCs in + the same category as RFC 1149, IP over Avian Carriers. ;-) + + Pete Mee +} +{ + ToDo: Allow localisation date / time strings generated (i.e., to zone name). + ToDo: Rework SetFromRFC822 as it is (marginally) limited by it's + conversion to TDateTime. + ToDo: Conversion between Time Zones. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdBaseComponent; + +const + // Some basic constants + IdMilliSecondsInSecond = 1000; + IdSecondsInMinute = 60; + IdMinutesInHour = 60; + IdHoursInDay = 24; + + IdDaysInWeek = 7; + IdDaysInYear = 365; + IdDaysInLeapYear = 366; + IdYearsInShortLeapYearCycle = 4; + IdDaysInShortLeapYearCycle = IdDaysInLeapYear + (IdDaysInYear * 3); + IdDaysInShortNonLeapYearCycle = IdDaysInYear * IdYearsInShortLeapYearCycle; + IdDaysInFourYears = IdDaysInShortLeapYearCycle; + IdYearsInCentury = 100; + IdDaysInCentury = (25 * IdDaysInFourYears) - 1; + IdDaysInLeapCentury = IdDaysInCentury + 1; + IdYearsInLeapYearCycle = 400; + IdDaysInLeapYearCycle = IdDaysInCentury * 4 + 1; + + IdMonthsInYear = 12; + + // Beat time is Swatch's "Internet Time" http://www.swatch.com/ {Do not Localize} + IdBeatsInDay = 1000; + + // Some compound constants + IdHoursInHalfDay = IdHoursInDay div 2; + + IdSecondsInHour = IdSecondsInMinute * IdMinutesInHour; + IdSecondsInDay = IdSecondsInHour * IdHoursInDay; + IdSecondsInHalfDay = IdSecondsInHour * IdHoursInHalfDay; + IdSecondsInWeek = IdDaysInWeek * IdSecondsInDay; + IdSecondsInYear = IdSecondsInDay * IdDaysInYear; + IdSecondsInLeapYear = IdSecondsInDay * IdDaysInLeapYear; + + IdMillisecondsInMinute = IdSecondsInMinute * IdMillisecondsInSecond; + IdMillisecondsInHour = IdSecondsInHour * IdMillisecondsInSecond; + IdMillisecondsInDay = IdSecondsInDay * IdMillisecondsInSecond; + IdMillisecondsInWeek = IdSecondsInWeek * IdMillisecondsInSecond; + + SShortMonthNameJan = 'Jan'; + SShortMonthNameFeb = 'Feb'; + SShortMonthNameMar = 'Mar'; + SShortMonthNameApr = 'Apr'; + SShortMonthNameMay = 'May'; + SShortMonthNameJun = 'Jun'; + SShortMonthNameJul = 'Jul'; + SShortMonthNameAug = 'Aug'; + SShortMonthNameSep = 'Sep'; + SShortMonthNameOct = 'Oct'; + SShortMonthNameNov = 'Nov'; + SShortMonthNameDec = 'Dec'; + + SLongMonthNameJan = 'January'; + SLongMonthNameFeb = 'February'; + SLongMonthNameMar = 'March'; + SLongMonthNameApr = 'April'; + SLongMonthNameMay = 'May'; + SLongMonthNameJun = 'June'; + SLongMonthNameJul = 'July'; + SLongMonthNameAug = 'August'; + SLongMonthNameSep = 'September'; + SLongMonthNameOct = 'October'; + SLongMonthNameNov = 'November'; + SLongMonthNameDec = 'December'; + + SShortDayNameSun = 'Sun'; + SShortDayNameMon = 'Mon'; + SShortDayNameTue = 'Tue'; + SShortDayNameWed = 'Wed'; + SShortDayNameThu = 'Thu'; + SShortDayNameFri = 'Fri'; + SShortDayNameSat = 'Sat'; + + SLongDayNameSun = 'Sunday'; + SLongDayNameMon = 'Monday'; + SLongDayNameTue = 'Tuesday'; + SLongDayNameWed = 'Wednesday'; + SLongDayNameThu = 'Thursday'; + SLongDayNameFri = 'Friday'; + SLongDayNameSat = 'Saturday'; + + IdDaysInMonth : array[1..IdMonthsInYear] of byte = + ( + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + ); + + IdMonthNames : array[0..IdMonthsInYear] of string = + ( '', {Do not Localize} + SLongMonthNameJan, SLongMonthNameFeb, SLongMonthNameMar, + SLongMonthNameApr, SLongMonthNameMay, SLongMonthNameJun, + SLongMonthNameJul, SLongMonthNameAug, SLongMonthNameSep, + SLongMonthNameOct, SLongMonthNameNov, SLongMonthNameDec ); + + + IdMonthShortNames : array[0..IdMonthsInYear] of string = + ( '', // Used for GetMonth {Do not Localize} + SShortMonthNameJan, SShortMonthNameFeb, SShortMonthNameMar, + SShortMonthNameApr, SShortMonthNameMay, SShortMonthNameJun, + SShortMonthNameJul, SShortMonthNameAug, SShortMonthNameSep, + SShortMonthNameOct, SShortMonthNameNov, SShortMonthNameDec ); + + IdDayNames : array[0..IdDaysInWeek] of string = + ( '', SLongDayNameSun, SLongDayNameMon, SLongDayNameTue, {Do not Localize} + SLongDayNameWed, SLongDayNameThu, SLongDayNameFri, + SLongDayNameSat ); + + IdDayShortNames : array[0..IdDaysInWeek] of string = + ( '', SShortDayNameSun, SShortDayNameMon, SShortDayNameTue, {Do not Localize} + SShortDayNameWed, SShortDayNameThu, SShortDayNameFri, + SShortDayNameSat ); + + // Area Time Zones + TZ_NZDT = 13; // New Zealand Daylight Time + TZ_IDLE = 12; // International Date Line East + TZ_NZST = TZ_IDLE;// New Zealand Standard Time + TZ_NZT = TZ_IDLE; // New Zealand Time + TZ_EADT = 11; // Eastern Australian Daylight Time + TZ_GST = 10; // Guam Standard Time / Russia Zone 9 + TZ_JST = 9; // Japan Standard Time / Russia Zone 8 + TZ_CCT = 8; // China Coast Time / Russia Zone 7 + TZ_WADT = TZ_CCT; // West Australian Daylight Time + TZ_WAST = 7; // West Australian Standard Time / Russia Zone 6 + TZ_ZP6 = 6; // Chesapeake Bay / Russia Zone 5 + TZ_ZP5 = 5; // Chesapeake Bay / Russia Zone 4 + TZ_ZP4 = 4; // Russia Zone 3 + TZ_BT = 3; // Baghdad Time / Russia Zone 2 + TZ_EET = 2; // Eastern European Time / Russia Zone 1 + TZ_MEST = TZ_EET; // Middle European Summer Time + TZ_MESZ = TZ_EET; // Middle European Summer Zone + TZ_SST = TZ_EET; // Swedish Summer Time + TZ_FST = TZ_EET; // French Summer Time + TZ_CET = 1; // Central European Time + TZ_FWT = TZ_CET; // French Winter Time + TZ_MET = TZ_CET; // Middle European Time + TZ_MEWT = TZ_CET; // Middle European Winter Time + TZ_SWT = TZ_CET; // Swedish Winter Time + TZ_GMT = 0; // Greenwich Meanttime + TZ_UT = TZ_GMT; // Universla Time + TZ_UTC = TZ_GMT; // Universal Time Co-ordinated + TZ_WET = TZ_GMT; // Western European Time + TZ_WAT = -1; // West Africa Time + TZ_BST = TZ_WAT; // British Summer Time + TZ_AT = -2; // Azores Time + TZ_ADT = -3; // Atlantic Daylight Time + TZ_AST = -4; // Atlantic Standard Time + TZ_EDT = TZ_AST; // Eastern Daylight Time + TZ_EST = -5; // Eastern Standard Time + TZ_CDT = TZ_EST; // Central Daylight Time + TZ_CST = -6; // Central Standard Time + TZ_MDT = TZ_CST; // Mountain Daylight Time + TZ_MST = -7; // Mountain Standard Time + TZ_PDT = TZ_MST; // Pacific Daylight Time + TZ_PST = -8; // Pacific Standard Time + TZ_YDT = TZ_PST; // Yukon Daylight Time + TZ_YST = -9; // Yukon Standard Time + TZ_HDT = TZ_YST; // Hawaii Daylight Time + TZ_AHST = -10; // Alaska-Hawaii Standard Time + TZ_CAT = TZ_AHST;// Central Alaska Time + TZ_HST = TZ_AHST; // Hawaii Standard Time + TZ_EAST = TZ_AHST;// East Australian Standard Time + TZ_NT = -11; // -None- + TZ_IDLW = -12; // International Date Line West + + // Military Time Zones + TZM_A = TZ_WAT; + TZM_Alpha = TZM_A; + TZM_B = TZ_AT; + TZM_Bravo = TZM_B; + TZM_C = TZ_ADT; + TZM_Charlie = TZM_C; + TZM_D = TZ_AST; + TZM_Delta = TZM_D; + TZM_E = TZ_EST; + TZM_Echo = TZM_E; + TZM_F = TZ_CST; + TZM_Foxtrot = TZM_F; + TZM_G = TZ_MST; + TZM_Golf = TZM_G; + TZM_H = TZ_PST; + TZM_Hotel = TZM_H; + TZM_J = TZ_YST; + TZM_Juliet = TZM_J; + TZM_K = TZ_AHST; + TZM_Kilo = TZM_K; + TZM_L = TZ_NT; + TZM_Lima = TZM_L; + TZM_M = TZ_IDLW; + TZM_Mike = TZM_M; + TZM_N = TZ_CET; + TZM_November = TZM_N; + TZM_O = TZ_EET; + TZM_Oscar = TZM_O; + TZM_P = TZ_BT; + TZM_Papa = TZM_P; + TZM_Q = TZ_ZP4; + TZM_Quebec = TZM_Q; + TZM_R = TZ_ZP5; + TZM_Romeo = TZM_R; + TZM_S = TZ_ZP6; + TZM_Sierra = TZM_S; + TZM_T = TZ_WAST; + TZM_Tango = TZM_T; + TZM_U = TZ_CCT; + TZM_Uniform = TZM_U; + TZM_V = TZ_JST; + TZM_Victor = TZM_V; + TZM_W = TZ_GST; + TZM_Whiskey = TZM_W; + TZM_X = TZ_NT; + TZM_XRay = TZM_X; + TZM_Y = TZ_IDLE; + TZM_Yankee = TZM_Y; + TZM_Z = TZ_GMT; + TZM_Zulu = TZM_Z; + +type + { TODO: I'm sure these are stored in a unit elsewhere... need to find out } {Do not Localize} + TDays = (TDaySun, TDayMon, TDayTue, TDayWed, TDayThu, TDayFri, TDaySat); + TMonths = (TMthJan, TMthFeb, TMthMar, TMthApr, TMthMay, TMthJun, + TMthJul, TMthAug, TMthSep, TMthOct, TMthNov, TMthDec); + + TIdDateTimeStamp = class(TIdBaseComponent) + protected + FDay : Integer; + FIsLeapYear : Boolean; + FMillisecond : Integer; + FSecond : Integer; + FTimeZone : Integer; // Number of minutes + / - from GMT / UTC + FYear : Integer; + + procedure CheckLeapYear; + procedure SetDateFromISO8601(AString : String); + procedure SetTimeFromISO8601(AString : String); + procedure InitComponent; override; + public + procedure AddDays(ANumber : UInt32); + procedure AddHours(ANumber : UInt32); + procedure AddMilliseconds(ANumber : UInt32); + procedure AddMinutes(ANumber : UInt32); + procedure AddMonths(ANumber : UInt32); + procedure AddSeconds(ANumber : UInt32); + procedure AddTDateTime(ADateTime : TDateTime); + procedure AddTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp); + procedure AddTTimeStamp(ATimeStamp : TIdDateTimeStamp); + procedure AddWeeks(ANumber : UInt32); + procedure AddYears(ANumber : UInt32); + + function GetAsISO8601Calendar : String; + function GetAsISO8601Ordinal : String; + function GetAsISO8601Week : String; + function GetAsRFC822 : String; +{TODO : function GetAsRFC977DateTime : String;} + function GetAsTDateTime : TDateTime; + function GetAsTTimeStamp : TIdDateTimeStamp; + function GetAsTimeOfDay : String; // HH:MM:SS + + function GetBeatOfDay : Integer; + function GetDaysInYear : Integer; + function GetDayOfMonth : Integer; + function GetDayOfWeek : Integer; + function GetDayOfWeekName : String; + function GetDayOfWeekShortName : String; + function GetHourOf12Day : Integer; + function GetHourOf24Day : Integer; + function GetIsMorning : Boolean; + function GetMinuteOfDay : Integer; + function GetMinuteOfHour : Integer; + function GetMonthOfYear : Integer; + function GetMonthName : String; + function GetMonthShortName : String; + function GetSecondsInYear : Integer; + function GetSecondOfMinute : Integer; + function GetTimeZoneAsString: String; + function GetTimeZoneHour: Integer; + function GetTimeZoneMinutes: Integer; + function GetWeekOfYear : Integer; + + procedure SetFromDOSDateTime(ADate, ATime : Word); + procedure SetFromISO8601(AString : String); + procedure SetFromRFC822(AString : String); + procedure SetFromTDateTime(ADateTime : TDateTime); + procedure SetFromTTimeStamp(ATimeStamp : TIdDateTimeStamp); + + procedure SetDay(ANumber : Integer); + procedure SetMillisecond(ANumber : Integer); + procedure SetSecond(ANumber : Integer); + procedure SetTimeZone(const Value: Integer); + procedure SetYear(ANumber : Integer); + + procedure SubtractDays(ANumber : UInt32); + procedure SubtractHours(ANumber : UInt32); + procedure SubtractMilliseconds(ANumber : UInt32); + procedure SubtractMinutes(ANumber : UInt32); + procedure SubtractMonths(ANumber : UInt32); + procedure SubtractSeconds(ANumber : UInt32); + procedure SubtractTDateTime(ADateTime : TDateTime); + procedure SubtractTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp); + procedure SubtractTTimeStamp(ATimeStamp : TIdDateTimeStamp); + procedure SubtractWeeks(ANumber : UInt32); + procedure SubtractYears(ANumber : UInt32); + + procedure Zero; + procedure ZeroDate; + procedure ZeroTime; + + property AsISO8601Calendar : String read GetAsISO8601Calendar; + property AsISO8601Ordinal : String read GetAsISO8601Ordinal; + property AsISO8601Week : String read GetAsISO8601Week; + property AsRFC822 : String read GetAsRFC822; + property AsTDateTime : TDateTime read GetAsTDateTime; + property AsTTimeStamp : TIdDateTimeStamp read GetAsTTimeStamp; + property AsTimeOfDay : String read GetAsTimeOfDay; + property BeatOfDay : Integer read GetBeatOfDay; + property Day : Integer read FDay write SetDay; + property DaysInYear : Integer read GetDaysInYear; + property DayOfMonth : Integer read GetDayOfMonth; + property DayOfWeek : Integer read GetDayOfWeek; + property DayOfWeekName : String read GetDayOfWeekName; + property DayOfWeekShortName : String read GetDayOfWeekShortName; + property HourOf12Day : Integer read GetHourOf12Day; + property HourOf24Day : Integer read GetHourOf24Day; + property IsLeapYear : Boolean read FIsLeapYear; + property IsMorning : Boolean read GetIsMorning; + property Millisecond : Integer read FMillisecond write SetMillisecond; + property MinuteOfDay : Integer read GetMinuteOfDay; + property MinuteOfHour : Integer read GetMinuteOfHour; + property MonthOfYear : Integer read GetMonthOfYear; + property MonthName : String read GetMonthName; + property MonthShortName : String read GetMonthShortName; + property Second : Integer read FSecond write SetSecond; + property SecondsInYear : Integer read GetSecondsInYear; + property SecondOfMinute : Integer read GetSecondOfMinute; + property TimeZone : Integer read FTimeZone write SetTimeZone; + property TimeZoneHour : Integer read GetTimeZoneHour; + property TimeZoneMinutes : Integer read GetTimeZoneMinutes; + property TimeZoneAsString : String read GetTimeZoneAsString; + property Year : Integer read FYear write SetYear; + property WeekOfYear : Integer read GetWeekOfYear; + end; + +implementation + +uses + IdGlobalProtocols, + IdStrings, + SysUtils; + +const + MaxWeekAdd : UInt32 = $FFFFFFFF div IdDaysInWeek; + MaxMinutesAdd : UInt32 = $FFFFFFFF div IdSecondsInMinute; + DIGITS : String = '0123456789'; {Do not Localize} + +function LocalDateTimeToTimeStamp(ADateTime: TDateTime): TIdDateTimeStamp; +var + Year, + Month, + Day, + Hour, + Minute, + Second, + MSec: Word; +begin + DecodeDate(ADateTime, Year, Month, Day); + DecodeTime(ADateTime, Hour, Minute, Second, MSec); + Result := TIdDateTimeStamp.Create; + Result.Zero; + Result.AddYears(Year); + Result.AddMonths(Month); + Result.AddDays(Day); + Result.AddHours(Hour); + Result.AddMinutes(Minute); + Result.AddSeconds(Second); + Result.AddMilliseconds(MSec); +end; + +procedure ValidateTimeStamp(const ATimeStamp: TIdDateTimeStamp); +begin + IdGlobal.ToDo('ValidateTimeStamp() function in IdDateTimeStamp.pas is not implemented yet'); {do not localize} +// if (ATimeStamp.Time < 0) or (ATimeStamp.Date <= 0) then +// EIdExceptionBase.CreateFmt('''%d.%d'' is not a valid timestamp', [ATimeStamp.Date, ATimeStamp.Time]); +end; + +function LocalTimeStampToDateTime(const ATimeStamp: TIdDateTimeStamp): TDateTime; +begin + ValidateTimeStamp(ATimeStamp); + Result := EncodeDate(ATimeStamp.Year, ATimeStamp.MonthOfYear, ATimeStamp.DayOfMonth) + + EncodeTime(ATimeStamp.HourOf24Day, ATimeStamp.MinuteOfHour, ATimeStamp.SecondOfMinute, ATimeStamp.Millisecond); +end; + +/////////////////// +// TIdDateTimeStamp +/////////////////// + +procedure TIdDateTimeStamp.InitComponent; +begin + inherited InitComponent; + Zero; + FTimeZone := 0; +end; + +procedure TIdDateTimeStamp.AddDays; +var + i : Integer; +begin + // First 'round off' the current day of the year. This is done to prevent {Do not Localize} + // miscalculations in leap years and also as an optimisation for small + // increments. + if (ANumber > UInt32(DaysInYear - FDay)) and (not (FDay = 1)) then begin + ANumber := ANumber - UInt32(DaysInYear - FDay); + FDay := 0; + AddYears(1); + end else begin + // The number of days added is contained within this year. + FDay := FDay + Integer(ANumber); + if FDay > DaysInYear then + begin + ANumber := FDay; + FDay := 0; + AddDays(ANumber); + end; + Exit; + end; + + if ANumber >= IdDaysInLeapYearCycle then begin + i := ANumber div IdDaysInLeapYearCycle; + AddYears(i * IdYearsInLeapYearCycle); + ANumber := ANumber - UInt32(i * IdDaysInLeapYearCycle); + end; + + if ANumber >= IdDaysInLeapCentury then begin + while ANumber >= IDDaysInLeapCentury do begin + i := FYear div 100; + if i mod 4 = 3 then begin + // Going forward through a 'leap' century {Do not Localize} + AddYears(IdYearsInCentury); + ANumber := ANumber - UInt32(IdDaysInLeapCentury); + end else begin + AddYears(IdYearsInCentury); + ANumber := ANumber - UInt32(IdDaysInCentury); + end; + end; + end; + + if ANumber >= IdDaysInShortLeapYearCycle then begin + i := ANumber div IdDaysInShortLeapYearCycle; + AddYears(i * IdYearsInShortLeapYearCycle); + ANumber := ANumber - UInt32(i * IdDaysInShortLeapYearCycle); + end; + + i := GetDaysInYear; + while Integer(ANumber) > i do begin + AddYears(1); + Dec(ANumber, i); + i := GetDaysInYear; + end; + + if FDay + Integer(ANumber) > i then begin + AddYears(1); + Dec(ANumber, i - FDay); + FDay := ANumber; + end else begin + Inc(FDay, ANumber); + end; +end; + +procedure TIdDateTimeStamp.AddHours; +var + i : UInt32; +begin + i := ANumber div IdHoursInDay; + AddDays(i); + Dec(ANumber, i * IdHoursInDay); + AddSeconds(ANumber * IdSecondsInHour); +end; + +procedure TIdDateTimeStamp.AddMilliseconds; +var + i : UInt32; +begin + i := ANumber div IdMillisecondsInDay; + if i > 0 then begin + AddDays(i); + Dec(ANumber, i * IdMillisecondsInDay); + end; + + i := ANumber div IdMillisecondsInSecond; + if i > 0 then begin + AddSeconds(i); + Dec(ANumber, i * IdMillisecondsInSecond); + end; + + Inc(FMillisecond, ANumber); + while FMillisecond > IdMillisecondsInSecond do begin + // Should only happen once... + AddSeconds(1); + Dec(FMillisecond, IdMillisecondsInSecond); + end; +end; + +procedure TIdDateTimeStamp.AddMinutes; +begin + // Convert down to seconds + while ANumber > MaxMinutesAdd do begin + AddSeconds(MaxMinutesAdd); + Dec(ANumber, MaxMinutesAdd); + end; + + AddSeconds(ANumber * IdSecondsInMinute); +end; + +procedure TIdDateTimeStamp.AddMonths; +var + i : Integer; +begin + i := ANumber div IdMonthsInYear; + AddYears(i); + Dec(ANumber, i * IdMonthsInYear); + + i := MonthOfYear; + while ANumber > 0 do begin + if i = 12 then begin + i := 1; + end; + if (i = 2) and (IsLeapYear) then begin + AddDays(IdDaysInMonth[i] + 1); + end else begin + AddDays(IdDaysInMonth[i]); + end; + Dec(ANumber); + Inc(i); + end; +end; + +procedure TIdDateTimeStamp.AddSeconds; +var + i : UInt32; +begin + i := ANumber Div IdSecondsInDay; + if i > 0 then begin + AddDays(i); + ANumber := ANumber - (i * IdSecondsInDay); + end; + + Inc(FSecond, ANumber); + while FSecond > IdSecondsInDay do begin + // Should only ever happen once... + AddDays(1); + Dec(FSecond, IdSecondsInDay); + end; +end; + +procedure TIdDateTimeStamp.AddTDateTime; +begin +// todo: +// AddTTimeStamp(DateTimeToTimeStamp(ADateTime)); +end; + +procedure TIdDateTimeStamp.AddTIdDateTimeStamp; +begin + { TODO : Check for accuracy } + AddYears(AIdDateTime.Year); + AddDays(AIdDateTime.Day); + AddSeconds(AIdDateTime.Second); + AddMilliseconds(AIdDateTime.Millisecond); +end; + +procedure TIdDateTimeStamp.AddTTimeStamp; +begin + AddTIdDateTimeStamp(ATimeStamp); +end; + +procedure TIdDateTimeStamp.AddWeeks; +begin + // Cannot add years as there are not exactly 52 weeks in the year and there + // is no exact match between weeks and the 400 year leap cycle + + // Convert down to days... + while ANumber > MaxWeekAdd do begin + AddDays(MaxWeekAdd); + Dec(ANumber, MaxWeekAdd); + end; + + AddDays(ANumber * IdDaysInWeek); +end; + +procedure TIdDateTimeStamp.AddYears; +begin + {TODO: Capture overflow because adding UInt32 to Integer } + if (FYear <= -1) and (Integer(ANumber) >= -FYear) then begin + Inc(ANumber); + end; + Inc(FYear, ANumber); + CheckLeapYear; +end; + +procedure TIdDateTimeStamp.CheckLeapYear; +begin + // Nested if done to prevent unnecessary calcs on slower machines + if FYear mod 4 = 0 then begin + if FYear mod 100 = 0 then begin + if FYear mod 400 = 0 then begin + FIsLeapYear := True; + end else begin + FIsLeapYear := False; + end; + end else begin + FIsLeapYear := True; + end; + end else begin + FIsLeapYear := False; + end; + {TODO : If (FIsLeapYear = false) and (FDay = IdDaysInLeapYear) then begin + and, do what? + } +end; + +function TIdDateTimeStamp.GetAsISO8601Calendar : String; +begin + Result := IntToStr(FYear) + '-' {Do not Localize} + + IntToStr(MonthOfYear) + '-' {Do not Localize} + + IntToStr(DayOfMonth) + 'T' {Do not Localize} + + AsTimeOfDay; +end; + +function TIdDateTimeStamp.GetAsISO8601Ordinal : String; +begin + Result := IntToStr(FYear) + '-' {Do not Localize} + + IntToStr(FDay) + 'T' {Do not Localize} + + AsTimeOfDay; +end; + +function TIdDateTimeStamp.GetAsISO8601Week : String; +begin + Result := IntToStr(FYear) + '-W' {Do not Localize} + + IntToStr(WeekOfYear) + '-' {Do not Localize} + + IntToStr(DayOfWeek) + 'T' {Do not Localize} + + AsTimeOfDay; +end; + +function TIdDateTimeStamp.GetAsRFC822 : String; +begin + Result := IdDayShortNames[DayOfWeek] + ', ' {Do not Localize} + + IntToStr(DayOfMonth) + ' ' {Do not Localize} + + IdMonthShortNames[MonthOfYear] + ' ' {Do not Localize} + + IntToStr(Year) + ' ' {Do not Localize} + + AsTimeOfDay + ' ' {Do not Localize} + + TimeZoneAsString; +end; + +function TIdDateTimeStamp.GetAsTDateTime : TDateTime; +begin + Result := LocalTimeStampToDateTime(GetAsTTimeStamp); +end; + +function TIdDateTimeStamp.GetAsTTimeStamp : TIdDateTimeStamp; +begin + Result := Self; +end; + +function TIdDateTimeStamp.GetAsTimeOfDay : String; +begin + Result := IndyFormat('%.2d:%.2d:%.2d', {Do not localize} + [HourOf24Day, MinuteOfHour, SecondOfMinute]); +end; + +function TIdDateTimeStamp.GetBeatOfDay : Integer; +var + i64 : Int64; + DTS : TIdDateTimeStamp; +begin + // Check + if FTimeZone <> TZ_MET then + begin + // Rather than messing about with this instance, create + // a new one. + DTS := TIdDateTimeStamp.Create; + try + DTS.SetYear(FYear); + DTS.SetDay(FDay); + DTS.SetSecond(FSecond); + DTS.SetMillisecond(FMillisecond); + DTS.SetTimeZone(TZ_MET); + DTS.AddMinutes( (TZ_MET * IdMinutesInHour) - FTimeZone); + Result := DTS.GetBeatOfDay; + finally + DTS.Free; + end; + end else + begin + i64 := (FSecond * IdMillisecondsInSecond) + FMillisecond; + i64 := i64 * IdBeatsInDay; + i64 := i64 div IdMillisecondsInDay; + Result := Integer(i64); + end; +end; + +function TIdDateTimeStamp.GetDaysInYear : Integer; +begin + if IsLeapYear then begin + Result := IdDaysInLeapYear; + end else begin + Result := IdDaysInYear; + end; +end; + +function TIdDateTimeStamp.GetDayOfMonth : Integer; +var + count, mnth, days : Integer; +begin + mnth := MonthOfYear; + if IsLeapYear and (mnth > 2) then begin + days := 1; + end else begin + days := 0; + end; + for count := 1 to mnth - 1 do begin + Inc(days, IdDaysInMonth[count]); + end; + days := Day - days; + if days < 0 then begin + Result := 0; + end else begin + Result := days; + end; +end; + +function TIdDateTimeStamp.GetDayOfWeek : Integer; +var + a, y, m, d, mnth : Integer; +begin + // Thanks to the "FAQ About Calendars" by Claus Tndering for this algorithm + // http://www.tondering.dk/claus/calendar.html + mnth := MonthOfYear; + a := (14 - mnth) div 12; + y := Year - a; + m := mnth + (12 * a) - 2; + d := DayOfMonth + y + (y div 4) - (y div 100) + (y div 400) + ((31 * m) div 12); + d := d mod 7; + Result := d + 1; +end; + +function TIdDateTimeStamp.GetDayOfWeekName : String; +begin + result := IdDayNames[GetDayOfWeek]; +end; + +function TIdDateTimeStamp.GetDayOfWeekShortName : String; +begin + result := IdDayShortNames[GetDayOfWeek]; +end; + +function TIdDateTimeStamp.GetHourOf12Day : Integer; +var + hr : Integer; +begin + hr := GetHourOf24Day; + if hr > IdHoursInHalfDay then begin + Dec(hr, IdHoursInHalfDay); + end; + Result := hr; +end; + +function TIdDateTimeStamp.GetHourOf24Day : Integer; +begin + Result := Second div IdSecondsInHour; +end; + +function TIdDateTimeStamp.GetIsMorning : Boolean; +begin + Result := Second <= (IdSecondsInHalfDay + 1); +end; + +function TIdDateTimeStamp.GetMinuteOfDay : Integer; +begin + Result := Second div IdSecondsInMinute; +end; + +function TIdDateTimeStamp.GetMinuteOfHour : Integer; +begin + Result := GetMinuteOfDay - (IdMinutesInHour * GetHourOf24Day); +end; + +function TIdDateTimeStamp.GetMonthOfYear : Integer; +var + AddOne, Count : Byte; + Today : Integer; +begin + Result := 0; + if IsLeapYear then begin + AddOne := 1; + end else begin + AddOne := 0; + end; + Today := Day; + Count := 1; + while Count <> 13 do begin + if Count = 2 then begin + if Today > IdDaysInMonth[Count] + AddOne then begin + Dec(Today, IdDaysInMonth[Count] + AddOne); + end else begin + Result := Count; + Break; + end; + end else begin + if Today > IdDaysInMonth[Count] then begin + Dec(Today, IdDaysInMonth[Count]); + end else begin + Result := Count; + Break; + end; + end; + Inc(Count); + end; +end; + +function TIdDateTimeStamp.GetMonthName : String; +begin + Result := IdMonthNames[MonthOfYear]; +end; + +function TIdDateTimeStamp.GetMonthShortName : String; +begin + Result := IdMonthShortNames[MonthOfYear]; +end; + +function TIdDateTimeStamp.GetSecondsInYear : Integer; +begin + if IsLeapYear then begin + Result := IdSecondsInLeapYear; + end else begin + Result := IdSecondsInYear; + end; +end; + +function TIdDateTimeStamp.GetSecondOfMinute : Integer; +begin + Result := Second - (GetMinuteOfDay * IdSecondsInMinute); +end; + +function TIdDateTimeStamp.GetTimeZoneAsString: String; +var + i : Integer; +begin + i := GetTimeZoneHour; + if i < 0 then begin + if i < -9 then begin + Result := IntToStr(i); + end else begin + Result := '-0' + IntToStr(Abs(i)); {Do not Localize} + end; + end + else if i <= 9 then begin + Result := '+0' + IntToStr(i); {Do not Localize} + end else + begin + Result := '+' + IntToStr(i); {Do not Localize} + end; + i := GetTimeZoneMinutes; + if i <= 9 then begin + Result := Result + '0'; {Do not Localize} + end; + Result := Result + IntToStr(i); +end; + +function TIdDateTimeStamp.GetTimeZoneHour: Integer; +begin + Result := FTimeZone div 60; +end; + +function TIdDateTimeStamp.GetTimeZoneMinutes: Integer; +begin + Result := Abs(FTimeZone) mod 60; +end; + +function TIdDateTimeStamp.GetWeekOfYear : Integer; +var + w : Integer; + DT : TIdDateTimeStamp; +begin + DT := TIdDateTimeStamp.Create; + try + DT.SetYear(Year); + w := DT.DayOfWeek; // Get the first day of this year & hence number of + // days of the first week that are in the previous year + w := w + Day - 2; // Get complete weeks + w := w div 7; + Result := w + 1; + finally + DT.Free; + end; +end; + +procedure TIdDateTimeStamp.SetFromDOSDateTime(ADate, ATime: Word); +begin + Zero; + SetYear(1980); + AddYears(ADate shr 9); + AddMonths(((ADate and $1E0) shr 5) - 1); + AddDays((ADate and $1F) - 1); + AddHours(ATime shr 11); + AddMinutes((ATime and $7E0) shr 5); + AddSeconds((ATime and $1F) - 1); +end; + +procedure TIdDateTimeStamp.SetDateFromISO8601(AString: String); +var + i, week : Integer; + s : String; +begin + // AString should be in one of three formats: + // Calender - YYYY-MM-DD + // Ordinal - YYYY-XXX where XXX is the day of the year + // Week - YYYY-WXX-D where W is a literal and XX is the week of the year. + i := IndyPos('-', AString); {Do not Localize} + if i > 0 then + begin + s := Trim(Copy(AString, 1, i - 1)); + AString := Trim(Copy(AString, i + 1, MaxInt)); + i := FindFirstNotOf('0123456789', s); {Do not Localize} + if i = 0 then + begin + SetYear(IndyStrToInt(s)); + if Length(AString) > 0 then + begin + i := IndyPos('-', AString); {Do not Localize} + if TextStartsWith(AString, 'W') then {Do not Localize} + begin + // Week format + s := Trim(Copy(AString, 2, i - 2)); + AString := Trim(Copy(AString, i + 1, MaxInt)); + + week := -1; + i := -1; + if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then + begin + i := IndyStrToInt(AString); + end; + + if (Length(s) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then + begin + week := IndyStrToInt(s); + end; + + if (week > 0) and (i >= 0) then + begin + Dec(week); + FDay := 1 + (IdDaysInWeek * week); + + // Now have the correct week of the year + if i < GetDayOfWeek then begin + SubtractDays(GetDayOfWeek - i); + end else begin + AddDays(i - GetDayOfWeek); + end; + end; + end + else if i > 0 then + begin + // Calender format + s := Trim(Copy(AString, 1, i - 1)); + AString := Trim(Copy(AString, i + 1, MaxInt)); + + // Set the day first due to internal format. + if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, s) = 0) then + begin + SetDay(IndyStrToInt(AString)); + end; + + // Add the months. + if (Length(s) > 0) and (FindFirstNotOf(DIGITS, s) = 0) then + begin + AddMonths(IndyStrToInt(s) - 1); + end; + end else + begin + // Ordinal format + i := FindFirstNotOf(DIGITS, AString); + if i = 0 then begin + SetDay(IndyStrToInt(AString)); + end; + end; + end; + end; + end; +end; + +procedure TIdDateTimeStamp.SetTimeFromISO8601(AString: String); +var + i : Integer; + Hour, Minute : String; +begin + // AString should be in the format of HH:MM:SS where : is a literal. + i := IndyPos(':', AString); {Do not Localize} + Hour := Trim(Copy(AString, 1, i - 1)); + AString := Trim(Copy(AString, i + 1, MaxInt)); + + i := IndyPos(':', AString); {Do not Localize} + Minute := Trim(Copy(AString, 1, i - 1)); + AString := Trim(Copy(AString, i + 1, MaxInt)); + + // Set seconds first due to internal format. + if (Length(AString) > 0) and (FindFirstNotOf(DIGITS, AString) = 0) then + begin + SetSecond(IndyStrToInt(AString)); + end; + + if (Length(Minute) > 0) and (FindFirstNotOf(DIGITS, Minute) = 0) then + begin + AddMinutes(IndyStrToInt(Minute)); + end; + + if (Length(Hour) > 0) and (FindFirstNotOf(DIGITS, Hour) = 0) then + begin + AddHours(IndyStrToInt(Hour)); + end; +end; + +procedure TIdDateTimeStamp.SetFromISO8601(AString: String); +var + i : Integer; +begin + Zero; + i := IndyPos('T', AString); {Do not Localize} + if i > 0 then + begin + SetDateFromISO8601(Trim(Copy(AString, 1, i - 1))); + SetTimeFromISO8601(Trim(Copy(AString, i + 1, MaxInt))); + end else + begin + SetDateFromISO8601(AString); + SetTimeFromISO8601(AString); + end; +end; + +procedure TIdDateTimeStamp.SetFromRFC822(AString: String); +begin + SetFromTDateTime(StrInternetToDateTime(AString)) +end; + +procedure TIdDateTimeStamp.SetFromTDateTime(ADateTime : TDateTime); +var + LStamp: TIdDateTimeStamp; +begin + LStamp := LocalDateTimeToTimeStamp(ADateTime); + try + SetFromTTimeStamp(LStamp); + finally + FreeAndNil(LStamp); + end; +end; + +procedure TIdDateTimeStamp.SetFromTTimeStamp(ATimeStamp : TIdDateTimeStamp); +begin + FDay := ATimeStamp.Day; + FMillisecond := ATimeStamp.Millisecond; + FIsLeapYear := ATimeStamp.IsLeapYear; + FSecond := ATimeStamp.Second; + FTimeZone := ATimeStamp.TimeZone; + FYear := ATimeStamp.Year; +end; + +procedure TIdDateTimeStamp.SetDay(ANumber : Integer); +begin + if ANumber > 0 then begin + FDay := 0; + AddDays(ANumber); + end else begin + FDay := 1; + end; +end; + +procedure TIdDateTimeStamp.SetMillisecond(ANumber : Integer); +begin + FMillisecond := 0; + AddMilliseconds(ANumber); +end; + +procedure TIdDateTimeStamp.SetSecond(ANumber : Integer); +begin + FSecond := 0; + AddSeconds(ANumber); +end; + +procedure TIdDateTimeStamp.SetTimeZone(const Value: Integer); +begin + FTimeZone := Value; +end; + +procedure TIdDateTimeStamp.SetYear(ANumber : Integer); +begin + If ANumber = 0 then begin + FYear := 1; + end else begin + FYear := ANumber; + end; + CheckLeapYear; +end; + +procedure TIdDateTimeStamp.SubtractDays(ANumber : UInt32); +var + i : Integer; +begin + if ANumber = 0 then begin + Exit; + end; + + // First remove the number of days in this year. As with AddDays this + // is both an optimisation and a fix for calculations that begin in leap years. + if ANumber >= UInt32(FDay - 1) then begin + ANumber := ANumber - UInt32(FDay - 1); + FDay := 1; + end else begin + FDay := FDay - Integer(ANumber); + end; + + // Subtract the number of whole leap year cycles = 400 years + if ANumber >= IdDaysInLeapYearCycle then begin + i := ANumber div IdDaysInLeapYearCycle; + SubtractYears(i * IdYearsInLeapYearCycle); + ANumber := ANumber - UInt32(i * IdDaysInLeapYearCycle); + end; + + // Next subtract the centuries, checking for the century that is passed through + if ANumber >= IdDaysInLeapCentury then begin + while ANumber >= IdDaysInLeapCentury do begin + i := FYear div 100; + if i mod 4 = 0 then begin + // Going back through a 'leap' century {Do not Localize} + SubtractYears(IdYearsInCentury); + ANumber := ANumber - UInt32(IdDaysInLeapCentury); + end else begin + SubtractYears(IdYearsInCentury); + ANumber := ANumber - UInt32(IdDaysInCentury); + end; + end; + end; + + // Subtract multiples of 4 ("Short" Leap year cycle) + if ANumber >= IdDaysInShortLeapYearCycle then begin + while ANumber >= IdDaysInShortLeapYearCycle do begin + // Round off current year to nearest four. + i := (FYear shr 2) shl 2; + if SysUtils.IsLeapYear(i) then begin + // Normal + SubtractYears(IdYearsInShortLeapYearCycle); + ANumber := ANumber - UInt32(IdDaysInShortLeapYearCycle); + end else begin + // Subtraction crosses a 100-year (but not 400-year) boundary. Add the + // same number of years, but one less day. + SubtractYears(IdYearsInShortLeapYearCycle); + ANumber := ANumber - UInt32(IdDaysInShortNonLeapYearCycle); + end; + end; + end; + + // Now the individual years + while ANumber > UInt32(DaysInYear) do begin + SubtractYears(1); + Dec(ANumber, DaysInYear); + if Self.IsLeapYear then begin + // Correct the assumption of a non-leap year + AddDays(1); + end; + end; + + // and finally the remainders + if ANumber >= UInt32(FDay) then begin + SubtractYears(1); + ANumber := ANumber - UInt32(FDay); + Day := DaysInYear - Integer(ANumber); + end else begin + Dec(FDay, ANumber); + end; + +end; + +procedure TIdDateTimeStamp.SubtractHours(ANumber : UInt32); +var + i : UInt32; +begin + i := ANumber div IdHoursInDay; + SubtractDays(i); + Dec(ANumber, i * IdHoursInDay); + SubtractSeconds(ANumber * IdSecondsInHour); +end; + +procedure TIdDateTimeStamp.SubtractMilliseconds(ANumber : UInt32); +var + i : UInt32; +begin + if ANumber = 0 then begin + Exit; + end; + + i := ANumber div IdMillisecondsInDay; + SubtractDays(i); + Dec(ANumber, i * IdMillisecondsInDay); + + i := ANumber div IdMillisecondsInSecond; + SubtractSeconds(i); + Dec(ANumber, i * IdMillisecondsInSecond); + + Dec(FMillisecond, ANumber); + while FMillisecond <= 0 do begin + SubtractSeconds(1); + // FMillisecond is already negative, so add it. + FMillisecond := IdMillisecondsInSecond + FMillisecond; + end; +end; + +procedure TIdDateTimeStamp.SubtractMinutes(ANumber : UInt32); +begin + // Down size to seconds + while ANumber > MaxMinutesAdd do begin + SubtractSeconds(MaxMinutesAdd * IdSecondsInMinute); + Dec(ANumber, MaxMinutesAdd); + end; + SubtractSeconds(ANumber * IdSecondsInMinute); +end; + +procedure TIdDateTimeStamp.SubtractMonths(ANumber : UInt32); +var + i : Integer; +begin + i := ANumber div IdMonthsInYear; + SubtractYears(i); + Dec(ANumber, i * IdMonthsInYear); + + while ANumber > 0 do begin + i := MonthOfYear; + if i = 1 then begin + i := 13; + end; + if (i = 3) and (IsLeapYear) then begin + SubtractDays(IdDaysInMonth[2] + 1); + end else begin + SubtractDays(IdDaysInMonth[i - 1]); + end; + Dec(ANumber); + end; +end; + +procedure TIdDateTimeStamp.SubtractSeconds(ANumber : UInt32); +var + i : UInt32; +begin + if ANumber = 0 then begin + Exit; + end; + + i := ANumber div IdSecondsInDay; + SubtractDays(i); + Dec(ANumber, i * IdSecondsInDay); + + Dec(FSecond, ANumber); + If FSecond < 0 then begin + SubtractDays(1); + FSecond := IdSecondsInDay + FSecond; + end; +end; + +procedure TIdDateTimeStamp.SubtractTDateTime(ADateTime : TDateTime); +var LStamp : TIdDateTimeStamp; +begin + LStamp := LocalDateTimeToTimeStamp(ADateTime); + try + SubtractTIdDateTimeStamp(LStamp); + finally + FreeAndNil(LStamp); + end; +end; + +procedure TIdDateTimeStamp.SubtractTIdDateTimeStamp(AIdDateTime : TIdDateTimeStamp); +begin + { TODO : Check for accuracy } + SubtractYears(AIdDateTime.Year); + SubtractDays(AIdDateTime.Day); + SubtractSeconds(AIdDateTime.Second); + SubtractMilliseconds(AIdDateTime.Millisecond); +end; + +procedure TIdDateTimeStamp.SubtractTTimeStamp(ATimeStamp : TIdDateTimeStamp); +begin + SubtractTIdDateTimeStamp(ATimeStamp); +end; + +procedure TIdDateTimeStamp.SubtractWeeks(ANumber : UInt32); +begin + if ANumber = 0 then begin + Exit; + end; + + // Down size to subtracting Days + while ANumber > MaxWeekAdd do begin + SubtractDays(MaxWeekAdd * IdDaysInWeek); + Dec(ANumber, MaxWeekAdd * IdDaysInWeek); + end; + SubtractDays(ANumber * IdDaysInWeek); +end; + +procedure TIdDateTimeStamp.SubtractYears(ANumber : UInt32); +begin + if (FYear > 0) and (ANumber >= UInt32(FYear)) then begin + Inc(ANumber); + end; + FYear := FYear - Integer(ANumber); + CheckLeapYear; +end; + +procedure TIdDateTimeStamp.Zero; +begin + ZeroDate; + ZeroTime; + FTimeZone := 0; +end; + +procedure TIdDateTimeStamp.ZeroDate; +begin + SetYear(1); + SetDay(1); +end; + +procedure TIdDateTimeStamp.ZeroTime; +begin + SetSecond(0); + SetMillisecond(0); +end; + +end. diff --git a/indy/Protocols/IdDayTime.pas b/indy/Protocols/IdDayTime.pas new file mode 100644 index 0000000..dc79851 --- /dev/null +++ b/indy/Protocols/IdDayTime.pas @@ -0,0 +1,84 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 1/21/2004 2:12:40 PM JPMugaas + InitComponent + + Rev 1.2 12/8/2002 07:26:30 PM JPMugaas + Added published host and port properties. + + Rev 1.1 12/6/2002 05:29:28 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.0 11/14/2002 02:17:02 PM JPMugaas + + 2000-April-30 J. Peter Mugaas + changed to drop control charactors and spaces from result to ease + parsing +} + +unit IdDayTime; + +{*******************************************************} +{ } +{ Indy QUOTD Client TIdDayTime } +{ } +{ Copyright (C) 2000 Winshoes WOrking Group } +{ Started by J. Peter Mugaas } +{ 2000-April-23 } +{ } +{*******************************************************} + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdTCPClient; + +type + TIdDayTime = class(TIdTCPClientCustom) + protected + Function GetDayTimeStr : String; + procedure InitComponent; override; + public + Property DayTimeStr : String read GetDayTimeStr; + published + property Port default IdPORT_DAYTIME; + property Host; + end; + +implementation + +uses + IdGlobal, SysUtils; + +{ TIdDayTime } + +procedure TIdDayTime.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_DAYTIME; +end; + +function TIdDayTime.GetDayTimeStr: String; +begin + Result := Trim ( ConnectAndGetAll ); +end; + +end. diff --git a/indy/Protocols/IdDayTimeServer.pas b/indy/Protocols/IdDayTimeServer.pas new file mode 100644 index 0000000..bfd11bb --- /dev/null +++ b/indy/Protocols/IdDayTimeServer.pas @@ -0,0 +1,102 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 12/2/2004 4:23:50 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.3 1/21/2004 2:12:40 PM JPMugaas + InitComponent + + Rev 1.2 1/17/2003 05:35:18 PM JPMugaas + Now compiles with new design. + + Rev 1.1 1-1-2003 20:12:48 BGooijen + Changed to support the new TIdContext class + + Rev 1.0 11/14/2002 02:17:06 PM JPMugaas + +2000-Apr-22: J Peter Mugass + -Ported to Indy + +1999-Apr-13 + -Final Version + +2000-JAN-13 MTL + -Moved to new Palette Scheme (Winshoes Servers) +} + +unit IdDayTimeServer; + +{ +Original Author: Ozz Nixon +} + +interface +{$i IdCompilerDefines.inc} + +uses + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + Classes, + {$ENDIF} + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Type + TIdDayTimeServer = class(TIdCustomTCPServer) + protected + FTimeZone: String; + // + function DoExecute(AContext:TIdContext): boolean; override; + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + published + property TimeZone: String read FTimeZone write FTimeZone; + property DefaultPort default IdPORT_DAYTIME; + end; + +implementation + +uses + IdGlobal, SysUtils; + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDayTimeServer.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDayTimeServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_DAYTIME; + FTimeZone := 'EST'; {Do not Localize} +end; + +function TIdDayTimeServer.DoExecute(AContext:TIdContext ): boolean; +begin + Result := True; + AContext.Connection.IOHandler.WriteLn(FormatDateTime('dddd, mmmm dd, yyyy hh:nn:ss', Now) + '-' + FTimeZone); {Do not Localize} + AContext.Connection.Disconnect; +end; + +end. diff --git a/indy/Protocols/IdDayTimeUDP.pas b/indy/Protocols/IdDayTimeUDP.pas new file mode 100644 index 0000000..e62d45b --- /dev/null +++ b/indy/Protocols/IdDayTimeUDP.pas @@ -0,0 +1,60 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 1/21/2004 2:12:42 PM JPMugaas + InitComponent + + Rev 1.0 11/14/2002 02:17:14 PM JPMugaas +} + +unit IdDayTimeUDP; + +interface +{$i IdCompilerDefines.inc} +uses + IdAssignedNumbers, IdUDPBase, IdUDPClient; + +type + TIdDayTimeUDP = class(TIdUDPClient) + protected + Function GetDayTimeStr : String; + procedure InitComponent; override; + public + Property DayTimeStr : String read GetDayTimeStr; + published + property Port default IdPORT_DAYTIME; + end; + +implementation + +{ TIdDayTimeUDP } + +procedure TIdDayTimeUDP.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_DAYTIME; +end; + +function TIdDayTimeUDP.GetDayTimeStr: String; +begin + //The string can be anything - The RFC says the server should discard packets + Send(' '); {Do not Localize} + Result := ReceiveString; +end; + +end. diff --git a/indy/Protocols/IdDayTimeUDPServer.pas b/indy/Protocols/IdDayTimeUDPServer.pas new file mode 100644 index 0000000..9407e2f --- /dev/null +++ b/indy/Protocols/IdDayTimeUDPServer.pas @@ -0,0 +1,92 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 2004.02.03 5:45:04 PM czhower + Name changes + + Rev 1.3 1/21/2004 2:12:44 PM JPMugaas + InitComponent + + Rev 1.2 10/24/2003 02:54:52 PM JPMugaas + These should now work with the new code. + + Rev 1.1 2003.10.24 10:38:24 AM czhower + UDP Server todos + + Rev 1.0 11/14/2002 02:17:18 PM JPMugaas +} + +unit IdDayTimeUDPServer; + +interface +{$i IdCompilerDefines.inc} + +uses + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + Classes, + {$ENDIF} + IdAssignedNumbers, IdGlobal, IdSocketHandle, IdUDPBase, IdUDPServer; + +type + TIdDayTimeUDPServer = class(TIdUDPServer) + protected + FTimeZone : String; + procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); override; + procedure InitComponent; override; + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + public + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + published + property TimeZone: String read FTimeZone write FTimeZone; + property DefaultPort default IdPORT_DAYTIME; + end; + +implementation + +uses + SysUtils; + +{ TIdDayTimeUDPServer } + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdDayTimeUDPServer.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdDayTimeUDPServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_DAYTIME; + FTimeZone := 'EST'; {Do not Localize} +end; + +procedure TIdDayTimeUDPServer.DoUDPRead(AThread: TIdUDPListenerThread; + const AData: TIdBytes; ABinding: TIdSocketHandle); +var + s : String; +begin + inherited DoUDPRead(AThread, AData, ABinding); + s := FormatDateTime('dddd, mmmm dd, yyyy hh:nn:ss', Now) + ' -' + FTimeZone; {Do not Localize} + ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, ToBytes(s), ABinding.IPVersion); +end; + +end. + diff --git a/indy/Protocols/IdDeprecatedImplBugOff.inc b/indy/Protocols/IdDeprecatedImplBugOff.inc new file mode 100644 index 0000000..1e8eee8 --- /dev/null +++ b/indy/Protocols/IdDeprecatedImplBugOff.inc @@ -0,0 +1,4 @@ +{$IFDEF DEPRECATED_IMPL_BUG} + {$WARN SYMBOL_DEPRECATED OFF} +{$ENDIF} + diff --git a/indy/Protocols/IdDeprecatedImplBugOn.inc b/indy/Protocols/IdDeprecatedImplBugOn.inc new file mode 100644 index 0000000..43d7f77 --- /dev/null +++ b/indy/Protocols/IdDeprecatedImplBugOn.inc @@ -0,0 +1,8 @@ +{$IFDEF DEPRECATED_IMPL_BUG} + {$IFDEF HAS_DIRECTIVE_WARN_DEFAULT} + {$WARN SYMBOL_DEPRECATED DEFAULT} + {$ELSE} + {$WARN SYMBOL_DEPRECATED ON} + {$ENDIF} +{$ENDIF} + diff --git a/indy/Protocols/IdDiscardServer.pas b/indy/Protocols/IdDiscardServer.pas new file mode 100644 index 0000000..632eef4 --- /dev/null +++ b/indy/Protocols/IdDiscardServer.pas @@ -0,0 +1,97 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 12/2/2004 4:23:50 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.7 1/21/2004 2:12:46 PM JPMugaas + InitComponent + + Rev 1.6 2003.11.29 10:18:48 AM czhower + Updated for core change to InputBuffer. + + Rev 1.5 3/6/2003 5:08:48 PM SGrobety + Updated the read buffer methodes to fit the new core (InputBuffer -> + InputBufferAsString + call to CheckForDataOnSource) + + Rev 1.4 2/24/2003 08:33:44 PM JPMugaas + + Rev 1.3 1/17/2003 05:35:12 PM JPMugaas + Now compiles with new design. + + Rev 1.2 1-1-2003 20:12:56 BGooijen + Changed to support the new TIdContext class + + Rev 1.1 12/6/2002 02:35:28 PM JPMugaas + Now compiles with Indy 10. + + Rev 1.0 11/14/2002 02:18:08 PM JPMugaas + +2000-Apr-22: J Peter Mugass + Ported to Indy + +1999-Apr-13 + Final Version + +2000-JAN-13 MTL + Moved to new Palette Scheme (Winshoes Servers) +} + +unit IdDiscardServer; + +{ +Original Author: Ozz Nixon +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Type + TIdDISCARDServer = class ( TIdCustomTCPServer ) + protected + function DoExecute(AContext:TIdContext ): Boolean; override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_DISCARD; + end; + +implementation + +uses + IdGlobal; + +procedure TIdDISCARDServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_DISCARD; +end; + +function TIdDISCARDServer.DoExecute(AContext:TIdContext): Boolean; +begin + Result := True; + // Discard it + AContext.Connection.IOHandler.CheckForDataOnSource; + AContext.Connection.IOHandler.InputBuffer.Clear; +end; + +end. diff --git a/indy/Protocols/IdDiscardUDPServer.pas b/indy/Protocols/IdDiscardUDPServer.pas new file mode 100644 index 0000000..f028f8e --- /dev/null +++ b/indy/Protocols/IdDiscardUDPServer.pas @@ -0,0 +1,52 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 1/21/2004 2:12:48 PM JPMugaas + InitComponent + + Rev 1.0 11/14/2002 02:18:14 PM JPMugaas +} + +unit IdDiscardUDPServer; + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, IdSocketHandle, IdUDPBase, IdUDPServer; + +type + TIdDiscardUDPServer = class(TIdUDPServer) + protected + procedure InitComponent; override; + published + property DefaultPort default IdPORT_DISCARD; + end; + +implementation + +{ TIdDiscardUDPServer } + +procedure TIdDiscardUDPServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_DISCARD; +end; + +end. + diff --git a/indy/Protocols/IdDsnRegister.pas b/indy/Protocols/IdDsnRegister.pas new file mode 100644 index 0000000..1d6734c --- /dev/null +++ b/indy/Protocols/IdDsnRegister.pas @@ -0,0 +1,226 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 9/5/2004 3:16:58 PM JPMugaas + Should work in D9 DotNET. + + Rev 1.6 3/8/2004 10:14:54 AM JPMugaas + Property editor for SASL mechanisms now supports TIdDICT. + + Rev 1.5 2/26/2004 8:53:14 AM JPMugaas + Hack to restore the property editor for SASL mechanisms. + + Rev 1.4 1/25/2004 4:28:42 PM JPMugaas + Removed a discontinued Unit. + + Rev 1.3 1/25/2004 3:11:06 PM JPMugaas + SASL Interface reworked to make it easier for developers to use. + SSL and SASL reenabled components. + + Rev 1.2 10/12/2003 1:49:28 PM BGooijen + Changed comment of last checkin + + Rev 1.1 10/12/2003 1:43:28 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/14/2002 02:18:56 PM JPMugaas +} + +unit IdDsnRegister; + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + {$IFDEF DOTNET} + Borland.Vcl.Design.DesignIntF, + Borland.Vcl.Design.DesignEditors + {$ELSE} + {$IFDEF FPC} + PropEdits, + ComponentEditors + {$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + DesignIntf, + DesignEditors + {$ELSE} + Dsgnintf + {$ENDIF} + {$ENDIF} + {$ENDIF} + ; +// Procs + +type + TIdPropEdSASL = class(TClassProperty) + public + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + function GetValue: string; override; + procedure SetValue(const Value: string); override; + end; + + {$IFDEF TSelectionEditor} + {$IFDEF USE_OPENSSL} + TIdOpenSSLSelectionEditor = class(TSelectionEditor) + public + procedure RequiresUnits(Proc: TGetStrProc); override; + end; + {$ENDIF} + TIdFTPServerSelectionEditor = class(TSelectionEditor) + public + procedure RequiresUnits(Proc: TGetStrProc); override; + end; + {$ENDIF} + +procedure Register; + +implementation + +uses + IdDsnResourceStrings, + {$IFDEF WIDGET_WINFORMS} + IdDsnSASLListEditorFormNET, + {$R 'IdDsnSASLListEditorFormNET.TfrmSASLListEditor.resources' 'IdDsnSASLListEditorFormNET.resx'} + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + IdDsnSASLListEditorFormVCL, + {$ENDIF} + {$IFDEF TSelectionEditor} + {$IFDEF USE_OPENSSL} + IdSSLOpenSSL, + {$ENDIF} + IdFTPServer, + {$ENDIF} + IdSASL, IdSASLCollection, + SysUtils, TypInfo; + {Since we are removing New Design-Time part, we remove the "New Message Part Editor"} + {IdDsnNewMessagePart, } + +type + {$IFDEF WIDGET_WINFORMS} + //we make a create here because I'm not sure how the Visual Designer for WinForms + //we behave in a package. I know it can act weird if something is renamed + TfrmSASLListEditor = class(IdDsnSASLListEditorFormNET.TfrmSASLListEditor) + public + constructor Create(AOwner : TComponent); + end; + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + TfrmSASLListEditor = class(TfrmSASLListEditorVCL); + {$ENDIF} + +{ TfrmSASLListEditor } + +{$IFDEF WIDGET_WINFORMS} +constructor TfrmSASLListEditor.Create(AOwner : TComponent); +begin + inherited Create; +end; +{$ENDIF} + +{$IFDEF TSelectionEditor} + + {$IFDEF USE_OPENSSL} + +{TIdOpenSSLSelectionEditor} + +procedure TIdOpenSSLSelectionEditor.RequiresUnits(Proc: TGetStrProc); +begin + inherited RequiresUnits(Proc); + //for new callback event + Proc('IdCTypes'); + Proc('IdSSLOpenSSLHeaders'); +end; + + {$ENDIF} + +{TIdFTPServerSelectionEditor} + +procedure TIdFTPServerSelectionEditor.RequiresUnits(Proc: TGetStrProc); +begin + inherited RequiresUnits(Proc); + Proc('IdFTPListOutput'); + Proc('IdFTPList'); +end; + +{$ENDIF} + +{ TIdPropEdSASL } + +procedure TIdPropEdSASL.Edit; +var + LF: TfrmSASLListEditor; + LComp: TPersistent; + LList: TIdSASLEntries; +begin + inherited Edit; + + LComp := GetComponent(0); + + //done this way to prevent invalid typecast error. + {$IFDEF HAS_GetObjectProp} + LList := TIdSASLEntries(GetObjectProp(LComp, GetPropInfo, TIdSASLEntries)); + {$ELSE} + LList := TObject(GetOrdProp(LComp, GetPropInfo)) as TIdSASLEntries; + {$ENDIF} + + LF := TfrmSASLListEditor.Create(nil); + try + if LComp is TComponent then begin + LF.SetComponentName(TComponent(LComp).Name); + end; + LF.SetList(LList); + if LF.Execute then begin + LF.GetList(LList); + end; + finally + FreeAndNil(LF); + end; +end; + +function TIdPropEdSASL.GetAttributes: TPropertyAttributes; +begin + Result := inherited GetAttributes + [paDialog]; +end; + +function TIdPropEdSASL.GetValue: string; +begin + Result := GetStrValue; +end; + +procedure TIdPropEdSASL.SetValue(const Value: string); +begin + inherited SetValue(Value); +end; + +procedure Register; +begin + RegisterPropertyEditor(TypeInfo(TIdSASLEntries), nil, '', TIdPropEdSASL); + {$IFDEF TSelectionEditor} + {$IFDEF USE_OPENSSL} + RegisterSelectionEditor(TIdServerIOHandlerSSLOpenSSL, TIdOpenSSLSelectionEditor); + RegisterSelectionEditor(TIdSSLIOHandlerSocketOpenSSL, TIdOpenSSLSelectionEditor); + {$ENDIF} + RegisterSelectionEditor(TIdFTPServer,TIdFTPServerSelectionEditor); + {$ENDIF} +end; + +end. diff --git a/indy/Protocols/IdDsnResourceStrings.pas b/indy/Protocols/IdDsnResourceStrings.pas new file mode 100644 index 0000000..df6727d --- /dev/null +++ b/indy/Protocols/IdDsnResourceStrings.pas @@ -0,0 +1,68 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/14/2002 02:19:02 PM JPMugaas +} + +unit IdDsnResourceStrings; + +{ This is only for resource strings that appear in the design-time editors in the main Indy package } + +interface +//Here just so we can IFDEF some things for a Lazarus Workaround +//This should make things a little easier for GNU Make. +{$I IdCompilerDefines.inc} + +resourcestring + {Binding Editor stuff} + + { + Note to translators - Please Read!!! + + & symbol before a letter or number. This is rendered as that chractor being + underlined. In addition, the charactor after the & symbol along with the ALT + key enables a user to move to that control. Since these are on one form, be + careful to ensure that the same letter or number does not have a & before it + in more than one string, otherwise an ALT key sequence will be broken. + } + + {Design time SASLList Hints} + RSADlgSLMoveUp = 'Move Up'; + RSADlgSLMoveDown = 'Move Down'; + RSADlgSLAdd = 'Add'; + RSADlgSLRemove = 'Remove'; + //Caption that uses format with component name + RSADlgSLCaption = 'Editing SASL List for %s'; + RSADlgSLAvailable = '&Available'; + RSADlgSLAssigned = 'A&ssigned (tried in order listed)'; + {Note that the Ampersand indicates an ALT keyboard sequence} + RSADlgSLEditList = 'Edit &List'; +{$IFDEF FPC} + //This is part of a workaround for the Lazarus IDE Toolbar being + //unable to scroll. + RSProt = ' Protocols'; + RSProtam = ' Protocols (am)'; + RSProtnz = ' Protocols (nz)'; + RSMappedPort = ' Mapped Port'; + RSEncoder = ' Encoder'; + RSDecoder = ' Decoder'; +{$ENDIF} + +implementation + +end. diff --git a/indy/Protocols/IdDsnSASLListEditor.pas b/indy/Protocols/IdDsnSASLListEditor.pas new file mode 100644 index 0000000..c371a0d --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditor.pas @@ -0,0 +1,126 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.6 9/5/2004 3:16:58 PM JPMugaas + Should work in D9 DotNET. + + Rev 1.5 3/8/2004 10:14:56 AM JPMugaas + Property editor for SASL mechanisms now supports TIdDICT. + + Rev 1.4 2/26/2004 8:53:16 AM JPMugaas + Hack to restore the property editor for SASL mechanisms. + + Rev 1.3 1/25/2004 3:11:08 PM JPMugaas + SASL Interface reworked to make it easier for developers to use. + SSL and SASL reenabled components. + + Rev 1.2 10/12/2003 1:49:30 PM BGooijen + Changed comment of last checkin + + Rev 1.1 10/12/2003 1:43:30 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/14/2002 02:19:08 PM JPMugaas +} + +unit IdDsnSASLListEditor; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF DOTNET} + Borland.Vcl.Design.DesignIntf, + Borland.Vcl.Design.DesignEditors + {$ELSE} + {$IFDEF FPC} + PropEdits, + ComponentEditors + {$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + DesignIntf, + DesignEditors + {$ELSE} + Dsgnintf + {$ENDIF} + {$ENDIF} + {$ENDIF} + ; + +type + TIdPropEdSASL = class(TClassProperty) + public + procedure Edit; override; + function GetAttributes: TPropertyAttributes; override; + function GetValue: string; override; + procedure SetValue(const Value: string); override; + end; + +implementation + +uses + Classes, + IdDsnResourceStrings, IdDsnSASLListEditorForm, + IdSASL, IdSASLCollection, + SysUtils, TypInfo; + +{ TIdPropEdSASL } + +procedure TIdPropEdSASL.Edit; +var + LF: TfrmSASLListEditor; + LComp: TPersistent; + LList: TIdSASLEntries; +begin + inherited Edit; + + LComp := GetComponent(0); + //done this way to prevent invalid typecast error. + LList := TIdSASLEntries(TObject(GetOrdProp(LComp, GetPropInfo))); + + LF := TfrmSASLListEditor.Create(nil); + try + if LComp is TComponent then begin + LF.SetComponentName(TComponent(LComp).Name); + end; + LF.SetList(LList); + if LF.Execute then begin + LF.GetList(LList); + end; + finally + FreeAndNil(LF); + end; +end; + +function TIdPropEdSASL.GetAttributes: TPropertyAttributes; +begin + Result := inherited GetAttributes + [paDialog]; +end; + +function TIdPropEdSASL.GetValue: string; +begin + Result := GetStrValue; +end; + +procedure TIdPropEdSASL.SetValue(const Value: string); +begin + inherited SetValue(Value); +end; + +end. diff --git a/indy/Protocols/IdDsnSASLListEditorForm.pas b/indy/Protocols/IdDsnSASLListEditorForm.pas new file mode 100644 index 0000000..5161ff6 --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditorForm.pas @@ -0,0 +1,86 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.7 9/8/2004 10:10:40 PM JPMugaas + Now should work properly in DotNET versions of Delphi. + + Rev 1.6 9/5/2004 3:16:58 PM JPMugaas + Should work in D9 DotNET. + + Rev 1.5 2/26/2004 8:53:22 AM JPMugaas + Hack to restore the property editor for SASL mechanisms. + + Rev 1.4 1/25/2004 3:11:10 PM JPMugaas + SASL Interface reworked to make it easier for developers to use. + SSL and SASL reenabled components. + + Rev 1.3 10/19/2003 6:05:38 PM DSiders + Added localization comments. + + Rev 1.2 10/12/2003 1:49:30 PM BGooijen + Changed comment of last checkin + + Rev 1.1 10/12/2003 1:43:30 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/14/2002 02:19:14 PM JPMugaas + + 2002-08 Johannes Berg + Form for the SASL List Editor. It doesn't use a DFM/XFM to be + more portable between Delphi/Kylix versions, and to make less + trouble maintaining it. +} + +unit IdDsnSASLListEditorForm; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF WIDGET_WINFORMS} + Classes, + IdDsnSASLListEditorFormNET; + {$R 'IdDsnSASLListEditorFormNET.TfrmSASLListEditor.resources' 'IdDsnSASLListEditorFormNET.resx'} + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + IdDsnSASLListEditorFormVCL; + {$ENDIF} + +type + {$IFDEF WIDGET_WINFORMS} + //we make a create here because I'm not sure how the Visual Designer for WinForms + //we behave in a package. I know it can act weird if something is renamed + TfrmSASLListEditor = class(IdDsnSASLListEditorFormNET.TfrmSASLListEditor) + public + constructor Create(AOwner : TComponent); + end; + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + TfrmSASLListEditor = class(TfrmSASLListEditorVCL); + {$ENDIF} + +implementation + +{$IFDEF WIDGET_WINFORMS} +constructor TfrmSASLListEditor.Create(AOwner : TComponent); +begin + inherited Create; +end; +{$ENDIF} +end. diff --git a/indy/Protocols/IdDsnSASLListEditorFormNET.TfrmSASLListEditor.resources b/indy/Protocols/IdDsnSASLListEditorFormNET.TfrmSASLListEditor.resources new file mode 100644 index 0000000..678dc8b Binary files /dev/null and b/indy/Protocols/IdDsnSASLListEditorFormNET.TfrmSASLListEditor.resources differ diff --git a/indy/Protocols/IdDsnSASLListEditorFormNET.pas b/indy/Protocols/IdDsnSASLListEditorFormNET.pas new file mode 100644 index 0000000..0d32871 --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditorFormNET.pas @@ -0,0 +1,412 @@ +unit IdDsnSASLListEditorFormNET; + +interface + +uses + Classes, + System.Drawing, System.Collections, System.ComponentModel, + System.Windows.Forms, System.Data, IdSASLCollection; + +type + TfrmSASLListEditor = class(System.Windows.Forms.Form) + {$REGION 'Designer Managed Code'} + strict private + /// + /// Required designer variable. + /// + Components: System.ComponentModel.Container; + btnOk: System.Windows.Forms.Button; + btnCancel: System.Windows.Forms.Button; + lblAvailable: System.Windows.Forms.Label; + lblAssigned: System.Windows.Forms.Label; + lbAvailable: System.Windows.Forms.ListBox; + lbAssigned: System.Windows.Forms.ListBox; + btnRemove: System.Windows.Forms.Button; + btnAdd: System.Windows.Forms.Button; + btnUp: System.Windows.Forms.Button; + btnDown: System.Windows.Forms.Button; + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + procedure InitializeComponent; + procedure lbAvailable_SelectedIndexChanged(sender: System.Object; e: System.EventArgs); + procedure btnAdd_Click(sender: System.Object; e: System.EventArgs); + procedure btnRemove_Click(sender: System.Object; e: System.EventArgs); + procedure btnUp_Click(sender: System.Object; e: System.EventArgs); + procedure btnDown_Click(sender: System.Object; e: System.EventArgs); + {$ENDREGION} + strict protected + /// + /// Clean up any resources being used. + /// + procedure Dispose(Disposing: Boolean); override; + private + { Private Declarations } + FSASLList: TIdSASLEntries; + FAvailObjs : TList; + procedure LoadBitmaps; + procedure UpdateList; + procedure UpdateGUI; + public + constructor Create; + procedure SetList(const CopyFrom: TIdSASLEntries); + procedure GetList(const CopyTo: TIdSASLEntries); + procedure SetComponentName(const Name: string); + function Execute : Boolean; + end; + + [assembly: RuntimeRequiredAttribute(TypeOf(TfrmSASLListEditor))] + +implementation + +uses + System.Reflection, System.Resources, + IdDsnCoreResourceStrings, + IdGlobal, + IdResourceStrings, + IdSASL, + SysUtils; + +{$R IdSASLListEditorForm.resources} +const + ResourceBaseName = 'IdSASLListEditorForm'; + +{$AUTOBOX ON} + +{$REGION 'Windows Form Designer generated code'} +/// +/// Required method for Designer support -- do not modify +/// the contents of this method with the code editor. +/// +procedure TfrmSASLListEditor.InitializeComponent; +begin + Self.btnOk := System.Windows.Forms.Button.Create; + Self.btnCancel := System.Windows.Forms.Button.Create; + Self.lblAvailable := System.Windows.Forms.Label.Create; + Self.lblAssigned := System.Windows.Forms.Label.Create; + Self.lbAvailable := System.Windows.Forms.ListBox.Create; + Self.lbAssigned := System.Windows.Forms.ListBox.Create; + Self.btnAdd := System.Windows.Forms.Button.Create; + Self.btnRemove := System.Windows.Forms.Button.Create; + Self.btnUp := System.Windows.Forms.Button.Create; + Self.btnDown := System.Windows.Forms.Button.Create; + Self.SuspendLayout; + // + // btnOk + // + Self.btnOk.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnOk.DialogResult := System.Windows.Forms.DialogResult.OK; + Self.btnOk.Location := System.Drawing.Point.Create(294, 323); + Self.btnOk.Name := 'btnOk'; + Self.btnOk.TabIndex := 0; + // + // btnCancel + // + Self.btnCancel.DialogResult := System.Windows.Forms.DialogResult.Cancel; + Self.btnCancel.Location := System.Drawing.Point.Create(374, 323); + Self.btnCancel.Name := 'btnCancel'; + Self.btnCancel.TabIndex := 1; + // + // lblAvailable + // + Self.lblAvailable.AutoSize := True; + Self.lblAvailable.Location := System.Drawing.Point.Create(8, 8); + Self.lblAvailable.Name := 'lblAvailable'; + Self.lblAvailable.Size := System.Drawing.Size.Create(38, 16); + Self.lblAvailable.TabIndex := 2; + Self.lblAvailable.Text := 'Label1'; + // + // lblAssigned + // + Self.lblAssigned.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Right))); + Self.lblAssigned.Location := System.Drawing.Point.Create(248, 8); + Self.lblAssigned.Name := 'lblAssigned'; + Self.lblAssigned.Size := System.Drawing.Size.Create(168, 16); + Self.lblAssigned.TabIndex := 3; + Self.lblAssigned.Text := 'Label2'; + // + // lbAvailable + // + Self.lbAvailable.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Bottom) or System.Windows.Forms.AnchorStyles.Left))); + Self.lbAvailable.Location := System.Drawing.Point.Create(8, 24); + Self.lbAvailable.Name := 'lbAvailable'; + Self.lbAvailable.Size := System.Drawing.Size.Create(169, 277); + Self.lbAvailable.TabIndex := 4; + Include(Self.lbAvailable.SelectedIndexChanged, Self.lbAvailable_SelectedIndexChanged); + // + // lbAssigned + // + Self.lbAssigned.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Bottom) or System.Windows.Forms.AnchorStyles.Right))); + Self.lbAssigned.Location := System.Drawing.Point.Create(248, 24); + Self.lbAssigned.Name := 'lbAssigned'; + Self.lbAssigned.Size := System.Drawing.Size.Create(169, 277); + Self.lbAssigned.TabIndex := 5; + Include(Self.lbAssigned.SelectedIndexChanged, Self.lbAvailable_SelectedIndexChanged); + // + // btnAdd + // + Self.btnAdd.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.btnAdd.Location := System.Drawing.Point.Create(184, 88); + Self.btnAdd.Name := 'btnAdd'; + Self.btnAdd.Size := System.Drawing.Size.Create(57, 23); + Self.btnAdd.TabIndex := 6; + Include(Self.btnAdd.Click, Self.btnAdd_Click); + // + // btnRemove + // + Self.btnRemove.Anchor := (System.Windows.Forms.AnchorStyles(((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Left) or System.Windows.Forms.AnchorStyles.Right))); + Self.btnRemove.Location := System.Drawing.Point.Create(184, 128); + Self.btnRemove.Name := 'btnRemove'; + Self.btnRemove.Size := System.Drawing.Size.Create(57, 23); + Self.btnRemove.TabIndex := 7; + Include(Self.btnRemove.Click, Self.btnRemove_Click); + // + // btnUp + // + Self.btnUp.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnUp.Location := System.Drawing.Point.Create(424, 88); + Self.btnUp.Name := 'btnUp'; + Self.btnUp.Size := System.Drawing.Size.Create(23, 23); + Self.btnUp.TabIndex := 8; + Include(Self.btnUp.Click, Self.btnUp_Click); + // + // btnDown + // + Self.btnDown.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Top + or System.Windows.Forms.AnchorStyles.Right))); + Self.btnDown.Location := System.Drawing.Point.Create(424, 128); + Self.btnDown.Name := 'btnDown'; + Self.btnDown.Size := System.Drawing.Size.Create(23, 23); + Self.btnDown.TabIndex := 9; + Include(Self.btnDown.Click, Self.btnDown_Click); + // + // TfrmSASLListEditor + // + Self.AcceptButton := Self.btnOk; + Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); + Self.CancelButton := Self.btnCancel; + Self.ClientSize := System.Drawing.Size.Create(454, 354); + Self.Controls.Add(Self.btnDown); + Self.Controls.Add(Self.btnUp); + Self.Controls.Add(Self.btnRemove); + Self.Controls.Add(Self.btnAdd); + Self.Controls.Add(Self.lbAssigned); + Self.Controls.Add(Self.lbAvailable); + Self.Controls.Add(Self.lblAssigned); + Self.Controls.Add(Self.lblAvailable); + Self.Controls.Add(Self.btnCancel); + Self.Controls.Add(Self.btnOk); + Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; + Self.MaximizeBox := False; + Self.MaximumSize := System.Drawing.Size.Create(460, 386); + Self.MinimizeBox := False; + Self.MinimumSize := System.Drawing.Size.Create(460, 386); + Self.Name := 'TfrmSASLListEditor'; + Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + Self.Text := 'WinForm'; + Self.ResumeLayout(False); +end; +{$ENDREGION} + +procedure TfrmSASLListEditor.Dispose(Disposing: Boolean); +begin + if Disposing then + begin + if Components <> nil then begin + Components.Dispose(); + end; + FreeAndNil(FSASLList); + FreeAndNil(FAvailObjs); + end; + inherited Dispose(Disposing); +end; + +constructor TfrmSASLListEditor.Create; +begin + inherited Create; + // + // Required for Windows Form Designer support + // + InitializeComponent; + // + // TODO: Add any constructor code after InitializeComponent call + // + //captions + Text := RSADlgSLCaption; + lblAvailable.Text := RSADlgSLAvailable; + lblAssigned.Text := RSADlgSLAssigned; + FSASLList := TIdSASLEntries.Create(Self); + FAvailObjs := TList.Create; + LoadBitmaps; + btnCancel.Text := RSCancel; + btnOk.Text := RSOk; +end; + +function TfrmSASLListEditor.Execute: Boolean; +begin + Result := Self.ShowDialog = System.Windows.Forms.DialogResult.OK; +end; + +procedure TfrmSASLListEditor.btnDown_Click(sender: System.Object; e: System.EventArgs); +var + sel: integer; +begin + sel := lbAssigned.SelectedIndex; + if (sel >= 0) and (sel < lbAssigned.Items.Count-1) then begin + FSASLList.Items[sel].Index := sel+1; + Updatelist; + lbAssigned.SelectedIndex := sel+1; + end; +end; + +procedure TfrmSASLListEditor.btnUp_Click(sender: System.Object; e: System.EventArgs); +var + sel : Integer; +begin + sel := lbAssigned.SelectedIndex; + // >0 is intentional, can't move the top element up!! + if sel > 0 then begin + FSASLList.Items[Sel].Index := sel-1; + UpdateList; + lbAssigned.SelectedIndex := sel-1; + end; +end; + +procedure TfrmSASLListEditor.btnRemove_Click(sender: System.Object; e: System.EventArgs); +var + sel : Integer; +begin + sel := lbAssigned.SelectedIndex; + if sel >= 0 then + begin + FSASLList.Delete(sel); + end; + UpdateList; +end; + +procedure TfrmSASLListEditor.btnAdd_Click(sender: System.Object; e: System.EventArgs); +var + sel: integer; + LCI : TIdSASLListEntry; +begin + sel := lbAvailable.SelectedIndex ; + if sel >= 0 then begin + LCI := FSASLList.Add; + LCI.SASL := TIdSASL(FAvailObjs[sel]); + // SASLList.Add(TIdSASL(lbAvailable.Items.Objects[sel])); + UpdateList; + end; +end; + +procedure TfrmSASLListEditor.lbAvailable_SelectedIndexChanged(sender: System.Object; + e: System.EventArgs); +begin + UpdateGUI; +end; + +procedure TfrmSASLListEditor.SetComponentName(const Name: string); +begin + Text := IndyFormat(RSADlgSLCaption, [Name]); +end; + +procedure TfrmSASLListEditor.GetList(const CopyTo: TIdSASLEntries); +begin + CopyTo.Assign(FSASLList); +end; + +procedure TfrmSASLListEditor.SetList(const CopyFrom: TIdSASLEntries); +var + i, idx: integer; +begin + FSASLList.Assign(CopyFrom); + for i := 0 to CopyFrom.Count-1 do begin + if Assigned(CopyFrom[i].SASL) then + begin + idx := lbAvailable.Items.IndexOf(CopyFrom[i].SASL.Name); + if idx >= 0 then begin + lbAvailable.Items.Remove(idx); + end; + end; + // SASLList.Add(CopyFrom[i]); + end; +// FListOwner := CopyFrom.Owner; + UpdateList; +end; + +procedure TfrmSASLListEditor.LoadBitmaps; +var + LR: System.Resources.ResourceManager; + LB : Bitmap; +begin + LR := System.Resources.ResourceManager.Create(ResourceBaseName, System.Reflection.Assembly.GetExecutingAssembly); + try + LB := Bitmap(LR.GetObject( 'ARROWLEFT.bmp')); + LB.MakeTransparent; + Self.btnRemove.Image := LB; + LB := Bitmap(LR.GetObject( 'ARROWRIGHT.bmp')); + LB.MakeTransparent; + Self.btnAdd.Image := LB; + LB := Bitmap(LR.GetObject( 'ARROWUP.bmp')); + LB.MakeTransparent; + Self.btnUp.Image := LB; + LB := Bitmap(LR.GetObject( 'ARROWDOWN.bmp')); + LB.MakeTransparent; + Self.btnDown.Image := LB; + finally + FreeAndNil(LR); + end; +end; + +procedure TfrmSASLListEditor.UpdateList; +var + i: integer; + l : TList; +begin + lbAssigned.Items.Clear; + FAvailObjs.Clear; + for i := 0 to FSASLList.Count-1 do begin + if Assigned(FSASLList[i].SASL) then begin + lbAssigned.Items.Add(FSASLList[i].SASL.Name + ': ' + FSASLList[i].SASL.ServiceName); + end; + end; + lbAvailable.Items.Clear; + l := GlobalSASLList.LockList; + try + for i := 0 to l.Count-1 do begin + if FSASLList.IndexOfComp(TIdSASL(l[i])) < 0 then begin + if Assigned(l[i]) then + begin + FAvailObjs.Add(l[i]); + lbAvailable.Items.Add(TIdSASL(l[i]).Name + ': ' + TIdSASL(l[i]).ServiceName); + end; + end; + end; + finally + GlobalSASLList.UnlockList; + end; + UpdateGUI; +end; + +procedure TfrmSASLListEditor.UpdateGUI; +//This is necessary because unlike VCL, WinForms does not +//have a native ActionList. +begin + btnAdd.Enabled := (lbAvailable.Items.Count <> 0) and + (lbAvailable.SelectedIndex <> -1); + btnRemove.Enabled := (lbAssigned.Items.Count <> 0) and + (lbAssigned.SelectedIndex <> -1); + btnUp.Enabled := (lbAssigned.Items.Count > 1) and + (lbAssigned.SelectedIndex > 0); // -1 not selected and 0 = top + btnDown.Enabled := (lbAssigned.Items.Count > 1) and + (lbAssigned.SelectedIndex <> -1) and (lbAssigned.SelectedIndex < (lbAssigned.Items.Count - 1)); +end; + +end. diff --git a/indy/Protocols/IdDsnSASLListEditorFormNET.resx b/indy/Protocols/IdDsnSASLListEditorFormNET.resx new file mode 100644 index 0000000..5bea0a9 --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditorFormNET.resx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + diff --git a/indy/Protocols/IdDsnSASLListEditorFormVCL.lrs b/indy/Protocols/IdDsnSASLListEditorFormVCL.lrs new file mode 100644 index 0000000..f0c9203 --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditorFormVCL.lrs @@ -0,0 +1,136 @@ +LazarusResources.Add('ARROWDOWN','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030000000303030303030' + +'3",'#13#10'"03030303030300090003030303030303",'#13#10'"03030303030300090003' + +'030303030303",'#13#10'"03030303030300090003030303030303",'#13#10'"030303030' + +'30300090003030303030303",'#13#10'"03030303030300090003030303030303",'#13#10 + +'"03030300000000090000000003030303",'#13#10'"0303030009090909090909000303030' + +'3",'#13#10'"03030303000909090909000303030303",'#13#10'"03030303000909090909' + +'000303030303",'#13#10'"03030303030009090900030303030303",'#13#10'"030303030' + +'30009090900030303030303",'#13#10'"03030303030300090003030303030303",'#13#10 + +'"03030303030300090003030303030303",'#13#10'"0303030303030300030303030303030' + +'3",'#13#10'"03030303030303000303030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('ARROWLEFT','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303",'#13#10'"03030303030303030303' + +'030303030303",'#13#10'"03030303030303030303030303030303",'#13#10'"030303030' + +'30303030000030303030303",'#13#10'"03030303030300000900030303030303",'#13#10 + +'"03030303000009090900030303030303",'#13#10'"0303000009090909090000000000000' + +'0",'#13#10'"00000909090909090909090909090900",'#13#10'"03030000090909090900' + +'000000000000",'#13#10'"03030303000009090900030303030303",'#13#10'"030303030' + +'30300000900030303030303",'#13#10'"03030303030303030000030303030303",'#13#10 + +'"03030303030303030303030303030303",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('ARROWRIGHT','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303",'#13#10'"03030303030303030303' + +'030303030303",'#13#10'"03030303030300000303030303030303",'#13#10'"030303030' + +'30300090000030303030303",'#13#10'"03030303030300090909000003030303",'#13#10 + +'"00000000000000090909090900000303",'#13#10'"0009090909090909090909090909000' + +'0",'#13#10'"00000000000000090909090900000303",'#13#10'"03030303030300090909' + +'000003030303",'#13#10'"03030303030300090000030303030303",'#13#10'"030303030' + +'30300000303030303030303",'#13#10'"03030303030303030303030303030303",'#13#10 + +'"03030303030303030303030303030303",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('ARROWUP','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030300030303030303030' + +'3",'#13#10'"03030303030303000303030303030303",'#13#10'"03030303030300090003' + +'030303030303",'#13#10'"03030303030300090003030303030303",'#13#10'"030303030' + +'30009090900030303030303",'#13#10'"03030303030009090900030303030303",'#13#10 + +'"03030303000909090909000303030303",'#13#10'"0303030300090909090900030303030' + +'3",'#13#10'"03030300090909090909090003030303",'#13#10'"03030300000000090000' + +'000003030303",'#13#10'"03030303030300090003030303030303",'#13#10'"030303030' + +'30300090003030303030303",'#13#10'"03030303030300090003030303030303",'#13#10 + +'"03030303030300090003030303030303",'#13#10'"0303030303030009000303030303030' + +'3",'#13#10'"03030303030300000003030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('DIS_ARROWDOWN','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030707070303030303030' + +'3",'#13#10'"03030303030307150715030303030303",'#13#10'"03030303030307150715' + +'030303030303",'#13#10'"03030303030307150715030303030303",'#13#10'"030303030' + +'30307150715030303030303",'#13#10'"03030303030307150715030303030303",'#13#10 + +'"03030307070707150707070703030303",'#13#10'"0303030715151515031515071503030' + +'3",'#13#10'"03030303070303030303070315030303",'#13#10'"03030303071503030303' + +'071503030303",'#13#10'"03030303030703030307031503030303",'#13#10'"030303030' + +'30715030307150303030303",'#13#10'"03030303030307030703150303030303",'#13#10 + +'"03030303030307150715030303030303",'#13#10'"0303030303030307031503030303030' + +'3",'#13#10'"03030303030303071503030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('DIS_ARROWLEFT','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303",'#13#10'"03030303030303030303' + +'030303030303",'#13#10'"03030303030303030303030303030303",'#13#10'"030303030' + +'30303030707030303030303",'#13#10'"03030303030307070307150303030303",'#13#10 + +'"03030303070703151507150303030303",'#13#10'"0303070703151503030707070707070' + +'7",'#13#10'"07070315150303030303151515151507",'#13#10'"03150707030303030307' + +'070707070707",'#13#10'"03030315070703030307151515151515",'#13#10'"030303030' + +'31507070307150303030303",'#13#10'"03030303030303150707150303030303",'#13#10 + +'"03030303030303030315150303030303",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('DIS_ARROWRIGHT','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303",'#13#10'"03030303030303030303' + +'030303030303",'#13#10'"03030303030307070303030303030303",'#13#10'"030303030' + +'30307150707030303030303",'#13#10'"03030303030307150315070703030303",'#13#10 + +'"07070707070707150303031507070303",'#13#10'"0715151515151515030303030315070' + +'7",'#13#10'"07070707070707030303030307070315",'#13#10'"03151515151507150303' + +'070703151503",'#13#10'"03030303030307150707031515030303",'#13#10'"030303030' + +'30307070315150303030303",'#13#10'"03030303030303151503030303030303",'#13#10 + +'"03030303030303030303030303030303",'#13#10'"0303030303030303030303030303030' + +'3",'#13#10'"03030303030303030303030303030303"'#13#10'};'#13#10 +]); +LazarusResources.Add('DIS_ARROWUP','XPM',[ + '/* XPM */'#13#10'static char *Pixmap[] = {'#13#10'"16 16 16 2",'#13#10'"00 c' + +' black",'#13#10'"01 c #800000",'#13#10'"02 c #008000",'#13#10'"03 c none",' + +#13#10'"04 c #000080",'#13#10'"05 c #800080",'#13#10'"06 c #008080",'#13#10 + +'"07 c #808080",'#13#10'"08 c #C0C0C0",'#13#10'"09 c red",'#13#10'"10 c gree' + +'n",'#13#10'"11 c yellow",'#13#10'"12 c blue",'#13#10'"13 c magenta",'#13#10 + +'"14 c cyan",'#13#10'"15 c Gray100",'#13#10'"0303030303030307030303030303030' + +'3",'#13#10'"03030303030303071503030303030303",'#13#10'"03030303030307030703' + +'030303030303",'#13#10'"03030303030307150715030303030303",'#13#10'"030303030' + +'30703150307030303030303",'#13#10'"03030303030715030307150303030303",'#13#10 + +'"03030303070315030303070303030303",'#13#10'"0303030307150303030307150303030' + +'3",'#13#10'"03030307031503030303030703030303",'#13#10'"03030307070707030707' + +'070715030303",'#13#10'"03030303151507150715151515030303",'#13#10'"030303030' + +'30307150715030303030303",'#13#10'"03030303030307150715030303030303",'#13#10 + +'"03030303030307150715030303030303",'#13#10'"0303030303030715071503030303030' + +'3",'#13#10'"03030303030307070715030303030303"'#13#10'};'#13#10 +]); diff --git a/indy/Protocols/IdDsnSASLListEditorFormVCL.pas b/indy/Protocols/IdDsnSASLListEditorFormVCL.pas new file mode 100644 index 0000000..7240c42 --- /dev/null +++ b/indy/Protocols/IdDsnSASLListEditorFormVCL.pas @@ -0,0 +1,553 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 9/8/2004 10:10:40 PM JPMugaas + Now should work properly in DotNET versions of Delphi. + + Rev 1.6 9/5/2004 3:16:58 PM JPMugaas + Should work in D9 DotNET. + + Rev 1.5 2/26/2004 8:53:22 AM JPMugaas + Hack to restore the property editor for SASL mechanisms. + + Rev 1.4 1/25/2004 3:11:10 PM JPMugaas + SASL Interface reworked to make it easier for developers to use. + SSL and SASL reenabled components. + + Rev 1.3 10/19/2003 6:05:38 PM DSiders + Added localization comments. + + Rev 1.2 10/12/2003 1:49:30 PM BGooijen + Changed comment of last checkin + + Rev 1.1 10/12/2003 1:43:30 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/14/2002 02:19:14 PM JPMugaas + + 2002-08 Johannes Berg + Form for the SASL List Editor. It doesn't use a DFM/XFM to be + more portable between Delphi/Kylix versions, and to make less + trouble maintaining it. +} + +unit IdDsnSASLListEditorFormVCL; + +interface + +{$I IdCompilerDefines.inc} + +uses + {$IFDEF WIDGET_KYLIX} + QControls, QForms, QStdCtrls, QButtons, QExtCtrls, QActnList, QGraphics, + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE} + Controls, Forms, StdCtrls, Buttons, ExtCtrls, ActnList, Graphics, + {$ENDIF} + Classes, IdSASLCollection; + +type + TfrmSASLListEditorVCL = class(TForm) + protected + lbAvailable: TListBox; + lbAssigned: TListBox; + sbAdd: TSpeedButton; + sbRemove: TSpeedButton; + {$IFDEF USE_TBitBtn} + BtnCancel: TBitBtn; + BtnOk: TBitBtn; + {$ELSE} + BtnCancel: TButton; + BtnOk: TButton; + {$ENDIF} + Label1: TLabel; + Label2: TLabel; + sbUp: TSpeedButton; + sbDown: TSpeedButton; + SASLList: TIdSASLEntries; + FListOwner: TComponent; + actEditor: TActionList; + actAdd : TAction; + actRemove : TAction; + actMoveUp : TAction; + actMoveDown : TAction; + procedure actAddUpdate(Sender: TObject); + procedure actAddExecute(Sender: TObject); + procedure actRemoveUpdate(Sender:TObject); + procedure actRemoveExecute(Sender:TObject); + procedure actMoveUpUpdate(Sender:TObject); + procedure actMoveUpExecute(Sender:TObject); + procedure actMoveDownExecute(Sender:TObject); + procedure actMoveDownUpdate(Sender:TObject); + procedure FormCreate; + procedure UpdateList; + public + { Public declarations } + constructor Create(AOwner: TComponent); override; + function Execute : Boolean; + procedure SetList(const CopyFrom: TIdSASLEntries); + procedure GetList(const CopyTo: TIdSASLEntries); + procedure SetComponentName(const Name: string); + end; + +implementation + +uses + {$IFDEF WIDGET_LCL} + LResources, + {$ENDIF} + IdDsnCoreResourceStrings, + IdGlobal, IdResourceStrings, IdSASL, + SysUtils; + +{ TfrmSASLListEditorVCL } + +{$IFNDEF WIDGET_LCL} + {$IFDEF WINDOWS} + {$R IdSASLListEditorForm.RES} + {$ENDIF} + {$IFDEF KYLIX} + {$R IdSASLListEditorForm.RES} + {$ENDIF} +{$ENDIF} + +constructor TfrmSASLListEditorVCL.Create(AOwner: TComponent); +begin + inherited CreateNew(AOwner,0); + FormCreate; + UpdateList; +end; + +procedure TfrmSASLListEditorVCL.GetList(const CopyTo: TIdSASLEntries); +begin + CopyTo.Assign(SASLList); +end; + +procedure TfrmSASLListEditorVCL.SetList(const CopyFrom: TIdSASLEntries); +var + i, idx: integer; +begin + SASLList.Assign(CopyFrom); + for i := 0 to CopyFrom.Count-1 do begin + if Assigned(CopyFrom[i].SASL) then + begin + idx := lbAvailable.Items.IndexOf(CopyFrom[i].SASL.Name); + if idx >= 0 then begin + lbAvailable.Items.Delete(idx); + end; + end; + end; + UpdateList; +end; + +procedure TfrmSASLListEditorVCL.UpdateList; +var + i: integer; + l : TList; +begin + lbAssigned.Clear; + for i := 0 to SASLList.Count -1 do begin + if Assigned(SASLList[i].SASL) then + begin + lbAssigned.Items.AddObject(SASLList[i].SASL.Name + ': ' + String(SASLList[i].SASL.ServiceName), SASLList[i]); + end; + end; + lbAvailable.Clear; + l := GlobalSASLList.LockList; + try + for i := 0 to l.Count-1 do begin + if SASLList.IndexOfComp(TIdSASL(l[i])) < 0 then begin + if Assigned(l[i]) then + begin + lbAvailable.Items.AddObject(TIdSASL(l[i]).Name + ': ' + String(TIdSASL(l[i]).ServiceName), TIdSASL(l[i])); + end; + end; + end; + finally + GlobalSASLList.UnlockList; + end; +end; + +procedure TfrmSASLListEditorVCL.SetComponentName(const Name: string); +begin + Caption := IndyFormat(Caption, [Name]); +end; + +procedure TfrmSASLListEditorVCL.FormCreate; +begin + SASLList := TIdSASLEntries.Create(Self); + + Left := 292; + Top := 239; + + {$IFDEF WIDGET_KYLIX} + BorderStyle := fbsDialog; + {$ENDIF} + {$IFDEF WIDGET_VCL_LIKE} + BorderStyle := bsDialog; + {$ENDIF} + + Caption := RSADlgSLCaption; + + {$IFDEF USE_TBitBtn} + ClientHeight := 349; + {$ELSE} + ClientHeight := 344; + {$ENDIF} + ClientWidth := 452; + + Position := poScreenCenter; + //workaround for problem - form position does not work like in VCL + Left := (Screen.Width - Width) div 2; + Top := (Screen.Height - Height) div 2; + + {we do the actions here so that the rest of the components can bind to them} + actEditor := TActionList.Create(Self); + + actAdd := TAction.Create(Self); + actAdd.ActionList := actEditor; + actAdd.Hint := RSADlgSLAdd; + actAdd.OnExecute := actAddExecute; + actAdd.OnUpdate := actAddUpdate; + + actRemove := TAction.Create(Self); + actRemove.ActionList := actEditor; + actRemove.Hint := RSADlgSLRemove; + actRemove.OnExecute := actRemoveExecute; + actRemove.OnUpdate := actRemoveUpdate; + + actMoveUp := TAction.Create(Self); + actMoveUp.ActionList := actEditor; + actMoveUp.Hint := RSADlgSLMoveUp; + actMoveUp.OnExecute := actMoveUpExecute; + actMoveUp.OnUpdate := actMoveUpUpdate; + + actMoveDown := TAction.Create(Self); + actMoveDown.ActionList := actEditor; + actMoveDown.Hint := RSADlgSLMoveDown; + actMoveDown.OnExecute := actMoveDownExecute; + actMoveDown.OnUpdate := actMoveDownUpdate; + + sbAdd := TSpeedButton.Create(Self); + sbAdd.Name := 'sbAdd'; {do not localize} + sbAdd.Parent := Self; + sbAdd.Action := actAdd; + sbAdd.Left := 184; + sbAdd.Top := 88; + sbAdd.Width := 57; + sbAdd.Height := 25; + sbAdd.ShowHint := True; + {$IFDEF WIDGET_LCL} + sbAdd.Glyph.LoadFromLazarusResource('DIS_ARROWRIGHT'); {do not localize} + {$ELSE} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + sbAdd.Glyph.LoadFromResourceName(HInstance, 'ARROWRIGHT'); {do not localize} + sbAdd.NumGlyphs := 2; + {$ENDIF} + {$ENDIF} + + sbRemove := TSpeedButton.Create(Self); + sbRemove.Name := 'sbRemove'; {do not localize} + sbRemove.Parent := Self; + sbRemove.Action := actRemove; + sbRemove.Left := 184; + sbRemove.Top := 128; + sbRemove.Width := 57; + sbRemove.Height := 25; + sbRemove.ShowHint := True; + {$IFDEF WIDGET_LCL} + sbRemove.Glyph.LoadFromLazarusResource('DIS_ARROWLEFT'); {do not localize} + {$ELSE} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + sbRemove.Glyph.LoadFromResourceName(HInstance, 'ARROWLEFT'); {do not localize} + sbRemove.NumGlyphs := 2; + {$ENDIF} + {$ENDIF} + + Label1 := TLabel.Create(Self); + Label1.Name := 'Label1'; {do not localize} + Label1.Parent := Self; + Label1.Left := 8; + Label1.Top := 8; + Label1.Width := 42; + Label1.Height := 13; + Label1.Caption := RSADlgSLAvailable; + + Label2 := TLabel.Create(Self); + Label2.Name := 'Label2'; {do not localize} + Label2.Parent := Self; + Label2.Left := 248; + Label2.Top := 8; + Label2.Width := 136; + Label2.Height := 13; + Label2.Caption := RSADlgSLAssigned; + + sbUp := TSpeedButton.Create(Self); + sbUp.Name := 'sbUp'; {do not localize} + sbUp.Parent := Self; + sbUp.Action := actMoveUp; + sbUp.Left := 424; + sbUp.Top := 88; + sbUp.Width := 23; + sbUp.Height := 22; + sbUp.ShowHint := True; + {$IFDEF WIDGET_LCL} + sbUp.Glyph.LoadFromLazarusResource('DIS_ARROWUP'); {do not localize} + {$ELSE} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + sbUp.Glyph.LoadFromResourceName(HInstance, 'ARROWUP'); {do not localize} + sbUp.NumGlyphs := 2; + {$ENDIF} + {$ENDIF} + + sbDown := TSpeedButton.Create(Self); + sbDown.Name := 'sbDown'; {do not localize} + sbDown.Parent := Self; + sbDown.Action := actMoveDown; + sbDown.Left := 424; + sbDown.Top := 128; + sbDown.Width := 23; + sbDown.Height := 22; + sbDown.ShowHint := True; + {$IFDEF WIDGET_LCL} + sbDown.Glyph.LoadFromLazarusResource('DIS_ARROWDOWN'); {do not localize} + {$ELSE} + {$IFDEF WIDGET_VCL_LIKE_OR_KYLIX} + sbDown.Glyph.LoadFromResourceName(HInstance, 'ARROWDOWN'); {do not localize} + sbDown.NumGlyphs := 2; + {$ENDIF} + {$ENDIF} + + lbAvailable := TListBox.Create(Self); + lbAvailable.Name := 'lbAvailable'; {do not localize} + lbAvailable.Parent := Self; + lbAvailable.Left := 8; + lbAvailable.Top := 24; + lbAvailable.Width := 169; + lbAvailable.Height := 281; + lbAvailable.ItemHeight := 13; + lbAvailable.TabOrder := 0; + + lbAssigned := TListBox.Create(Self); + lbAssigned.Name := 'lbAssigned'; {do not localize} + lbAssigned.Parent := Self; + lbAssigned.Left := 248; + lbAssigned.Top := 24; + lbAssigned.Width := 169; + lbAssigned.Height := 281; + lbAssigned.ItemHeight := 13; + lbAssigned.TabOrder := 1; + + {$IFDEF USE_TBitBtn} + BtnCancel := TBitBtn.Create(Self); + {$ELSE} + BtnCancel := TButton.Create(Self); + {$ENDIF} + BtnCancel.Name := 'BtnCancel'; {do not localize} + BtnCancel.Left := 368; + BtnCancel.Top := 312; + BtnCancel.Width := 75; + {$IFDEF WIDGET_LCL} + BtnCancel.Height := 30; + BtnCancel.Kind := bkCancel; + {$ELSE} + BtnCancel.Height := 25; + BtnCancel.Cancel := True; + BtnCancel.Caption := RSCancel; + BtnCancel.ModalResult := 2; + {$ENDIF} + BtnCancel.Parent := Self; + + {$IFDEF USE_TBitBtn} + BtnOk := TBitBtn.Create(Self); + {$ELSE} + BtnOk := TButton.Create(Self); + {$ENDIF} + BtnOk.Name := 'BtnOk'; {do not localize} + BtnOk.Parent := Self; + BtnOk.Left := 287; + BtnOk.Top := 312; + BtnOk.Width := 75; + {$IFDEF WIDGET_LCL} + BtnOk.Height := 30; + BtnOk.Kind := bkOk; + {$ELSE} + BtnOk.Height := 25; + BtnOk.Caption := RSOk; + BtnOk.Default := True; + BtnOk.ModalResult := 1; + {$ENDIF} + BtnOk.TabOrder := 2; + BtnOk.TabOrder := 3; +end; + +procedure TfrmSASLListEditorVCL.actAddExecute(Sender: TObject); +var + sel: integer; +begin + sel := lbAvailable.ItemIndex; + if sel >= 0 then begin + SASLList.Add.SASL := TIdSASL(lbAvailable.Items.Objects[sel]); + UpdateList; + end; +end; + +procedure TfrmSASLListEditorVCL.actAddUpdate(Sender: TObject); +var + LEnabled : Boolean; +begin + //we do this in a round about way because we should update the glyph + //with an enabled/disabled form so a user can see what is applicable + + LEnabled := (lbAvailable.Items.Count <> 0) and + (lbAvailable.ItemIndex <> -1); + + {$IFDEF WIDGET_LCL} + if LEnabled <> actAdd.Enabled then + begin + if LEnabled then begin + sbAdd.Glyph.LoadFromLazarusResource('ARROWRIGHT'); {do not localize} + end else begin + sbAdd.Glyph.LoadFromLazarusResource('DIS_ARROWRIGHT'); {do not localize} + end; + end; + {$ENDIF} + + actAdd.Enabled := LEnabled; +end; + +procedure TfrmSASLListEditorVCL.actMoveDownExecute(Sender: TObject); +var + sel: integer; +begin + sel := lbAssigned.ItemIndex; + if (sel >= 0) and (sel < lbAssigned.Items.Count-1) then begin + SASLList.Items[sel].Index := sel+1; + Updatelist; + lbAssigned.ItemIndex := sel+1; + end; +end; + +procedure TfrmSASLListEditorVCL.actMoveDownUpdate(Sender: TObject); +var + LEnabled : Boolean; +begin + LEnabled := (lbAssigned.Items.Count > 1) and + (lbAssigned.ItemIndex <> -1) and + (lbAssigned.ItemIndex < (lbAssigned.Items.Count - 1)); + + {$IFDEF WIDGET_LCL} + if LEnabled <> actMoveDown.Enabled then + begin + if LEnabled then begin + sbDown.Glyph.LoadFromLazarusResource('ARROWDOWN'); {do not localize} + end else begin + sbDown.Glyph.LoadFromLazarusResource('DIS_ARROWDOWN'); {do not localize} + end; + end; + {$ENDIF} + + actMoveDown.Enabled := LEnabled; +end; + +procedure TfrmSASLListEditorVCL.actMoveUpExecute(Sender: TObject); +var + sel: integer; +begin + sel := lbAssigned.ItemIndex; + // >0 is intentional, can't move the top element up!! + if sel > 0 then begin + SASLList.Items[Sel].Index := sel-1; + UpdateList; + lbAssigned.ItemIndex := sel -1; + end; +end; + +procedure TfrmSASLListEditorVCL.actMoveUpUpdate(Sender: TObject); +var + LEnabled : Boolean; +begin + //we do this in a round about way because we should update the glyph + //with an enabled/disabled form so a user can see what is applicable + + LEnabled := (lbAssigned.Items.Count > 1) and + (lbAssigned.ItemIndex > 0); // -1 not selected and 0 = top + + {$IFDEF WIDGET_LCL} + if LEnabled <> actMoveUp.Enabled then + begin + if LEnabled then begin + sbUp.Glyph.LoadFromLazarusResource('ARROWUP'); {do not localize} + end else begin + sbUp.Glyph.LoadFromLazarusResource('DIS_ARROWUP'); {do not localize} + end; + end; + {$ENDIF} + + actMoveUp.Enabled := LEnabled; +end; + +procedure TfrmSASLListEditorVCL.actRemoveExecute(Sender: TObject); +var + sel: integer; +begin + sel := lbAssigned.ItemIndex; + if sel >= 0 then begin + SASLList.Delete(sel); + end; + UpdateList; +{ sel := lbAssigned.ItemIndex; + if sel >= 0 then begin + SASLList.Remove(TIdSASL(lbAssigned.Items.Objects[sel])); + UpdateList; + end; } +end; + +procedure TfrmSASLListEditorVCL.actRemoveUpdate(Sender: TObject); +var + LEnabled : Boolean; +begin + LEnabled := (lbAssigned.Items.Count <> 0) and + (lbAssigned.ItemIndex <> -1); + + {$IFDEF WIDGET_LCL} + if LEnabled <> actRemove.Enabled then + begin + if LEnabled then begin + sbRemove.Glyph.LoadFromLazarusResource('ARROWLEFT'); {do not localize} + end else begin + sbRemove.Glyph.LoadFromLazarusResource('DIS_ARROWLEFT'); {do not localize} + end; + end; + {$ENDIF} + + actRemove.Enabled := LEnabled; +end; + +function TfrmSASLListEditorVCL.Execute: Boolean; +begin + Result := ShowModal = mrOk; +end; + +{$IFDEF WIDGET_LCL} +initialization + {$I IdDsnSASLListEditorFormVCL.lrs} +{$ENDIF} + +end. + diff --git a/indy/Protocols/IdDummyUnit.pas b/indy/Protocols/IdDummyUnit.pas new file mode 100644 index 0000000..529f601 --- /dev/null +++ b/indy/Protocols/IdDummyUnit.pas @@ -0,0 +1,49 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ + + + Rev 1.14 9/18/2003 10:44:00 AM JPMugaas + Moved IdThread to Core. + + + Rev 1.0 7/24/2003 12:13:58 PM JPMugaas + Test compile template and the IdDummyUnit template for managing run-time + units we put into the design-time package and force to be statically linked + into the program. +} + +unit IdDummyUnit; +{ + + This unit is really not a part of Indy. This unit's purpose is to trick the DCC32 +compiler into generating .HPP and .OBJ files for run-time units that will not be +in the run-time package but will be on the palette. + +Contributed by John Doe + +} + +interface +{$i IdCompilerDefines.inc} +uses + IdAntiFreeze; + +implementation + +{ de-de-de-de, that's all folks. } + +end. diff --git a/indy/Protocols/IdEMailAddress.pas b/indy/Protocols/IdEMailAddress.pas new file mode 100644 index 0000000..4c1e33f --- /dev/null +++ b/indy/Protocols/IdEMailAddress.pas @@ -0,0 +1,879 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.13 10/26/2004 9:09:36 PM JPMugaas + Updated references. + + Rev 1.12 24/10/2004 21:25:18 ANeillans + Modifications to allow Username and Domain parts to be set. + + Rev 1.11 24.08.2004 17:29:30 Andreas Hausladen + Fixed GetEMailAddresses + Lots of simple but effective optimizations + + Rev 1.10 09/08/2004 08:17:08 ANeillans + Rename username property to user + + Rev 1.9 08/08/2004 20:58:02 ANeillans + Added support for Username extraction. + + Rev 1.8 23/04/2004 20:34:36 CCostelloe + Clarified a question in the code as to why a code path ended there + + Rev 1.7 3/6/2004 5:45:00 PM JPMugaas + Fixed problem obtaining the Text property for an E-Mail address with + no domain. + + Rev 1.6 2004.02.03 5:45:08 PM czhower + Name changes + + Rev 1.5 24/01/2004 19:12:10 CCostelloe + Cleaned up warnings + + Rev 1.4 10/12/2003 7:51:50 PM BGooijen + Fixed Range Check Error + + Rev 1.3 10/8/2003 9:50:24 PM GGrieve + use IdDelete + + Rev 1.2 6/10/2003 5:48:50 PM SGrobety + DotNet updates + + Rev 1.1 5/18/2003 02:30:36 PM JPMugaas + Added some backdoors for the TIdDirectSMTP processing. + + Rev 1.0 11/14/2002 02:19:44 PM JPMugaas + + 2001-Aug-30 - Jim Gunkel + Fixed bugs that would occur with group names containing spaces + (box test 19) and content being located after the email + address (box test 33) + + 2001-Jul-11 - Allen O'Neill + Added hack to not allow recipient entries being added that are blank + + 2001-Jul-11 - Allen O'Neill + Added hack to accomodate a PERIOD (#46) in an email address - + this whole area needs to be looked at. + + 2001-Feb-03 - Peter Mee + Overhauled TIdEMailAddressItem.GetText to support non-standard textual + elements. + + 2001-Jan-29 - Peter Mee + Overhauled TIdEMailAddressList.SetEMailAddresses to support comments + and escaped characters and to ignore groups. + + 2001-Jan-28 - Peter Mee + Overhauled TIdEMailAddressItem.SetText to support comments and escaped + characters. + + 2000-Jun-10 - J. Peter Mugaas + started this unit to facilitate some Indy work including the + TIdEMailAddressItem and TIdEMailAddressList classes + + The GetText and SetText were originally the ToArpa and FromArpa + functions in the TIdMessage component +} + +unit IdEMailAddress; + +{ + Developer(s): + J. Peter Mugaas + + Contributor(s): + Ciaran Costelloe + Bas Gooijen + Grahame Grieve + Stephane Grobety + Jim Gunkel + Andreas Hausladen + Peter Mee + Andy Neillans + Allen O'Neill +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException; + +type + EIdEmailParseError = class(EIdException); + + { ToDo: look into alterations required for TIdEMailAddressItem.GetText } + TIdEMailAddressItem = class(TCollectionItem) + protected + FAddress: string; + FName: string; + function GetText: string; + procedure SetText(AText: string); + function ConvertAddress: string; + function GetDomain: string; + procedure SetDomain(const ADomain: String); + function GetUsername: string; + procedure SetUsername(const AUsername: String); + public + procedure Assign(Source: TPersistent); override; + constructor Create; reintroduce; overload; + constructor Create(ACollection: TCollection); overload; override; + constructor Create(const AText: string); reintroduce; overload; + published + {This is the E-Mail address itself } + property Address: string read FAddress write FAddress; + { This is the person's name } + property Name: string read FName write FName; + { This is the combined person's name and E-Mail address } + property Text: string read GetText write SetText; + {Extracted domain for some types of E-Mail processing} + property Domain: string read GetDomain write SetDomain; + property User: string read GetUsername write SetUsername; + end; + + TIdEMailAddressList = class (TOwnedCollection) + protected + function GetItem(Index: Integer): TIdEMailAddressItem; + procedure SetItem(Index: Integer; const Value: TIdEMailAddressItem); + function GetEMailAddresses: string; + procedure SetEMailAddresses(AList: string); + public + constructor Create(AOwner: TPersistent); reintroduce; + { List of formated addresses including the names from the collection } + procedure FillTStrings(AStrings: TStrings); + function Add: TIdEMailAddressItem; reintroduce; + procedure AddItems(AList: TIdEMailAddressList); + { get all of the domains in the list so we can process individually } + procedure GetDomains(AStrings: TStrings); + { Sort by domains for making it easier to process E-Mails directly } + procedure SortByDomain; + { Gets all E-Mail addresses for a particular domain so we can + send to recipients at one domain with only one connection } + procedure AddressesByDomain(AList: TIdEMailAddressList; const ADomain: string); + property Items[Index: Integer]: TIdEMailAddressItem read GetItem write SetItem; default; + { Comma-separated list of formated addresses including the names + from the collection } + property EMailAddresses: string read GetEMailAddresses write SetEMailAddresses; + end; + +implementation + +uses + IdGlobal, + IdGlobalProtocols, + IdExceptionCore, + IdResourceStringsProtocols, SysUtils; + +const + // ATEXT without the double quote and space characters + IETF_ATEXT: string = 'abcdefghijklmnopqrstuvwxyz' + {do not localize} + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + {do not localize} + '1234567890!#$%&''*+-/=?_`{}|~'; {do not localize} + + // ATEXT without the double quote character + IETF_ATEXT_SPACE: string = 'abcdefghijklmnopqrstuvwxyz' + {do not localize} + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + {do not localize} + '1234567890!#$%&''*+-/=?_`{}|~ '; {do not localize} + + IETF_QUOTABLE: string = '\"'; {do not localize} + +{ TIdEMailAddressItem } + +constructor TIdEMailAddressItem.Create; +begin + inherited Create(nil); +end; + +constructor TIdEMailAddressItem.Create(ACollection: TCollection); +begin + inherited Create(ACollection); +end; + +constructor TIdEMailAddressItem.Create(const AText: string); +begin + inherited Create(nil); + Text := AText; +end; + +procedure TIdEMailAddressItem.Assign(Source: TPersistent); +var + LAddr : TIdEMailAddressItem; +begin + if Source is TIdEMailAddressItem then begin + LAddr := TIdEMailAddressItem(Source); + Address := LAddr.Address; + Name := LAddr.Name; + end else begin + inherited Assign(Source); + end; +end; + +function TIdEMailAddressItem.ConvertAddress: string; +var + i: Integer; + domainPart, tempAddress, localPart: string; +begin + if FAddress = '' then + begin + if FName <> '' then + begin + Result := '<>'; {Do not Localize} + end else + begin + Result := ''; {Do not Localize} + end; + Exit; + end; + + // First work backwards to the @ sign. + tempAddress := FAddress; + domainPart := ''; + for i := Length(FAddress) downto 1 do + begin + if FAddress[i] = '@' then {do not localize} + begin + domainPart := Copy(FAddress, i, MaxInt); + tempAddress := Copy(FAddress, 1, i - 1); + Break; + end; + end; + + i := FindFirstNotOf(IETF_ATEXT, tempAddress); + // hack to accomodate periods in emailaddress + if (i = 0) or CharEquals(tempAddress, i, #46) then + begin + if FName <> '' then begin + Result := '<' + tempAddress + domainPart + '>'; {do not localize} + end else begin + Result := tempAddress + domainPart; + end; + end else + begin + localPart := '"'; {do not localize} + while i > 0 do + begin + localPart := localPart + Copy(tempAddress, 1, i - 1); + if IndyPos(tempAddress[i], IETF_QUOTABLE) > 0 then + begin + localPart := localPart + '\'; {do not localize} + end; + localPart := localPart + tempAddress[i]; + IdDelete(tempAddress, 1, i); + i := FindFirstNotOf(IETF_ATEXT, tempAddress); + end; + Result := '<' + localPart + tempAddress + '"' + domainPart + '>'; {do not localize} + end; +end; + +function TIdEMailAddressItem.GetDomain: string; +var + i: Integer; +begin + Result := ''; + for i := Length(FAddress) downto 1 do + begin + if FAddress[i] = '@' then {do not localize} + begin + Result := Copy(FAddress, i + 1, MaxInt); + Break; + end; + end; +end; + +procedure TIdEMailAddressItem.SetDomain(const ADomain: String); +var + S : String; + lPos: Integer; +begin + S := FAddress; + // keep existing user info in the address... use new domain info + lPos := IndyPos('@', S); {do not localize} + if lPos > 0 then begin + IdDelete(S, lPos, Length(S)); + end; + FAddress := S + '@' + ADomain; {do not localize} +end; + +function TIdEMailAddressItem.GetUsername: string; +var + i: Integer; +begin + Result := ''; + for i := Length(FAddress) downto 1 do + begin + if FAddress[i] = '@' then {do not localize} + begin + Result := Copy(FAddress, 1, i - 1); + Break; + end; + end; +end; + +procedure TIdEMailAddressItem.SetUsername(const AUsername: String); +var + S : String; + lPos: Integer; +begin + S := FAddress; + // discard old user info... keep existing domain in the address + lPos := IndyPos('@', S); + if lPos > 0 then begin + IdDelete(S, 1, lPos); {do not localize} + end; + FAddress := AUsername + '@' + S; +end; + +function TIdEMailAddressItem.GetText: string; +var + i: Integer; + tempName, resName: string; +begin + if (FName <> '') and (not TextIsSame(FAddress, FName)) then + begin + i := FindFirstNotOf(IETF_ATEXT_SPACE, FName); + if i > 0 then + begin + // Need to quote the FName. + resName := '"' + Copy(FName, 1, i - 1); {do not localize} + if IndyPos(FName[i], IETF_QUOTABLE) > 0 then + begin + resName := resName + '\'; {do not localize} + end; + resName := resName + FName[i]; + tempName := Copy(FName, i + 1, MaxInt); + while tempName <> '' do + begin + i := FindFirstNotOf(IETF_ATEXT_SPACE, tempName); + if i = 0 then + begin + Result := resName + tempName + '" ' + ConvertAddress; {do not localize} + Exit; + end; + resName := resName + Copy(tempName, 1, i - 1); + if IndyPos(tempName[i], IETF_QUOTABLE) > 0 then + begin + resName := resName + '\'; {do not localize} + end; + resName := resName + tempName[i]; + IdDelete(tempName, 1, i); + end; + Result := resName + '" ' + ConvertAddress; {do not localize} + end else + begin + Result := FName + ' ' + ConvertAddress; {do not localize} + end; + end else + begin + Result := ConvertAddress; + end; +end; + +procedure TIdEMailAddressItem.SetText(AText: string); +var + nFirst, + nBracketCount: Integer; + bInAddress, + bAddressInLT, + bAfterAt, + bInQuote : Boolean; +begin + FAddress := ''; + FName := ''; + + AText := Trim(AText); + if AText = '' then begin + Exit; + end; + + // Find the first known character type. + if Pos('<', AText) > 0 then begin + nFirst := FindFirstOf('("< ' + TAB, AText) {Do not Localize} + end else begin + nFirst := FindFirstOf('(" @' + TAB, AText); {Do not Localize} + end; + + if nFirst <> 0 then + begin + nBracketCount := 0; + bInAddress := False; + bAddressInLT := False; + bInQuote := False; + bAfterAt := False; + repeat + case AText[nFirst] of + ' ', TAB : {do not localize} + begin + if nFirst = 1 then + begin + IdDelete(AText, 1, 1); + end else + begin + // Only valid if in a name not contained in quotes - keep the space. + if bAfterAt then begin + FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1)); + end else begin + FName := FName + Copy(AText, 1, nFirst); + end; + IdDelete(AText, 1, nFirst); + end; + end; + '(' : {do not localize} + begin + Inc(nBracketCount); + if nFirst > 1 then + begin + // There's at least one character to the name + if bInAddress then + begin + FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1)); + end + else if nBracketCount = 1 then + begin + FName := FName + Copy(AText, 1, nFirst - 1); + end; + IdDelete(AText, 1, nFirst); + end else + begin + IdDelete(AText, 1, 1); + end; + end; + ')' : {do not localize} + begin + Dec(nBracketCount); + IdDelete(AText, 1, nFirst); + end; + '"' : {do not localize} + begin + if bInQuote then + begin + if bAddressInLT then + begin + FAddress := FAddress + Trim(Copy(AText, 1, nFirst - 1)); + end else begin + FName := FName + Trim(Copy(AText, 1, nFirst - 1)); + end; + IdDelete(AText, 1, nFirst); + bInQuote := False; + end else + begin + bInQuote := True; + IdDelete(AText, 1, 1); + end; + end; + '<' : {do not localize} + begin + if nFirst > 1 then + begin + FName := FName + Copy(AText, 1, nFirst - 1); + end; + FName := TrimAllOf(' ' + TAB, Trim(FName)); {do not localize} + bAddressInLT := True; + bInAddress := True; + IdDelete(AText, 1, nFirst); + end; + '>' : {do not localize} + begin + // Only searched for if the address starts with '<' + bInAddress := False; + bAfterAt := False; + FAddress := FAddress + TrimAllOf(' ' + TAB, {do not localize} + Trim(Copy(AText, 1, nFirst - 1))); + IdDelete(AText, 1, nFirst); + end; + '@' : {do not localize} + begin + bAfterAt := True; + if bInAddress then + begin + FAddress := FAddress + Copy(AText, 1, nFirst); + IdDelete(AText, 1, nFirst); + end else + begin + if bAddressInLT then + begin + { + Strange use. For now raise an exception until a real-world + example can be found. + + Basically, it's formatted as follows: + some-text @ some-text + or: + some-text some-text @ some-text + + where some text may be blank. Note you used to arrive here + if the From header in an email included more than one address + (which was subsequently changed) because our code did not + parse the From header for multiple addresses. That may have + been the reason for this code. + } + //raise EIdEmailParseError.Create(RSEMailSymbolOutsideAddress); + FName := FName + AText; + Exit; + end; + { + at this point, we're either supporting an e-mail address on + it's own, or the old-style valid format: + + "Name" name@domain.example + } + bInAddress := True; + FAddress := FAddress + Copy(AText, 1, nFirst); + IdDelete(AText, 1, nFirst); + end; + end; + '.' : {do not localize} + begin + // Must now be a part of the domain part of the address. + if bAddressInLT then + begin + // Whitespace is possible around the parts of the domain. + FAddress := FAddress + + TrimAllOf(' ' + TAB, Trim(Copy(AText, 1, nFirst - 1))) + '.'; {do not localize} + AText := TrimLeft(Copy(AText, nFirst + 1, MaxInt)); + end else + begin + // No whitespace is allowed if no wrapping <> characters. + FAddress := FAddress + Copy(AText, 1, nFirst); + IdDelete(AText, 1, nFirst); + end; + end; + '\' : {do not localize} + begin + { + This will only be discovered in a bracketed or quoted section. + It's an escape character indicating the next character is a literal. + } + if bInQuote then + begin + // Need to retain the second character + if bInAddress then + begin + FAddress := FAddress + Copy(AText, 1, nFirst - 1); + FAddress := FAddress + AText[nFirst + 1]; + end else + begin + FName := FName + Copy(AText, 1, nFirst - 1); + FName := FName + AText[nFirst + 1]; + end; + end; + IdDelete(AText, 1, nFirst + 1); + end; + end; + { + Check for bracketted sections first: + ("<>" <> "" <"">) - all is ignored + } + if nBracketCount > 0 then + begin + { + Inside a bracket, only three characters are special. + '(' Opens a nested bracket: (One (Two (Three ))) + ')' Closes a bracket + '\' Escape character: (One \) \( \\ (Two \) )) + } + nFirst := FindFirstOf('()\', AText); {do not localize} + + // Check if in quote before address: <"My Name"@domain.example> is valid + end else if bInQuote then + begin + // Inside quotes, only the end quote and escape character are special. + + // previously FindFirst. This fixes a bug in From: like: "This is "my" name" delivered from DecodeHeader + nFirst := LastDelimiter('"\', AText); {do not localize} + + // Check if after the @ of the address: domain.example> + end else if bAfterAt then + begin + if bAddressInLT then + begin + { + If the address is enclosed, then only the '(', '.' & '>' + need be looked for, trimming all content when found: + domain . example > + } + nFirst := FindFirstOf('.>(', AText); {do not localize} + end else begin + nFirst := FindFirstOf('.( ', AText); {Do not Localize} + end; + + // Check if in address: + end else if bInAddress then + begin + nFirst := FindFirstOf('"(@>', AText); {do not localize} + + // Not in anything - check for opening character + end else + begin + // Outside brackets + nFirst := FindFirstOf('("< @' + TAB, AText); {do not localize} + end; + until nFirst = 0; + if bInAddress and (not bAddressInLT) then + begin + FAddress := FAddress + TrimAllOf(' ' + TAB, Trim(AText)); {do not localize} + end; + end else + begin + // No special characters, so assume a simple address + FAddress := AText; + end; +end; + +{ TIdEMailAddressList } + +constructor TIdEMailAddressList.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdEMailAddressItem); +end; + +function TIdEMailAddressList.Add: TIdEMailAddressItem; +begin + Result := TIdEMailAddressItem(inherited Add); +end; + +procedure TIdEMailAddressList.AddItems(AList: TIdEMailAddressList); +var + I: Integer; +begin + if AList <> nil then begin + for I := 0 to AList.Count-1 do begin + Add.Assign(AList[I]); + end; + end; +end; + +procedure TIdEMailAddressList.FillTStrings(AStrings: TStrings); +var + idx: Integer; +begin + for idx := 0 to Count - 1 do + begin + AStrings.Add(GetItem(idx).Text); + end; +end; + +function TIdEMailAddressList.GetItem(Index: Integer): TIdEMailAddressItem; +begin + Result := TIdEMailAddressItem(inherited Items[Index]); +end; + +function TIdEMailAddressList.GetEMailAddresses: string; +var + idx: Integer; +begin + Result := ''; {Do not Localize} + for idx := 0 to Count - 1 do + begin + if Result = '' then + Result := GetItem(idx).Text + else + Result := Result + ', ' + GetItem(idx).Text; {do not localize} + end; +end; + +procedure TIdEMailAddressList.SetItem(Index: Integer; + const Value: TIdEMailAddressItem); +begin + inherited SetItem(Index, Value); +end; + +procedure TIdEMailAddressList.SetEMailAddresses(AList: string); +var + EMail : TIdEMailAddressItem; + iStart: Integer; + sTemp: string; + nInBracket: Integer; + bInQuote : Boolean; +begin + Clear; + if Trim(AList) = '' then begin {Do not Localize} + Exit; + end; + + iStart := FindFirstOf(':;(", ' + TAB, AList); {do not localize} + if iStart = 0 then + begin + EMail := Add; + EMail.Text := TrimLeft(AList); + end else + begin + sTemp := ''; {do not localize} + nInBracket := 0; + bInQuote := False; + repeat + case AList[iStart] of + ' ', TAB: {do not localize} + begin + if iStart = 1 then begin + sTemp := sTemp + AList[iStart]; + IdDelete(AList, 1, 1); + end else begin + sTemp := sTemp + Copy(AList, 1, iStart); + IdDelete(AList, 1, iStart); + end; + end; + ':' : {do not localize} + begin + // The start of a group - ignore the lot. + IdDelete(AList, 1, iStart); + sTemp := ''; + end; + ';' : {do not localize} + begin + { + End of a group. If we have something (groups can be empty), + then process it. + } + sTemp := sTemp + Copy(AList, 1, iStart - 1); + if Trim(sTemp) <> '' then + begin + EMail := Add; + EMail.Text := TrimLeft(sTemp); + sTemp := ''; {do not localize} + end; + // Now simply remove the end of the group. + IdDelete(AList, 1, iStart); + end; + '(': {do not localize} + begin + Inc(nInBracket); + sTemp := sTemp + Copy(AList, 1, iStart); + IdDelete(AList, 1, iStart); + end; + ')': {do not localize} + begin + Dec(nInBracket); + sTemp := sTemp + Copy(AList, 1, iStart); + IdDelete(AList, 1, iStart); + end; + '"': {do not localize} + begin + sTemp := sTemp + Copy(AList, 1, iStart); + IdDelete(AList, 1, iStart); + bInQuote := not bInQuote; + end; + ',': {do not localize} + begin + sTemp := sTemp + Copy(AList, 1, iStart - 1); + EMail := Add; + EMail.Text := sTemp; + // added - Allen .. saves blank entries being added + sTemp := Trim(Email.Text); + if (sTemp = '') or (sTemp = '<>') then {do not localize} + begin + FreeAndNil(Email); + end; + sTemp := ''; + IdDelete(AList, 1, iStart); + end; + '\': {do not localize} + begin + // Escape character - simply copy this char and the next to the buffer. + sTemp := sTemp + Copy(AList, 1, iStart + 1); + IdDelete(AList, 1, iStart + 1); + end; + end; + + if nInBracket > 0 then begin + iStart := FindFirstOf('(\)', AList); {Do not Localize} + end else if bInQuote then begin + iStart := FindFirstOf('"\', AList); {Do not Localize} + end else begin + iStart := FindFirstOf(':;(", ' + TAB, AList); {Do not Localize} + end; + until iStart = 0; + + // Clean up the content in sTemp + if (Trim(sTemp) <> '') or (Trim(AList) <> '') then + begin + sTemp := sTemp + AList; + EMail := Add; + EMail.Text := TrimLeft(sTemp); + // added - Allen .. saves blank entries being added + sTemp := Trim(Email.Text); + if (sTemp = '') or (sTemp = '<>') then {do not localize} + begin + FreeAndNil(Email); + end; + end; + end; +end; + +procedure TIdEMailAddressList.SortByDomain; +var + i, j: Integer; + LTemp: string; +begin + for i := Count-1 downto 0 do + begin + for j := 0 to Count-2 do + begin + if IndyCompareStr(Items[J].Domain, Items[J + 1].Domain) > 0 then + begin + LTemp := Items[j].Text; + Items[j].Text := Items[j+1].Text; + Items[j+1].Text := LTemp; + end; + end; + end; +end; + +procedure TIdEMailAddressList.GetDomains(AStrings: TStrings); +var + i: Integer; + LCurDom: string; +begin + if Assigned(AStrings) then + begin + AStrings.BeginUpdate; + try + AStrings.Clear; + for i := 0 to Count-1 do + begin + LCurDom := LowerCase(Items[i].Domain); + if AStrings.IndexOf(LCurDom) = -1 then begin + AStrings.Add(LCurDom); + end; + end; + finally + AStrings.EndUpdate; + end; + end; +end; + +procedure TIdEMailAddressList.AddressesByDomain(AList: TIdEMailAddressList; + const ADomain: string); +var + i: Integer; + LEnt : TIdEMailAddressItem; +begin + AList.Clear; + for i := 0 to Count-1 do + begin + if TextIsSame(Items[i].Domain, ADomain) then + begin + LEnt := AList.Add; + LEnt.Text := Items[i].Text; + end; + end; +end; + +end. diff --git a/indy/Protocols/IdEcho.pas b/indy/Protocols/IdEcho.pas new file mode 100644 index 0000000..73687a2 --- /dev/null +++ b/indy/Protocols/IdEcho.pas @@ -0,0 +1,128 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 2004.02.03 5:45:06 PM czhower + Name changes + + Rev 1.7 1/21/2004 3:27:46 PM JPMugaas + InitComponent + + Rev 1.6 1/3/2004 12:59:52 PM JPMugaas + These should now compile with Kudzu's change in IdCoreGlobal. + + Rev 1.5 2003.11.29 10:18:52 AM czhower + Updated for core change to InputBuffer. + + Rev 1.4 3/6/2003 5:08:48 PM SGrobety + Updated the read buffer methodes to fit the new core (InputBuffer -> + InputBufferAsString + call to CheckForDataOnSource) + + Rev 1.3 2/24/2003 08:41:28 PM JPMugaas + Should compile with new code. + + Rev 1.2 12/8/2002 07:26:34 PM JPMugaas + Added published host and port properties. + + Rev 1.1 12/6/2002 05:29:32 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.0 11/14/2002 02:19:24 PM JPMugaas +} + +unit IdEcho; + +{*******************************************************} +{ } +{ Indy Echo Client TIdEcho } +{ } +{ Copyright (C) 2000 Winshoes Working Group } +{ Original author J. Peter Mugaas } +{ 2000-April-24 } +{ } +{*******************************************************} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdAssignedNumbers, + IdTCPClient; + +type + TIdEcho = class(TIdTCPClient) + protected + FEchoTime: UInt32; + procedure InitComponent; override; + public + {This sends Text to the peer and returns the reply from the peer} + function Echo(const AText: String): String; + {Time taken to send and receive data} + property EchoTime: UInt32 read FEchoTime; + published + property Port default IdPORT_ECHO; + end; + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + {$IFDEF DARWIN} + Macapi.CoreServices, + {$ENDIF} + {$ENDIF} + IdComponent, + IdTCPConnection, + IdIOHandler; + +{ TIdEcho } + +procedure TIdEcho.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_ECHO; +end; + +function TIdEcho.Echo(const AText: String): String; +var + LEncoding: IIdTextEncoding; + LBuffer: TIdBytes; + LLen: Integer; + StartTime: TIdTicks; +begin + LEncoding := IndyTextEncoding( + {$IFDEF STRING_IS_UNICODE} + encUTF16LE + {$ELSE} + encOSDefault + {$ENDIF} + ); + {Send time monitoring} + LBuffer := ToBytes(AText, LEncoding); + LLen := Length(LBuffer); + {Send time monitoring} + StartTime := Ticks64; + IOHandler.Write(LBuffer); + IOHandler.ReadBytes(LBuffer, LLen, False); + {This is just in case the TickCount rolled back to zero} + FEchoTime := GetElapsedTicks(StartTime); + Result := BytesToString(LBuffer, LEncoding); +end; + +end. diff --git a/indy/Protocols/IdEchoServer.pas b/indy/Protocols/IdEchoServer.pas new file mode 100644 index 0000000..3f7a8fe --- /dev/null +++ b/indy/Protocols/IdEchoServer.pas @@ -0,0 +1,99 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. + + + $Log$ + + + Rev 1.7 12/2/2004 4:23:52 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.6 1/21/2004 3:27:48 PM JPMugaas + InitComponent + + Rev 1.5 2003.11.29 10:18:54 AM czhower + Updated for core change to InputBuffer. + + Rev 1.4 3/6/2003 5:08:50 PM SGrobety + Updated the read buffer methodes to fit the new core (InputBuffer -> + InputBufferAsString + call to CheckForDataOnSource) + + Rev 1.3 2/24/2003 08:41:32 PM JPMugaas + Should compile with new code. + + Rev 1.2 1/17/2003 05:35:06 PM JPMugaas + Now compiles with new design. + + Rev 1.1 1-1-2003 20:13:00 BGooijen + Changed to support the new TIdContext class + + Rev 1.0 11/14/2002 02:19:30 PM JPMugaas + +2000-Apr=22 J Peter Mugaas + Ported to Indy + +1999-May-13 + Final Version + +2000-Jan-13 MTL + Moved to new Palette Scheme (Winshoes Servers) +} + +unit IdEchoServer; + +{ +Original Author: Ozz Nixon +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Type + TIdECHOServer = class ( TIdCustomTCPServer ) + protected + function DoExecute(AContext:TIdContext): boolean; override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_ECHO; + end; + +implementation + +uses + IdGlobal, IdIOHandler; + +procedure TIdECHOServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_ECHO; +end; + +function TIdECHOServer.DoExecute(AContext: TIdContext): Boolean; +var + LBuffer: TIdBytes; + LIOHandler: TIdIOHandler; +begin + Result := True; + SetLength(LBuffer, 0); + LIOHandler := AContext.Connection.IOHandler; + LIOHandler.ReadBytes(LBuffer, -1); + LIOHandler.Write(LBuffer); +end; + +end. diff --git a/indy/Protocols/IdEchoUDP.pas b/indy/Protocols/IdEchoUDP.pas new file mode 100644 index 0000000..25342d7 --- /dev/null +++ b/indy/Protocols/IdEchoUDP.pas @@ -0,0 +1,85 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 2004.02.03 5:45:06 PM czhower + Name changes + + Rev 1.2 1/21/2004 3:27:50 PM JPMugaas + InitComponent + + Rev 1.1 1/3/2004 12:59:52 PM JPMugaas + These should now compile with Kudzu's change in IdCoreGlobal. + + Rev 1.0 11/14/2002 02:19:34 PM JPMugaas +} + +unit IdEchoUDP; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdAssignedNumbers, IdUDPBase, IdUDPClient; + +type + TIdEchoUDP = class(TIdUDPClient) + protected + FEchoTime: UInt32; + procedure InitComponent; override; + public + {This sends Text to the peer and returns the reply from the peer} + Function Echo(AText: String): String; + {Time taken to send and receive data} + Property EchoTime: UInt32 read FEchoTime; + published + property Port default IdPORT_ECHO; + end; + +implementation + +{$IFDEF USE_VCL_POSIX} + {$IFDEF DARWIN} +uses + Macapi.CoreServices; + {$ENDIF} +{$ENDIF} + +{ TIdIdEchoUDP } + +procedure TIdEchoUDP.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_ECHO; +end; + +function TIdEchoUDP.Echo(AText: String): String; +var + StartTime: TIdTicks; + LEncoding: IIdTextEncoding; +begin + StartTime := Ticks64; + LEncoding := IndyTextEncoding_8Bit; + Send(AText, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + Result := ReceiveString(IdTimeoutDefault, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + {This is just in case the TickCount rolled back to zero} + FEchoTime := GetElapsedTicks(StartTime); +end; + +end. diff --git a/indy/Protocols/IdEchoUDPServer.pas b/indy/Protocols/IdEchoUDPServer.pas new file mode 100644 index 0000000..60fa2c6 --- /dev/null +++ b/indy/Protocols/IdEchoUDPServer.pas @@ -0,0 +1,68 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 2004.02.03 5:45:08 PM czhower + Name changes + + Rev 1.3 1/22/2004 7:10:04 AM JPMugaas + Tried to fix AnsiSameText depreciation. + + Rev 1.2 1/21/2004 3:27:52 PM JPMugaas + InitComponent + + Rev 1.1 10/23/2003 03:50:52 AM JPMugaas + TIdEchoUDP Ported. + + Rev 1.0 11/14/2002 02:19:38 PM JPMugaas +} + +unit IdEchoUDPServer; + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, IdGlobal, IdSocketHandle, IdUDPBase, IdUDPServer; + +type + TIdEchoUDPServer = class(TIdUDPServer) + protected + procedure DoUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_ECHO; + end; + +implementation + +{ TIdEchoUDPServer } + +procedure TIdEchoUDPServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_ECHO; +end; + +procedure TIdEchoUDPServer.DoUDPRead(AThread: TIdUDPListenerThread; + const AData: TIdBytes; ABinding: TIdSocketHandle); +begin + inherited DoUDPRead(AThread, AData, ABinding); + ABinding.SendTo(ABinding.PeerIP, ABinding.PeerPort, AData, ABinding.IPVersion); +end; + +end. diff --git a/indy/Protocols/IdExplicitTLSClientServerBase.pas b/indy/Protocols/IdExplicitTLSClientServerBase.pas new file mode 100644 index 0000000..86d7bf9 --- /dev/null +++ b/indy/Protocols/IdExplicitTLSClientServerBase.pas @@ -0,0 +1,434 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.14 10/26/2004 9:09:36 PM JPMugaas + Updated references. + + Rev 1.13 2004.02.03 5:45:36 PM czhower + Name changes + + Rev 1.12 1/25/2004 3:52:28 PM JPMugaas + Fixes for abstract SSL interface to work in NET. + + Rev 1.11 1/21/2004 1:23:38 PM JPMugaas + InitComponent. + + Rev 1.10 5/25/2003 12:06:16 AM JPMugaas + TLS checking code moved into a protected method for reuse in TIdDirectSMTP. + Note that TLS support is different in that component because of the way it + works. + + Rev 1.9 5/21/2003 3:36:42 PM BGooijen + Fixed design time bug regarding the Active property + + Rev 1.8 5/8/2003 11:27:38 AM JPMugaas + Moved feature negoation properties down to the ExplicitTLSClient level as + feature negotiation goes hand in hand with explicit TLS support. + + Rev 1.7 4/13/2003 05:38:02 PM JPMugaas + Fix for SetTLS exception problem with IdMessage.SaveToFile. + + Rev 1.6 4/5/2003 02:06:48 PM JPMugaas + TLS handshake itself can now be handled. + + Rev 1.5 3/27/2003 05:46:22 AM JPMugaas + Updated framework with an event if the TLS negotiation command fails. + Cleaned up some duplicate code in the clients. + + Rev 1.4 3/26/2003 04:19:18 PM JPMugaas + Cleaned-up some code and illiminated some duplicate things. + + Rev 1.3 3/23/2003 11:45:02 PM BGooijen + classes -> Classes + + Rev 1.2 3/18/2003 04:36:52 PM JPMugaas + + Rev 1.1 3/16/2003 06:08:34 PM JPMugaas + Fixed a bug where the wrong port number was being set. I also expanded a few + things for the server. + + Rev 1.0 3/16/2003 02:38:08 PM JPMugaas + Base class for some clients that use both implicit and explicit TLS. +} + +unit IdExplicitTLSClientServerBase; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdCmdTCPServer, + IdException, + IdGlobal, + IdIOHandler, + IdServerIOHandler, + IdTCPClient; + +type + TIdUseTLS = ( + utNoTLSSupport, + utUseImplicitTLS, // ssl iohandler req, allways tls + utUseRequireTLS, // ssl iohandler req, user command only accepted when in tls + utUseExplicitTLS // < user can choose to use tls + ); + +const + ExplicitTLSVals = [utUseRequireTLS,utUseExplicitTLS]; + DEF_USETLS = utNoTLSSupport; //we can't assume the user wants to use a SSL IOHandler + +type + TIdOnTLSNegotiationFailure = procedure(Asender : TObject; var VContinue : Boolean) of object; + + TIdExplicitTLSServer = class(TIdCmdTCPServer) + protected + FRegularProtPort : TIdPort; + FImplicitTLSProtPort : TIdPort; + FUseTLS : TIdUseTLS; + procedure Loaded; override; + procedure SetIOHandler(const AValue: TIdServerIOHandler); override; + procedure SetUseTLS(AValue : TIdUseTLS); virtual; + property UseTLS : TIdUseTLS read FUseTLS write SetUseTLS default DEF_USETLS; + procedure InitComponent; override; + end; + + TIdExplicitTLSClient = class(TIdTCPClientCustom) + protected + FRegularProtPort : TIdPort; + FImplicitTLSProtPort : TIdPort; + FUseTLS : TIdUseTLS; + FOnTLSNotAvailable : TIdOnTLSNegotiationFailure; + FOnTLSNegCmdFailed : TIdOnTLSNegotiationFailure; + FOnTLSHandShakeFailed : TIdOnTLSNegotiationFailure; + + //feature negotiation stuff + FCapabilities : TStrings; + function GetSupportsTLS : Boolean; virtual; + procedure CheckIfCanUseTLS; virtual; + procedure Loaded; override; + procedure TLSNotAvailable; + procedure DoOnTLSNotAvailable; + procedure ProcessTLSNotAvail; + + procedure TLSNegCmdFailed; + procedure DoOnTLSNegCmdFailed; + procedure ProcessTLSNegCmdFailed; + + procedure TLSHandShakeFailed; + procedure DoOnTLSHandShakeFailed; + procedure ProcessTLSHandShakeFailed; + + procedure SetIOHandler(AValue: TIdIOHandler); override; + procedure SetUseTLS(AValue : TIdUseTLS); virtual; + //Note TLSHandshake should be the ONLY method to do the actual TLS + //or SSL handshake for explicit TLS clients. + procedure TLSHandshake; virtual; + procedure InitComponent; override; + property UseTLS : TIdUseTLS read FUseTLS write SetUseTLS default DEF_USETLS; + public + destructor Destroy; override; + procedure Connect; override; + property SupportsTLS: boolean read GetSupportsTLS; + property Capabilities : TStrings read FCapabilities; + property OnTLSHandShakeFailed : TIdOnTLSNegotiationFailure read FOnTLSHandShakeFailed write FOnTLSHandShakeFailed; + property OnTLSNotAvailable : TIdOnTLSNegotiationFailure read FOnTLSNotAvailable write FOnTLSNotAvailable; + property OnTLSNegCmdFailed : TIdOnTLSNegotiationFailure read FOnTLSNegCmdFailed write FOnTLSNegCmdFailed; + end; + + EIdTLSClientException = class(EIdException); + EIdTLSClientSSLIOHandlerRequred = class(EIdTLSClientException); + EIdTLSClientCanNotSetWhileConnected = class(EIdTLSClientException); + EIdTLSClientTLSNotAvailable = class(EIdTLSClientException); + EIdTLSClientTLSNegCmdFailed = class(EIdTLSClientException); + EIdTLSClientTLSHandShakeFailed = class(EIdTLSClientException); + EIdTLSServerException = class(EIdException); + EIdTLSServerSSLIOHandlerRequired = class(EIdTLSServerException); + EIdTLSClientCanNotSetWhileActive = class(EIdTLSClientException); + +implementation + +uses + IdResourceStringsProtocols, IdSSL, IdBaseComponent, SysUtils; + +{ TIdExplicitTLSServer } + +procedure TIdExplicitTLSServer.InitComponent; +begin + inherited InitComponent; + FUseTLS := DEF_USETLS; +end; + +procedure TIdExplicitTLSServer.Loaded; +begin + inherited Loaded; + if not (IOHandler is TIdServerIOHandler) then begin + SetUseTLS(utNoTLSSupport); + end; +end; + +procedure TIdExplicitTLSServer.SetIOHandler(const AValue: TIdServerIOHandler); +begin + inherited SetIOHandler(AValue); + if not (IOHandler is TIdServerIOHandlerSSLBase) then begin + SetUseTLS(utNoTLSSupport); + end; +end; + +procedure TIdExplicitTLSServer.SetUseTLS(AValue: TIdUseTLS); +begin + if (not Active) or IsDesignTime then + begin + if IsLoading then begin + FUseTLS := AValue; + Exit; + end; + if (not (IOHandler is TIdServerIOHandlerSSLBase)) and (AValue <> utNoTLSSupport) then begin + raise EIdTLSServerSSLIOHandlerRequired.Create(RSTLSSSLIOHandlerRequired); + end; + if FUseTLS <> AValue then + begin + if AValue = utUseImplicitTLS then + begin + if DefaultPort = FRegularProtPort then begin + DefaultPort := FImplicitTLSProtPort; + end; + end else + begin + if DefaultPort = FImplicitTLSProtPort then begin + DefaultPort := FRegularProtPort; + end; + end; + FUseTLS := AValue; + end; + end else begin + raise EIdTLSClientCanNotSetWhileActive.Create(RSTLSSLCanNotSetWhileConnected); + end; +end; + +{ TIdExplicitTLSClient } + +procedure TIdExplicitTLSClient.CheckIfCanUseTLS; +begin + if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin + raise EIdTLSClientSSLIOHandlerRequred.Create(RSTLSSSLIOHandlerRequired); + end; +end; + +procedure TIdExplicitTLSClient.Connect; +begin + if UseTLS in ExplicitTLSVals then begin + // TLS only enabled later in this case! + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True; + end; + if (IOHandler is TIdSSLIOHandlerSocketBase) then begin + case FUseTLS of + utNoTLSSupport : + begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True; + end; + utUseImplicitTLS : + begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False; + end; + else + begin + if FUseTLS <> utUseImplicitTLS then begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True; + end; + end; + end; + end; + inherited Connect; +end; + +procedure TIdExplicitTLSClient.InitComponent; +begin + inherited InitComponent; + FCapabilities := TStringList.Create; + FUseTLS := DEF_USETLS; +end; + +destructor TIdExplicitTLSClient.Destroy; +begin + FreeAndNil(FCapabilities); + inherited Destroy; +end; + +//OnTLSHandShakeFailed +procedure TIdExplicitTLSClient.DoOnTLSHandShakeFailed; +var + LContinue : Boolean; +begin + LContinue := False; + if Assigned(OnTLSHandShakeFailed) then begin + FOnTLSHandShakeFailed(Self, LContinue); + end; + if not LContinue then begin + TLSHandShakeFailed; + end; +end; + +procedure TIdExplicitTLSClient.DoOnTLSNegCmdFailed; +var + LContinue : Boolean; +begin + LContinue := False; + if Assigned(OnTLSNegCmdFailed) then begin + FOnTLSNegCmdFailed(Self, LContinue); + end; + if not LContinue then begin + TLSNegCmdFailed; + end; +end; + +procedure TIdExplicitTLSClient.DoOnTLSNotAvailable; +var + LContinue : Boolean; +begin + LContinue := True; + if Assigned(FOnTLSNotAvailable) then begin + FOnTLSNotAvailable(Self, LContinue); + end; + if not LContinue then begin + TLSNotAvailable; + end; +end; + +procedure TIdExplicitTLSClient.Loaded; +begin + inherited Loaded; + if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin + SetUseTLS(utNoTLSSupport); + end; +end; + +procedure TIdExplicitTLSClient.ProcessTLSHandShakeFailed; +begin + if FUseTLS = utUseRequireTLS then begin + TLSHandShakeFailed; + end else begin + DoOnTLSHandShakeFailed; + end; +end; + +procedure TIdExplicitTLSClient.ProcessTLSNegCmdFailed; +begin + if FUseTLS = utUseRequireTLS then begin + TLSNegCmdFailed; + end else begin + DoOnTLSNegCmdFailed; + end; +end; + +procedure TIdExplicitTLSClient.ProcessTLSNotAvail; +begin + if FUseTLS = utUseRequireTLS then begin + TLSNotAvailable; + end else begin + DoOnTLSNotAvailable; + end; +end; + +procedure TIdExplicitTLSClient.SetIOHandler(AValue: TIdIOHandler); +begin + inherited SetIOHandler(AValue); + if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin + if FUseTLS <> utNoTLSSupport then begin + SetUseTLS(utNoTLSSupport); + end; + end; +end; + +procedure TIdExplicitTLSClient.SetUseTLS(AValue: TIdUseTLS); +begin + if Connected then begin + raise EIdTLSClientCanNotSetWhileConnected.Create(RSTLSSLCanNotSetWhileConnected); + end; + if IsLoading then begin + FUseTLS := AValue; + Exit; + end; + if AValue <> utNoTLSSupport then begin + CheckIfCanUseTLS; + end; + if FUseTLS <> AValue then begin + if AValue = utUseImplicitTLS then begin + if Port = FRegularProtPort then begin + Port := FImplicitTLSProtPort; + end; + end else begin + if Port = FImplicitTLSProtPort then begin + Port := FRegularProtPort; + end; + end; + FUseTLS := AValue; + end; +end; + +procedure TIdExplicitTLSClient.TLSHandshake; +begin + try + if (IOHandler is TIdSSLIOHandlerSocketBase) then begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False; + end; + except + ProcessTLSHandShakeFailed; + end; +end; + +procedure TIdExplicitTLSClient.TLSHandShakeFailed; +begin + if Connected then begin + // RLebeau 9/19/2013: do not send a goodbye command to the peer. + // The socket data may be in a bad state at this point! + Disconnect(False); + end; + // This method should always be called in the context of an active 'except' + // block, so use IndyRaiseOuterException() to capture the inner exception + // (if possible) when raising this outer exception... + IndyRaiseOuterException(EIdTLSClientTLSHandShakeFailed.Create(RSTLSSLSSLHandshakeFailed)); +end; + +procedure TIdExplicitTLSClient.TLSNegCmdFailed; +begin + if Connected then begin + Disconnect; + end; + // This method should never be called in the context of an active 'except' + // block, so do not use IndyRaiseOuterException() to capture an inner exception + // when raising this exception... + raise EIdTLSClientTLSNegCmdFailed.Create(RSTLSSLSSLCmdFailed); +end; + +procedure TIdExplicitTLSClient.TLSNotAvailable; +begin + if Connected then begin + Disconnect; + end; + raise EIdTLSClientTLSNotAvailable.Create(RSTLSSLSSLNotAvailable); +end; + +function TIdExplicitTLSClient.GetSupportsTLS: boolean; +begin + //this is a dummy for descendants to override. NET doesn't support + //abstract methods. + Result := False; +end; + +end. diff --git a/indy/Protocols/IdFIPS.pas b/indy/Protocols/IdFIPS.pas new file mode 100644 index 0000000..50d7dca --- /dev/null +++ b/indy/Protocols/IdFIPS.pas @@ -0,0 +1,230 @@ +unit IdFIPS; + +interface + +{$I IdCompilerDefines.inc} + +{ + IMPORTANT!!! + + This unit does not directly provide FIPS support. It centalizes some Indy + encryption functions and exposes a function to get and set a FIPS mode that is + implemented by the library that hooks this unit. + + The idea is that Indy will not have a FIPS certification per se but will be + able to utilize cryptographic modules that are FIPS complient. + + In addition, this unit provides a way of centralizing all hashing and HMAC + functions and to control dependancies in Indy. +} +uses + IdException, IdGlobal + {$IFDEF DOTNET} + , System.Security.Cryptography + {$ENDIF} + ; + +type +{$IFDEF DOTNET} + TIdHashIntCtx = System.Security.Cryptography.HashAlgorithm; + TIdHMACIntCtx = System.Security.Cryptography.HMAC; +{$ELSE} + TIdHashIntCtx = Pointer; + TIdHMACIntCtx = Pointer; +{$ENDIF} + + EIdFIPSAlgorithmNotAllowed = class(EIdException); + TGetFIPSMode = function: Boolean; + TSetFIPSMode = function(const AMode: Boolean): Boolean; + TIsHashingIntfAvail = function: Boolean; + TGetHashInst = function: TIdHashIntCtx; + TUpdateHashInst = procedure(ACtx: TIdHashIntCtx; const AIn: TIdBytes); + TFinalHashInst = function(ACtx: TIdHashIntCtx): TIdBytes; + TIsHMACAvail = function : Boolean; + TIsHMACIntfAvail = function : Boolean; + TGetHMACInst = function (const AKey : TIdBytes) : TIdHMACIntCtx; + TUpdateHMACInst = procedure(ACtx : TIdHMACIntCtx; const AIn: TIdBytes); + TFinalHMACInst = function(ACtx: TIdHMACIntCtx): TIdBytes; + +var + GetFIPSMode: TGetFIPSMode; + SetFIPSMode: TSetFIPSMode; + IsHashingIntfAvail: TIsHashingIntfAvail; + GetMD2HashInst: TGetHashInst; + IsMD2HashIntfAvail: TIsHashingIntfAvail; + GetMD4HashInst: TGetHashInst; + IsMD4HashIntfAvail: TIsHashingIntfAvail; + GetMD5HashInst: TGetHashInst; + IsMD5HashIntfAvail: TIsHashingIntfAvail; + GetSHA1HashInst: TGetHashInst; + IsSHA1HashIntfAvail: TIsHashingIntfAvail; + GetSHA224HashInst: TGetHashInst; + IsSHA224HashIntfAvail: TIsHashingIntfAvail; + GetSHA256HashInst: TGetHashInst; + IsSHA256HashIntfAvail: TIsHashingIntfAvail; + GetSHA384HashInst: TGetHashInst; + IsSHA384HashIntfAvail: TIsHashingIntfAvail; + GetSHA512HashInst: TGetHashInst; + IsSHA512HashIntfAvail: TIsHashingIntfAvail; + UpdateHashInst: TUpdateHashInst; + FinalHashInst: TFinalHashInst; + IsHMACAvail : TIsHMACAvail; + IsHMACMD5Avail : TIsHMACIntfAvail; + GetHMACMD5HashInst: TGetHMACInst; + IsHMACSHA1Avail : TIsHMACIntfAvail; + GetHMACSHA1HashInst: TGetHMACInst; + IsHMACSHA224Avail : TIsHMACIntfAvail; + GetHMACSHA224HashInst: TGetHMACInst; + IsHMACSHA256Avail : TIsHMACIntfAvail; + GetHMACSHA256HashInst: TGetHMACInst; + IsHMACSHA384Avail : TIsHMACIntfAvail; + GetHMACSHA384HashInst: TGetHMACInst; + IsHMACSHA512Avail : TIsHMACIntfAvail; + GetHMACSHA512HashInst: TGetHMACInst; + UpdateHMACInst : TUpdateHMACInst; + FinalHMACInst : TFinalHMACInst; + + procedure CheckMD2Permitted; + procedure CheckMD4Permitted; + procedure CheckMD5Permitted; + procedure FIPSAlgorithmNotAllowed(const AAlgorithm: String); + +implementation + +uses + IdResourceStringsProtocols, SysUtils; + +// TODO: for .NET, implement functions that use .NET Hash/HMAC classes + +procedure CheckMD2Permitted; {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if GetFIPSMode then begin + FIPSAlgorithmNotAllowed('MD2'); + end; +end; + +procedure CheckMD4Permitted; {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if GetFIPSMode then begin + FIPSAlgorithmNotAllowed('MD4'); + end; +end; + +procedure CheckMD5Permitted; {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if GetFIPSMode then begin + FIPSAlgorithmNotAllowed('MD5'); + end; +end; + +procedure FIPSAlgorithmNotAllowed(const AAlgorithm: String); +{$IFDEF USE_INLINE} inline; {$ENDIF} +begin + raise EIdFIPSAlgorithmNotAllowed.Create(Format(RSFIPSAlgorithmNotAllowed, + [AAlgorithm])); +end; + +// fips mode default procs +function DefGetFIPSMode: Boolean; +begin + Result := False; +end; + +function DefSetFIPSMode(const AMode: Boolean): Boolean; +begin + // leave this empty as we may not be using something that supports FIPS + Result := False; +end; + +function DefIsHashingIntfAvail: Boolean; +begin + Result := False; +end; + +function DefIsHashIntfAvail: Boolean; +begin + Result := False; +end; + +function DefGetHashInst : TIdHashIntCtx; +begin + Result := nil; +end; + +procedure DefUpdateHashInst(ACtx: TIdHashIntCtx; const AIn: TIdBytes); +begin +end; + +function DefFinalHashInst(ACtx: TIdHashIntCtx): TIdBytes; +begin + SetLength(Result, 0); +end; + +function DefIsHMACAvail : Boolean; +begin + Result := False; +end; + +function DefIsHMACIntfAvail: Boolean; +begin + Result := False; +end; + +function DefGetHMACInst(const AKey : TIdBytes) : TIdHMACIntCtx; +begin + Result := nil; +end; + +procedure DefUpdateHMACInst(ACtx : TIdHMACIntCtx; const AIn: TIdBytes); +begin +end; + +function DefFinalHMACInst(ACtx: TIdHMACIntCtx): TIdBytes; +begin + SetLength(Result, 0); +end; + +initialization + + GetFIPSMode := DefGetFIPSMode; + SetFIPSMode := DefSetFIPSMode; + + IsHashingIntfAvail := DefIsHashingIntfAvail; + + IsMD2HashIntfAvail := DefIsHashIntfAvail; + GetMD2HashInst := DefGetHashInst; + IsMD4HashIntfAvail := DefIsHashIntfAvail; + GetMD4HashInst := DefGetHashInst; + IsMD5HashIntfAvail := DefIsHashIntfAvail; + GetMD5HashInst := DefGetHashInst; + IsSHA1HashIntfAvail := DefIsHashIntfAvail; + GetSHA1HashInst := DefGetHashInst; + IsSHA224HashIntfAvail := DefIsHashIntfAvail; + GetSHA224HashInst := DefGetHashInst; + + IsSHA256HashIntfAvail := DefIsHashIntfAvail; + GetSHA256HashInst := DefGetHashInst; + IsSHA384HashIntfAvail := DefIsHashIntfAvail; + GetSHA384HashInst := DefGetHashInst; + IsSHA512HashIntfAvail := DefIsHashIntfAvail; + GetSHA512HashInst := DefGetHashInst; + UpdateHashInst := DefUpdateHashInst; + FinalHashInst := DefFinalHashInst; + IsHMACAvail := DefIsHMACAvail; + IsHMACMD5Avail := DefIsHMACIntfAvail; + GetHMACMD5HashInst := DefGetHMACInst; + IsHMACSHA1Avail := DefIsHMACIntfAvail; + GetHMACSHA1HashInst := DefGetHMACInst; + IsHMACSHA224Avail := DefIsHMACIntfAvail; + GetHMACSHA224HashInst := DefGetHMACInst; + IsHMACSHA256Avail := DefIsHMACIntfAvail; + GetHMACSHA256HashInst := DefGetHMACInst; + IsHMACSHA384Avail := DefIsHMACIntfAvail; + GetHMACSHA384HashInst := DefGetHMACInst; + IsHMACSHA512Avail := DefIsHMACIntfAvail; + GetHMACSHA512HashInst := DefGetHMACInst; + + UpdateHMACInst := DefUpdateHMACInst; + FinalHMACInst := DefFinalHMACInst; + +end. diff --git a/indy/Protocols/IdFSP.pas b/indy/Protocols/IdFSP.pas new file mode 100644 index 0000000..e7c7162 --- /dev/null +++ b/indy/Protocols/IdFSP.pas @@ -0,0 +1,1333 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ + + + Rev 1.17 2/10/2005 2:24:38 PM JPMugaas + Minor Restructures for some new UnixTime Service components. + + + Rev 1.16 1/17/2005 7:29:12 PM JPMugaas + Now uses new TIdBuffer functionality. + + + Rev 1.15 1/9/2005 6:08:06 PM JPMugaas + Payload size now specified for CC_GET_FILE. + Now will raise exception if you specify a packet size less than 512. + + + Rev 1.12 11/12/2004 8:37:36 AM JPMugaas + Minor compile error. OOPS!!! + + + Rev 1.11 11/11/2004 11:22:54 PM JPMugaas + Removed an $IFDEF that's no longer needed. + + + Rev 1.10 11/8/2004 8:36:04 PM JPMugaas + Added value for command that may appear later. + + + Rev 1.9 11/7/2004 11:34:16 PM JPMugaas + Now uses inherited methods again. The inherited methods now use the Binding + methods we used here. + + + Rev 1.8 11/6/2004 1:46:34 AM JPMugaas + Minor bug fix for when there is no data in a reply to CC_GET_PRO. + + + Rev 1.7 11/5/2004 7:55:02 PM JPMugaas + Changed to use, Connect, Recv, Send, and Disconnect instead of ReceiveFrom + and SendTo. This should improve performance as we do make repeated contacts + to the host and UDP connect will cause the stack to filter out packets that + aren't from the peer. There should only be one DNS resolution per session + making this more efficient (cutting down to about 87 seconds to get a dir). + + + Rev 1.4 10/31/2004 1:49:58 AM JPMugaas + Now uses item type from TIdFTPList for dirs and files. We don't use Skip + items or end of dir marker items. + + + Rev 1.2 10/30/2004 10:23:58 PM JPMugaas + Should be much faster. + + + Rev 1.1 10/30/2004 7:04:26 PM JPMugaas + FSP Upload. + + + Rev 1.0 10/29/2004 12:34:20 PM JPMugaas + File Services Protocol implementation started + +} +unit IdFSP; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdException, + IdFTPList, + IdGlobal, + IdThreadSafe, + IdUDPClient; + +{This is based on: + +http://cvs.sourceforge.net/viewcvs.py/fsp/fsp/doc/PROTOCOL?rev=1.4&view=markup + +and the Java Lib at fsp.sourceforge.net was also referenced. + +I have verified this on a CygWin build of the FSP Server at fsp.sourceforge.net. +} +{ + + +FSP Packet format: + HEADER - size = Fixed size 12 bytes. Always present. + DATA - size = defined in header (DATA_LENGTH) + XTRA DATA- size = packet_size - header_size (12) - DATA_LENGTH + +Maximal data size DATA_LENGTH + XTRA_DATA length is 1024. Clients and servers +are not required to support XTRA DATA (but in current FSP implementation does). +If XTRA DATA are provided, there must be also contained in MESSAGE_CHECKSUM. + +HEADER FORMAT (12 bytes) + byte FSP_COMMAND + byte MESSAGE_CHECKSUM + word KEY + word SEQUENCE + word DATA_LENGTH + long FILE_POSITION + +MESSAGE_CHECKSUM +Entire packet (HEADER + DATA + XTRA DATA) is checksumed. When computing a +checksum use zero in place of MESSAGE_CHECKSUM header field. + +Due to some unknown reason, method of computing checksums is different in each +direction. For packets travelling from server to client initial checksum +value is zero, otherwise it is HEADER + DATA + XTRA DATA size. + +Checksums in server->client direction are computed as follows: + + /* assume that we have already zeroed checksum in packet */ + unsigned int sum,checksum; + for(t = packet_start, sum = 0; t < packet_end; sum += *t++); + checksum= sum + (sum >> 8); + +KEY +Client's message to server contain a KEY value that is the same as the KEY +value of the previous message received from the server. KEY is choosen random +by server. +} + +{ + + + + CC_VERSION 0x10- Get server version string and setup + + request + file position: ignored + data: not used +xtra data: not used + +reply +file position: size of optional extra version data +data: ASCIIZ Server version string +xtra data: optional extra version data +byte - FLAGS + bit 0 set - server does logging +bit 1 set - server is read only +bit 2 set - reverse lookup required +bit 3 set - server is in private mode +bit 4 set - thruput control + if bit 4 is set thruput info follows +long - max_thruput allowed (in bytes/sec) +word - max. packet size supported by server +} + +const + IdPORT_FSP = 21; + + HSIZE = 12; //header size + DEF_MAXSPACE = 1012; //data length + DEF_MAXSIZE = DEF_MAXSPACE+HSIZE; //default maximum packet size + +//commands + CC_VERSION = $10; //Get server version string and setup + CC_INFO = $11; //return server's extended info block + CC_ERR = $40; //error response from server + CC_GET_DIR = $41; // get a directory listing + CC_GET_FILE = $42; // get a file + CC_UP_LOAD = $43; // open a file for writing + CC_INSTALL = $44; // close and install file opened for writing + CC_DEL_FILE = $45; // delete a file + CC_DEL_DIR = $46; // delete a directory + CC_GET_PRO = $47; // get directory protection + CC_SET_PRO = $48; // set directory protection + CC_MAKE_DIR = $49; // create a directory + CC_BYE = $4A; // finish a session + CC_GRAB_FILE = $4B; // atomic get+delete a file + CC_GRAB_DONE = $4C; // atomic get+delete a file done + CC_STAT = $4D; // get information about file/directory + CC_RENAME = $4E; // rename file or directory + CC_CH_PASSW = $4F; // change password +//Reserved commands: + CC_LIMIT = $80; + { commands > 0x7F will have extended + header. No such extensions or commands + which uses that are known today. This + header will be used in protocol version 3. } + + CC_TEST = $81; //reserved for testing of new header + + RDTYPE_END = $00; + RDTYPE_FILE = $01; + RDTYPE_DIR = $02; + RDTYPE_SKIP = $2A; //42 + + MINTIMEOUT = 1340; //1.34 seconds + MAXTIMEOUT = 300000; //300 seconds +type + EIdFSPException = class(EIdException); + EIdFSPFileAlreadyExists = class(EIdFSPException); + EIdFSPFileNotFound = class(EIdFSPException); + EIdFSPProtException = class(EIdFSPException); + EIdFSPPacketTooSmall = class(EIdFSPException); +{ +RDIRENT.HEADER types: + RDTYPE_END 0x00 + RDTYPE_FILE 0x01 + RDTYPE_DIR 0x02 + RDTYPE_SKIP 0x2A +} + TIdFSPStatInfo = class(TCollectionItem) + protected + FModifiedDateGMT : TDateTime; + FModifiedDate: TDateTime; + //Size is Int64 in case FSP 3 has an expansion, otherise, it can only handle + //file sizes up 4 GB's. It's not a bug, it's a feature. + FSize: Int64; + FItemType :TIdDirItemType; + published + property ItemType :TIdDirItemType read FItemType write FItemType; + property Size: Int64 read FSize write FSize; + property ModifiedDate: TDateTime read FModifiedDate write FModifiedDate; + property ModifiedDateGMT : TDateTime read FModifiedDateGMT write FModifiedDateGMT; + end; + + TIdFSPListItem = class(TIdFSPStatInfo) + protected + FFileName: string; + published + property FileName: string read FFileName write FFileName; + end; + + TIdFSPListItems = class(TCollection) + protected + function GetItems(AIndex: Integer): TIdFSPListItem; + procedure SetItems(AIndex: Integer; const Value: TIdFSPListItem); + public + function Add: TIdFSPListItem; + constructor Create; reintroduce; + function ParseEntries(const AData : TIdBytes; const ADataLen : UInt32) : Boolean; + function IndexOf(AItem: TIdFSPListItem): Integer; + property Items[AIndex: Integer]: TIdFSPListItem read GetItems write SetItems; default; + end; + + TIdFSPDirInfo = class(TObject) + protected + FOwnsDir, + FCanDeleteFiles, + FCanAddFiles, + FCanMakeDir, + FOnlyOwnerCanReadFiles, + FHasReadMe, + FCanBeListed, + FCanRenameFiles : Boolean; + FReadMe : String; + public + property OwnsDir : Boolean read FOwnsDir write FOwnsDir; + property CanDeleteFiles : Boolean read FCanDeleteFiles write FCanDeleteFiles; + property CanAddFiles : Boolean read FCanAddFiles write FCanAddFiles; + property CanMakeDir : Boolean read FCanMakeDir write FCanMakeDir; + property OnlyOwnerCanReadFiles : Boolean read FOnlyOwnerCanReadFiles write FOnlyOwnerCanReadFiles; + property HasReadMe : Boolean read FHasReadMe write FHasReadMe; +{ + + + Compatibility + +Versions older than 2.8.1b6 do not uses bits 6 and 7. This +causes that directory can be listable even it do not have +6th bit set. +} + property CanBeListed : Boolean read FCanBeListed write FCanBeListed; + property CanRenameFiles : Boolean read FCanRenameFiles write FCanRenameFiles; + property ReadMe : String read FReadMe write FReadMe; + end; + + TIdFSPPacket = class(TObject) + protected + FCmd: Byte; + FFilePosition: UInt32; + FData: TIdBytes; + FDataLen : Word; + FExtraData: TIdBytes; +// FExtraDataLen : UInt32; + FSequence: Word; + FKey: Word; + FValid : Boolean; + public + constructor Create; + function WritePacket : TIdBytes; + procedure ReadPacket(const AData : TIdBytes; const ALen : UInt32); + property Valid : Boolean read FValid; + property Cmd : Byte read FCmd write FCmd; + property Key : Word read FKey write FKey; + property Sequence : Word read FSequence write FSequence; + property FilePosition : UInt32 read FFilePosition write FFilePosition; + property Data : TIdBytes read FData write FData; + property DataLen : Word read FDataLen write FDataLen; + property ExtraData : TIdBytes read FExtraData write FExtraData; + // property WritePacket : TIdBytes read GetWritePacket write SetWritePacket; + end; + + TIdFSPLogEvent = procedure (Sender : TObject; APacket : TIdFSPPacket) of object; + + TIdFSP = class(TIdUDPClient) + protected + FConEstablished : Boolean; + FSequence : Word; + FKey : Word; + FSystemDesc: string; + FSystemServerLogs : Boolean; + FSystemReadOnly : Boolean; + FSystemReverseLookupRequired : Boolean; + FSystemPrivateMode : Boolean; + FSystemAcceptsExtraData : Boolean; + FThruputControl : Boolean; + + FServerMaxThruPut : UInt32; //bytes per sec + FServerMaxPacketSize : Word; //maximum packet size server supports + FClientMaxPacketSize : Word; //maximum packet we wish to support + FDirectoryListing: TIdFSPListItems; + FDirInfo : TIdFSPDirInfo; + FStatInfo : TIdFSPStatInfo; + FOnRecv, FOnSend : TIdFSPLogEvent; + FAbortFlag : TIdThreadSafeBoolean; + FInCmd : TIdThreadSafeBoolean; + + //note: This is optimized for performance - DO NOT MESS with it even if you don't like it + //or think its wrong. There is a performance penalty that is noticable with downloading, + //uploading, and dirs because those use a series of packets - not one and we limited in + //packet size. We also do not want to eat CPU cycles excessively which I've noticed + //with previous code. + procedure SendCmdOnce(ACmdPacket, ARecvPacket : TIdFSPPacket; var VTempBuf : TIdBytes; const ARaiseException : Boolean=True); overload; + procedure SendCmdOnce(const ACmd : Byte; const AData, AExtraData : TIdBytes; + const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB + var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload; + + procedure SendCmd(ACmdPacket, ARecvPacket : TIdFSPPacket; var VTempBuf : TIdBytes; const ARaiseException : Boolean=True); overload; + procedure SendCmd(const ACmd : Byte; const AData, AExtraData : TIdBYtes; + const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB + var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload; + procedure SendCmd(const ACmd : Byte; const AData : TIdBYtes; + const AFilePosition : Int64; //in case FSP 3.0 does support more than 4GB + var VData, VExtraData : TIdBytes; const ARaiseException : Boolean=True); overload; + procedure ParseDirInfo(const ABuf, AExtraBuf: TIdBytes; ADir : TIdFSPDirInfo); + procedure InitComponent; override; + function MaxBufferSize : Word; + function PrefPayloadSize : Word; + procedure SetClientMaxPacketSize(const AValue: Word); + public + destructor Destroy; override; + procedure Connect; override; //this is so we can use it similarly to FTP + procedure Disconnect; override; + procedure Version; + procedure AbortCmd; + procedure Delete(const AFilename: string); + procedure RemoveDir(const ADirName: string); + procedure Rename(const ASourceFile, ADestFile: string); + procedure MakeDir(const ADirName: string); + //this is so we can use it similarly to FTP + //and also sends a BYE command which is the courteous thing to do. + procedure List; overload; + procedure List(const ASpecifier: string); overload; + procedure GetDirInfo(const ADIR : String); overload; + procedure GetDirInfo(const ADIR : String; ADirInfo : TIdFSPDirInfo); overload; + procedure GetStatInfo(const APath : String); + procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean = False; + AResume: Boolean = False); overload; + procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = False); overload; + procedure Put(const ASource: TStream; const ADestFile: string; const AGMTTime : TDateTime = 0); overload; + procedure Put(const ASourceFile: string; const ADestFile: string=''); overload; + property SystemDesc: string read FSystemDesc; + property SystemServerLogs : Boolean read FSystemServerLogs; + property SystemReadOnly : Boolean read FSystemReadOnly; + property SystemReverseLookupRequired : Boolean read FSystemReverseLookupRequired; + property SystemPrivateMode : Boolean read FSystemPrivateMode; + property SystemAcceptsExtraData : Boolean read FSystemAcceptsExtraData; + property ThruputControl : Boolean read FThruputControl; + property ServerMaxThruPut : UInt32 read FServerMaxThruPut; + property ServerMaxPacketSize : Word read FServerMaxPacketSize; + property ClientMaxPacketSize : Word read FClientMaxPacketSize write SetClientMaxPacketSize; + property DirectoryListing: TIdFSPListItems read FDirectoryListing; + property DirInfo : TIdFSPDirInfo read FDirInfo; + property StatInfo : TIdFSPStatInfo read FStatInfo; + published + property Port default IdPORT_FSP; + property OnWork; + property OnWorkBegin; + property OnWorkEnd; + property OnRecv : TIdFSPLogEvent read FOnRecv write FOnRecv; + property OnSend : TIdFSPLogEvent read FOnSend write FOnSend; + end; + +implementation + +uses + //facilitate inlining only. + {$IFDEF KYLIXCOMPAT} + Libc, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + Posix.Unistd, + {$ENDIF} + {$IFDEF WINDOWS} + {$IFDEF USE_INLINE} + Windows, + {$ELSE} + //facilitate inlining only. + {$IFDEF VCL_2009_OR_ABOVE} + Windows, + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + System.Threading, + {$ENDIF} + {$ENDIF} + IdComponent, IdGlobalProtocols, IdResourceStringsProtocols, IdStack, IdStream, SysUtils; + +function ParseASCIIZPos(const ABytes: TIdBytes ; const ALen : UInt32; var VPos : UInt32): String; +var + i : UInt32; +begin + if VPos < ALen then begin + for i := VPos to ALen-1 do begin + if ABytes[i] = 0 then begin + Break; + end; + end; + VPos := i; + Result := BytesToString(ABytes, i); + end else begin + Result := ''; + end; +end; + +function ParseASCIIZLen(const ABytes : TIdBytes; const ALen : UInt32) : String; +var + LPos : UInt32; +begin + LPos := 0; + Result := ParseASCIIZPos(ABytes, ALen, LPos); +end; + +function ParseASCIIZ(const ABytes : TIdBytes) : String; +var + LPos : UInt32; +begin + LPos := 0; + Result := ParseASCIIZPos(ABytes, Length(ABytes), LPos); +end; + +procedure ParseStatInfo(const AData : TIdBytes; VL : TIdFSPStatInfo; var VI : UInt32); +var + LC : UInt32; +begin + //we don't parse the file type because there is some variation between CC_GET_DIR and CC_STAT + CopyBytesToHostUInt32(AData, VI, LC); + + VL.FModifiedDateGMT := UnixDateTimeToDelphiDateTime(LC); + VL.FModifiedDate := VL.FModifiedDateGMT + OffSetFromUTC; + Inc(VI, 4); + + CopyBytesToHostUInt32(AData, VI, LC); + VL.Size := LC; + Inc(VI, 5); //we want to skip over the type byte we processed earlier +end; + +{ TIdFSP } + +procedure TIdFSP.Connect; +begin + FSequence := 1; + FKey := 0; + FServerMaxThruPut := 0; + FServerMaxPacketSize := DEF_MAXSIZE; + inherited Connect; +end; + +destructor TIdFSP.Destroy; +begin + Disconnect; + FreeAndNil(FDirInfo); + FreeAndNil(FDirectoryListing); + FreeAndNil(FStatInfo); + FreeAndNil(FAbortFlag); + FreeAndNil(FInCmd); + + inherited Destroy; +end; + +procedure TIdFSP.Disconnect; +var + LBuf, LData, LExtra : TIdBytes; +begin + AbortCmd; + if FConEstablished then begin + SetLength(LBuf, 0); + SendCmd(CC_BYE, LBuf, 0, LData, LExtra); + inherited Disconnect; + end; + FConEstablished := False; +end; + +procedure TIdFSP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean); +var + LSendPacket : TIdFSPPacket; + LRecvPacket : TIdFSPPacket; + LLen : Integer; + LTmpBuf : TIdBytes; +begin + SetLength(LTmpBuf, MaxBufferSize); + LSendPacket := TIdFSPPacket.Create; + try + LRecvPacket := TIdFSPPacket.Create; + try + if AResume then begin + LSendPacket.FFilePosition := ADest.Position; + end else begin + LSendPacket.FFilePosition := 0; + end; + LSendPacket.Cmd := CC_GET_FILE; + LSendPacket.FData := ToBytes(ASourceFile+#0); + LSendPacket.FDataLen := Length(LSendPacket.FData); + //specify a preferred block size + SetLength(LSendPacket.FExtraData, 2); + CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0); + + BeginWork(wmRead); + try + repeat + SendCmd(LSendPacket, LRecvPacket, LTmpBuf); + LLen := LRecvPacket.FDataLen; //Length(LRecvPacket.Data); + if LLen > 0 then begin + TIdStreamHelper.Write(ADest, LRecvPacket.Data, LLen); + DoWork(wmRead, LLen); + Inc(LSendPacket.FFilePosition, LLen); + end else begin + Break; + end; + until False; + finally + EndWork(wmRead); + end; + finally + FreeAndNil(LRecvPacket); + end; + finally + FreeAndNil(LSendPacket); + end; +end; + +procedure TIdFSP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean; AResume: Boolean); +var + LDestStream: TStream; +begin + if ACanOverwrite and (not AResume) then begin + SysUtils.DeleteFile(ADestFile); + LDestStream := TIdFileCreateStream.Create(ADestFile); + end + else if (not ACanOverwrite) and AResume then begin + LDestStream := TIdAppendFileStream.Create(ADestFile); + end + else begin + raise EIdFSPFileAlreadyExists.Create(RSDestinationFileAlreadyExists); + end; + + try + Get(ASourceFile, LDestStream, AResume); + finally + FreeAndNil(LDestStream); + end; +end; + +procedure TIdFSP.GetDirInfo(const ADIR: String); +begin + GetDirInfo(ADir, FDirInfo); +end; + +procedure TIdFSP.InitComponent; +begin + inherited InitComponent; + FAbortFlag := TIdThreadSafeBoolean.Create; + FAbortFlag.Value := False; + //you have to use FPort or this will cause a stack overflow + FPort := IdPORT_FSP; + FSequence := 0; + FKey := 0; + FDirInfo := TIdFSPDirInfo.Create; + FDirectoryListing := TIdFSPListItems.Create; + FStatInfo := TIdFSPStatInfo.Create(nil); + BroadcastEnabled := False; + FConEstablished := False; + FClientMaxPacketSize := DEF_MAXSIZE; + FInCmd := TIdThreadSafeBoolean.Create; + FInCmd.Value := False; + + +end; + +procedure TIdFSP.List; +begin + List('/'); +end; + +procedure TIdFSP.List(const ASpecifier: string); +var + LSendPacket : TIdFSPPacket; + LRecvPacket : TIdFSPPacket; + LTmpBuf : TIdBytes; + LSpecifier: String; +begin + LSpecifier := ASpecifier; + if LSpecifier = '' then begin + LSpecifier := '/'; + end; + SetLength(LTmpBuf, MaxBufferSize); + LSendPacket := TIdFSPPacket.Create; + try + LRecvPacket := TIdFSPPacket.Create; + try + LSendPacket.Cmd := CC_GET_DIR; + LSendPacket.FFilePosition := 0; + SetLength(LRecvPacket.FData, MaxBufferSize); + SetLength(LSendPacket.FExtraData, 2); + + CopyTIdNetworkUInt16(PrefPayloadSize, LSendPacket.FExtraData, 0); + + FDirectoryListing.Clear; + repeat + LSendPacket.Data := ToBytes(LSpecifier+#0); + LSendPacket.DataLen := Length(LSendPacket.Data); + + SendCmd(LSendPacket,LRecvPacket,LTmpBuf); + + if LRecvPacket.DataLen > 0 then begin + Inc(LSendPacket.FFilePosition, LRecvPacket.DataLen); + end else begin + Break; + end; + + if LRecvPacket.DataLen < PrefPayloadSize then begin + Break; + end; + until FDirectoryListing.ParseEntries(LRecvPacket.FData, LRecvPacket.FDataLen); + finally + FreeAndNil(LRecvPacket); + end; + finally + FreeAndNil(LSendPacket); + end; +end; + +procedure TIdFSP.SendCmd(const ACmd: Byte; const AData, AExtraData: TIdBytes; + const AFilePosition: Int64; var VData, VExtraData: TIdBytes; + const ARaiseException : Boolean = True); +var + LSendPacket : TIdFSPPacket; + LRecvPacket : TIdFSPPacket; + LTmpBuf : TIdBytes; +begin + SetLength(LTmpBuf, MaxBufferSize); + LSendPacket := TIdFSPPacket.Create; + try + LRecvPacket := TIdFSPPacket.Create; + try + LSendPacket.Cmd := ACmd; + LSendPacket.FilePosition := AFilePosition; + LSendPacket.Data := AData; + LSendPacket.FDataLen := Length(AData); + LSendPacket.ExtraData := AExtraData; + SendCmd(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException); + VData := LRecvPacket.Data; + VExtraData := LRecvPacket.ExtraData; + finally + FreeAndNil(LRecvPacket); + end; + finally + FreeAndNil(LSendPacket); + end; +end; + +procedure TIdFSP.SendCmd(const ACmd: Byte; const AData: TIdBytes; + const AFilePosition: Int64; var VData, VExtraData: TIdBytes; + const ARaiseException : Boolean = True); +var + LExtraData : TIdBytes; +begin + SetLength(LExtraData, 0); + SendCmd(ACmd, AData, LExtraData, AFilePosition, VData, VExtraData, ARaiseException); +end; + +procedure TIdFSP.Version; +var + LData, LBuf, LExtraBuf : TIdBytes; + LDetails : Byte; +begin +{ + we use this instead of SendCmd because of the following note + in the protocol specification + +FILE SERVICE PROTOCOL VERSION 2, OFFICIAL PROTOCOL DEFINITION, FSP v2, +Document version 0.17, Last updated 25 Dec 2004 + (http://fsp.sourceforge.net/doc/PROTOCOL.txt): + + + +Note + +Some fsp servers do not responds to this command, +because this command is used by FSP scanners and +servers do not wishes to be detected. + } + SetLength(LData, 0); + SendCmdOnce(CC_VERSION, LData, LData, 0, LBuf, LExtraBuf); + if Length(LData) > 0 then begin + FSystemDesc := ParseASCIIZ(LBuf); + if Length(LExtraBuf) > 0 then begin + LDetails := LExtraBuf[0]; + //bit 0 set - server does logging + FSystemServerLogs := (LDetails and $01) = $01; + //bit 1 set - server is read only + FSystemReadOnly := (LDetails and $02) = $02; + //bit 2 set - reverse lookup required + FSystemReverseLookupRequired := (LDetails and $04) = $04; + //bit 3 set - server is in private mode + FSystemPrivateMode := (LDetails and $08) = $08; + //if bit 4 is set thruput info follows + FThruputControl := (LDetails and $10) = $10; + //bit 5 set - server accept XTRA + //DATA on input + FSystemAcceptsExtraData := (LDetails and $20) = $20; + //long - max_thruput allowed (in bytes/sec) + //word - max. packet size supported by server + if FThruputControl then begin + if Length(LExtraBuf) > 4 then begin + CopyBytesToHostUInt32(LExtraBuf, 1, FServerMaxThruPut); + if Length(LExtraBuf) > 6 then begin + CopyBytesToHostUInt16(LExtraBuf, 5, FServerMaxPacketSize); + end; + end; + end else + begin + if Length(LExtraBuf) > 2 then begin + CopyBytesToHostUInt16(LExtraBuf, 1, FServerMaxPacketSize); + end; + end; + end; + end; +end; + +procedure TIdFSP.SendCmd(ACmdPacket, ARecvPacket: TIdFSPPacket; + var VTempBuf : TIdBytes; const ARaiseException : Boolean = True); +var + LLen : Integer; + LSendBuf : TIdBytes; + LMSec : Integer; +begin + FInCmd.Value := True; + try + Inc(FSequence); + FAbortFlag.Value := False; + //we don't set the temp buff size here for speed. + ACmdPacket.Key := FKey; + ACmdPacket.Sequence := FSequence; + LMSec := MINTIMEOUT; + LSendBuf := ACmdPacket.WritePacket; + + //It's very important that you have some way of aborting this loop + //if you do not and the server does not reply, this can go for infinity. + //AbortCmd is ThreadSafe. + + while not FAbortFlag.Value do + begin + SendBuffer(LSendBuf); + + if Assigned(FOnSend) then begin + FOnSend(Self, ACmdPacket); + end; + + IndySleep(5); //this is so we don't eat up all of the CPU + LLen := ReceiveBuffer(VTempBuf, LMsec); + + ARecvPacket.ReadPacket(VTempBuf, LLen); + if ARecvPacket.Valid then begin + if Assigned(FOnRecv) then begin + FOnRecv(Self, ARecvPacket); + end; + if ARecvPacket.Sequence = FSequence then begin + Break; + end; + end; + + LMSec := Round(LMSec * 1.5); + if LMSec > MAXTIMEOUT then begin + LMSec := MAXTIMEOUT; + end; + end; + + if not FAbortFlag.Value then begin + FKey := ARecvPacket.Key; + end; + FAbortFlag.Value := False; + + if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin + raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen)); + end; + finally + FInCmd.Value := False; + end; +end; + +procedure TIdFSP.GetStatInfo(const APath: String); +var + LData, LBuf,LExtraBuf : TIdBytes; + i : UInt32; +begin +{ +data format is the same as in directory listing with exception +that there is no file name appended. If file do not exists or +there is other problem (no access rights) return type of file is +0. + + struct STAT { + long time; + long size; + byte type; +} + i := 0; + LData := ToBytes(APath + #0); + SendCmd(CC_STAT, LData, 0, LBuf, LExtraBuf); + if Length(LBuf) > 8 then begin + case LBuf[8] of + 0 : //file not found + begin + raise EIdFSPFileNotFound.Create(RSFSPNotFound); + end; + RDTYPE_FILE : + begin + FStatInfo.ItemType := ditFile; + end; + RDTYPE_DIR : + begin + FStatInfo.ItemType := ditDirectory; + end; + end; + ParseStatInfo(LBuf, FStatInfo, i); + end; +end; + +procedure TIdFSP.Put(const ASource: TStream; const ADestFile: string; const AGMTTime: TDateTime); +var + LUnixDate : UInt32; + LSendPacket : TIdFSPPacket; + LRecvPacket : TIdFSPPacket; + LPosition : UInt32; + LLen : Integer; + LTmpBuf : TIdBytes; +begin + LPosition := 0; + SetLength(LTmpBuf, MaxBufferSize); + LSendPacket := TIdFSPPacket.Create; + try + LRecvPacket := TIdFSPPacket.Create; + try + SetLength(LSendPacket.FData, PrefPayloadSize); + LSendPacket.Cmd := CC_UP_LOAD; + + repeat + LLen := TIdStreamHelper.ReadBytes(ASource, LSendPacket.FData, PrefPayloadSize, 0); + if LLen = 0 then begin + Break; + end; + + LSendPacket.FDataLen := LLen; + LSendPacket.FilePosition := LPosition; + SendCmd(LSendPacket, LRecvPacket, LTmpBuf); + + if LLen < PrefPayloadSize then begin + Break; + end; + Inc(LPosition, LLen); + until False; + + //send the Install packet + LSendPacket.Cmd := CC_INSTALL; + LSendPacket.FilePosition := 0; + LSendPacket.Data := ToBytes(ADestFile+#0); + LSendPacket.FDataLen := Length(LSendPacket.Data); + //File date - optional + if AGMTTime = 0 then begin + SetLength(LSendPacket.FExtraData, 0); + end else begin + LUnixDate := DateTimeToUnix(AGMTTime); + SetLength(LSendPacket.FExtraData, 4); + CopyTIdNetworkUInt32(LUnixDate, LSendPacket.FExtraData, 0); + end; + SendCmd(LSendPacket, LRecvPacket, LTmpBuf); + finally + FreeAndNil(LRecvPacket); + end; + finally + FreeAndNil(LSendPacket); + end; +end; + +procedure TIdFSP.Put(const ASourceFile, ADestFile: string); +var + LSourceStream: TStream; + LDestFileName : String; +begin + LDestFileName := ADestFile; + if LDestFileName = '' then begin + LDestFileName := ExtractFileName(ASourceFile); + end; + LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile); + try + Put(LSourceStream, LDestFileName, GetGMTDateByName(ASourceFile)); + finally + FreeAndNil(LSourceStream); + end; +end; + +procedure TIdFSP.Delete(const AFilename: string); +var + LData : TIdBytes; + LBuf, LExBuf : TIdBytes; +begin + LData := ToBytes(AFilename+#0); + SendCmd(CC_DEL_FILE, LData, 0, LBuf, LExBuf); +end; + +procedure TIdFSP.MakeDir(const ADirName: string); +var + LData : TIdBytes; + LBuf, LExBuf : TIdBytes; +begin + LData := ToBytes(ADirName+#0); + SendCmd(CC_MAKE_DIR, LData, 0, LBuf, LExBuf); + ParseDirInfo(LBuf, LExBuf, FDirInfo); +end; + +procedure TIdFSP.RemoveDir(const ADirName: string); +var + LData : TIdBytes; + LBuf, LExBuf : TIdBytes; +begin + LData := ToBytes(ADirName+#0); + SendCmd(CC_DEL_DIR, LData, 0, LBuf, LExBuf); +end; + +procedure TIdFSP.Rename(const ASourceFile, ADestFile: string); +var + LBuf, LData, LDataExt : TIdBytes; +begin + SetLength(LData, 0); + SetLength(LDataExt, 0); + LBuf := ToBytes(ASourceFile+#0+ADestFile); + SendCmd(CC_RENAME, LBuf, 0, LData, LDataExt); +end; + +procedure TIdFSP.ParseDirInfo(const ABuf, AExtraBuf: TIdBytes; ADir : TIdFSPDirInfo); +begin + ADir.ReadMe := ParseASCIIZ(ABuf); + if Length(AExtraBuf) > 0 then begin + //0 - caller owns the directory + ADir.OwnsDir := (AExtraBuf[0] and $01) = $01; + //1 - files can be deleted from this dir + ADir.CanDeleteFiles := (AExtraBuf[0] and $02) = $02; + // 2 - files can be added to this dir + ADir.CanAddFiles := (AExtraBuf[0] and $04) = $04; + //3 - new subdirectories can be created + ADir.CanMakeDir := (AExtraBuf[0] and $08) = $08; + //4 - files are NOT readable by non-owners + ADir.OnlyOwnerCanReadFiles := (AExtraBuf[0] and $10) = $10; + //5 - directory contain an readme file + ADir.HasReadMe := (AExtraBuf[0] and $20) = $20; + //6 - directory can be listed + ADir.CanBeListed := (AExtraBuf[0] and $40) = $40; + //7 - files can be renamed in this directory + ADir.CanRenameFiles := (AExtraBuf[0] and $80) = $80; + end; +end; + +procedure TIdFSP.GetDirInfo(const ADIR: String; ADirInfo: TIdFSPDirInfo); +var + LData, LBuf, LExtraBuf : TIdBytes; +begin + LData := ToBytes(ADIR+#0); + SendCmd(CC_GET_PRO, LData, 0, LBuf, LExtraBuf); + ParseDirInfo(LBuf, LExtraBuf, ADirInfo); +end; + +procedure TIdFSP.SendCmdOnce(ACmdPacket, ARecvPacket: TIdFSPPacket; + var VTempBuf: TIdBytes; const ARaiseException: Boolean); +var + LLen : Integer; + LBuf : TIdBytes; + LSendBuf : TIdBytes; +//This is for where there may not be a reply to a command from a server. +begin + Inc(FSequence); + SetLength(LBuf, MaxBufferSize); + ACmdPacket.Key := FKey; + ACmdPacket.Sequence := FSequence; + + LSendBuf := ACmdPacket.WritePacket; + SendBuffer(LSendBuf); + + if Assigned(FOnSend) then begin + FOnSend(Self, ACmdPacket); + end; + + repeat + LLen := ReceiveBuffer(LBuf, MINTIMEOUT); + if LLen = 0 then begin + Break; + end; + + ARecvPacket.ReadPacket(LBuf, LLen); + + if ARecvPacket.Valid then begin + if Assigned(FOnRecv) then begin + FOnRecv(Self, ARecvPacket); + end; + if (ARecvPacket.Sequence = FSequence) then begin + FKey := ARecvPacket.Key; + Break; + end; + end; + until False; + + if (ARecvPacket.Cmd = CC_ERR) and ARaiseException then begin + raise EIdFSPProtException.Create(ParseASCIIZLen(ARecvPacket.Data, ARecvPacket.DataLen)); + end; +end; + +procedure TIdFSP.SendCmdOnce(const ACmd: Byte; const AData, + AExtraData: TIdBytes; const AFilePosition: Int64; var VData, + VExtraData: TIdBytes; const ARaiseException: Boolean); +var + LSendPacket : TIdFSPPacket; + LRecvPacket : TIdFSPPacket; + LTmpBuf : TIdBytes; +begin + SetLength(LTmpBuf, MaxBufferSize); + LSendPacket := TIdFSPPacket.Create; + try + LRecvPacket := TIdFSPPacket.Create; + try + LSendPacket.Cmd := ACmd; + LSendPacket.FilePosition := AFilePosition; + LSendPacket.Data := AData; + LSendPacket.FDataLen := Length(AData); + LSendPacket.ExtraData := AExtraData; + SendCmdOnce(LSendPacket, LRecvPacket, LTmpBuf, ARaiseException); + VData := LRecvPacket.Data; + VExtraData := LRecvPacket.ExtraData; + finally + FreeAndNil(LRecvPacket); + end; + finally + FreeAndNil(LSendPacket); + end; +end; + +function TIdFSP.MaxBufferSize: Word; +//use only for calculating buffer for reading UDP packet +begin + Result := IndyMax(FClientMaxPacketSize, DEF_MAXSIZE); + Result := IndyMax(FServerMaxPacketSize, Result); + Inc(Result, HSIZE); //just in case +end; + +function TIdFSP.PrefPayloadSize: Word; +//maximum size of the data feild we want to use +begin + Result := IndyMin(FClientMaxPacketSize, FServerMaxPacketSize); + Dec(Result, HSIZE); +end; + +procedure TIdFSP.SetClientMaxPacketSize(const AValue: Word); +begin +//maximal size required by RFC +//note that 512 gives a payload of 500 bytes in a packet + if AValue < 512 then begin + raise EIdFSPPacketTooSmall.Create(RSFSPPacketTooSmall); + end; + FClientMaxPacketSize := AValue; +end; + +procedure TIdFSP.AbortCmd; +begin + //we don't want to go into the abort loop if there is no command + //being send. If that happens, your program could hang. + if FInCmd.Value then + begin + FAbortFlag.Value := True; + repeat + IndySleep(5); + //we need to wait until the SendCmd routine catches the Abort + //request so you don't get an AV in a worker thread. + until not FAbortFlag.Value; + end; +end; + +{ TIdFSPPacket } + +constructor TIdFSPPacket.Create; +begin + inherited Create; + FCmd := 0; + FFilePosition := 0; + FDataLen := 0; + SetLength(FData, 0); + SetLength(FExtraData, 0); + FSequence := 0; + FKey := 0; +end; + +function TIdFSPPacket.WritePacket : TIdBytes; +var + LExtraDataLen, LW : Word; + LC, LSum : UInt32; + i : Integer; +//ported from: +//http://cvs.sourceforge.net/viewcvs.py/fsp/javalib/FSPpacket.java?rev=1.6&view=markup +begin + LExtraDataLen := Length(FExtraData); + SetLength(Result, HSIZE + FDataLen + LExtraDataLen); + + //cmd + Result[0] := Cmd; + //checksum + Result[1] := 0; //this will be the checksum value + //key + LW := GStack.HostToNetwork(FKey); + CopyTIdUInt16(LW, Result, 2); + // sequence + LW := GStack.HostToNetwork(FSequence); + CopyTIdUInt16(LW, Result, 4); + // data length + LW := GStack.HostToNetwork(FDataLen); + CopyTIdUInt16(LW, Result, 6); + // position + LC := GStack.HostToNetwork(FFilePosition); + CopyTIdUInt32(LC, Result, 8); + //end of header section + + //data section + if FDataLen > 0 then begin + CopyTIdBytes(FData, 0, Result, HSIZE, FDataLen); + end; + + //extra data section + if LExtraDataLen > 0 then begin + CopyTIdBytes(FExtraData, 0, Result, HSIZE+FDataLen, LExtraDataLen); + end; + + //checksum + LSum := Length(Result); + for i := Length(Result)-1 downto 0 do begin + Inc(LSum, Result[i]); + end; + Result[1] := Byte(LSum+(LSum shr 8)); +end; + +procedure TIdFSPPacket.ReadPacket(const AData : TIdBytes; const ALen : UInt32); +var + LSum, LnSum, LcSum : UInt32; //UInt32 to prevent a range-check error + LW : Word; + LExtraDataLen : UInt32; +begin + FValid := False; + + if ALen < HSIZE then begin + Exit; + end; + + //check data length + FDataLen := BytesToUInt16(AData, 6); + FDataLen := GStack.NetworkToHost(FDataLen); + if FDataLen > ALen then begin + Exit; + end; + + //validate checksum + LSum := AData[1]; //checksum + LnSum := ALen; + for LW := ALen-1 downto 0 do begin + if LW <> 1 then begin // skip the checksum byte + Inc(LnSum, AData[LW]); + end; + end; + lcSum := Byte(LnSum + (LnSum shr 8)); + + if LcSum <> LSum then begin + Exit; + end; + + //command + FCmd := AData[0]; + //key + FKey := BytesToUInt16(AData, 2); + FKey := GStack.NetworkToHost(FKey); + // sequence + FSequence := BytesToUInt16(AData, 4); + FSequence := GStack.NetworkToHost(FSequence); + //file position + FFilePosition := BytesToUInt32(AData, 8); + FFilePosition := GStack.NetworkToHost(FFilePosition); + + //extract data + if FDataLen > 0 then begin + SetLength(FData, FDataLen); + CopyTIdBytes(AData, HSIZE, FData, 0, FDataLen); + end else begin + SetLength(FData, 0); + end; + + //extract extra data + LExtraDataLen := ALen - (HSIZE+FDataLen); + if LExtraDataLen > 0 then begin + SetLength(FExtraData, LExtraDataLen); + CopyTIdBytes(AData, HSIZE+FDataLen, FExtraData, 0, LExtraDataLen); + end else begin + SetLength(FExtraData, 0); + end; + + FValid := True; +end; + +{ TIdFSPListItems } + +function TIdFSPListItems.Add: TIdFSPListItem; +begin + Result := TIdFSPListItem(inherited Add); +end; + +constructor TIdFSPListItems.Create; +begin + inherited Create(TIdFSPListItem); +end; + +function TIdFSPListItems.GetItems(AIndex: Integer): TIdFSPListItem; +begin + Result := TIdFSPListItem(inherited Items[AIndex]); +end; + +function TIdFSPListItems.IndexOf(AItem: TIdFSPListItem): Integer; +Var + i: Integer; +begin + for i := 0 to Count - 1 do begin + if AItem = Items[i] then begin + Result := i; + Exit; + end; + end; + Result := -1; +end; + +function TIdFSPListItems.ParseEntries(const AData: TIdBytes; const ADataLen : UInt32) : Boolean; +var + i : UInt32; + LI : TIdFSPListItem; + LSkip : Boolean; +begin + Result := False; + i := 0; + repeat + if i >= (ADataLen-9) then begin + Exit; + end; + LI := nil; + LSkip := False; + case AData[i+8] of + RDTYPE_END: + begin + Result := True; + Exit; + end; + RDTYPE_FILE: + begin + LI := Add; + LI.ItemType := ditFile; + end; + RDTYPE_DIR: + begin + LI := Add; + LI.ItemType := ditDirectory; + end; + RDTYPE_SKIP: + begin + LSkip := True; + end + else begin + Exit; + end; + end; + if LSkip then begin + Inc(i, 8); + end else begin + ParseStatInfo(AData, LI, i); + LI.FileName := ParseASCIIZPos(AData, ADataLen, i); + end; + repeat + Inc(i); + until (i and $03) = 0; + until False; +end; + +procedure TIdFSPListItems.SetItems(AIndex: Integer; const Value: TIdFSPListItem); +begin + inherited Items[AIndex] := Value; +end; + +end. diff --git a/indy/Protocols/IdFTP.pas b/indy/Protocols/IdFTP.pas new file mode 100644 index 0000000..fbb5842 --- /dev/null +++ b/indy/Protocols/IdFTP.pas @@ -0,0 +1,4346 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.126 4/28/2005 BTaylor + Changed .Size to use Int64 + + Rev 1.125 4/15/2005 9:10:10 AM JPMugaas + Changed the default timeout in TIdFTP to one minute and made a comment about + this. + + Some firewalls don't handle control connections properly during long data + transfers. They will timeout the control connection because it's idle and + making it worse is that they will chop off a connection instead of closing it + causing TIdFTP to wait forever for nothing. + + Rev 1.124 3/20/2005 10:42:44 PM JPMugaas + Marked TIdFTP.Quit as deprecated. We need to keep it only for compatibility. + + Rev 1.123 3/20/2005 2:44:08 PM JPMugaas + Should now send quit. Verified here. + + Rev 1.122 3/12/2005 6:57:12 PM JPMugaas + Attempt to add ACCT support for firewalls. I also used some logic from some + WS-FTP Pro about ACCT to be more consistant with those Firescripts. + + Rev 1.121 3/10/2005 2:41:12 PM JPMugaas + Removed the UseTelnetAbort property. It turns out that sending the sequence + is causing problems on a few servers. I have made a comment about this in + the source-code so someone later on will know why I decided not to send + those. + + Rev 1.120 3/9/2005 10:05:54 PM JPMugaas + Minor changes for Indy conventions. + + Rev 1.119 3/9/2005 9:15:46 PM JPMugaas + Changes submitted by Craig Peterson, Scooter Software He noted this: + + "We had a user who's FTP server prompted for account info after a + regular login, so I had to add an explicit Account string property and + an OnNeedAccount event that we could use for a prompt." This does break any + code using TIdFTP.Account. + + TODO: See about integrating Account Info into the proxy login sequences. + + Rev 1.118 3/9/2005 10:40:16 AM JPMugaas + Made comment explaining why I had made a workaround in a procedure. + + Rev 1.117 3/9/2005 10:28:32 AM JPMugaas + Fix for Abort problem when uploading. A workaround I made for WS-FTP Pro + Server was not done correctly. + + Rev 1.116 3/9/2005 1:21:38 AM JPMugaas + Made refinement to Abort and the data transfers to follow what Kudzu had + originally done in Indy 8. I also fixed a problem with ABOR at + ftp.ipswitch.com and I fixed a regression at ftp.marist.edu that occured when + getting a directory. + + Rev 1.115 3/8/2005 12:14:50 PM JPMugaas + Renamed UseOOBAbort to UseTelnetAbort because that's more accurate. We still + don't support Out of Band Data (hopefully, we'll never have to do that). + + Rev 1.114 3/7/2005 10:40:10 PM JPMugaas + Improvements: + + 1) Removed some duplicate code. + 2) ABOR should now be properly handled outside of a data operation. + 3) I added a UseOOBAbort read-write public property for controlling how the + ABOR command is sent. If true, the Telnet sequences are sent or if false, + the ABOR without sequences is sent. This is set to false by default because + one FTP client (SmartFTP recently removed the Telnet sequences from their + program). + + This code is expiriemental. + + Rev 1.113 3/7/2005 5:46:34 PM JPMugaas + Reworked FTP Abort code to make it more threadsafe and make abort work. This + is PRELIMINARY. + + Rev 1.112 3/5/2005 3:33:56 PM JPMugaas + Fix for some compiler warnings having to do with TStream.Read being platform + specific. This was fixed by changing the Compressor API to use TIdStreamVCL + instead of TStream. I also made appropriate adjustments to other units for + this. + + Rev 1.111 2/24/2005 6:46:36 AM JPMugaas + Clarrified remarks I made and added a few more comments about syntax in + particular cases in the set modified file date procedures. + + That's really been a ball....NOT!!!! + + Rev 1.110 2/24/2005 6:25:08 AM JPMugaas + Attempt to fix problem setting Date with Titan FTP Server. I had made an + incorrect assumption about MDTM on that system. It uses Syntax 3 (see my + earlier note above the File Date Set problem. + + Rev 1.109 2/23/2005 6:32:54 PM JPMugaas + Made note about MDTM syntax inconsistancy. There's a discussion about it. + + Rev 1.108 2/12/2005 8:08:04 AM JPMugaas + Attempt to fix MDTM bug where msec was being sent. + + Rev 1.107 1/12/2005 11:26:44 AM JPMugaas + Memory Leak fix when processing MLSD output and some minor tweeks Remy had + E-Mailed me last night. + + Rev 1.106 11/18/2004 2:39:32 PM JPMugaas + Support for another FTP Proxy type. + + Rev 1.105 11/18/2004 12:18:50 AM JPMugaas + Fixed compile error. + + Rev 1.104 11/17/2004 3:59:22 PM JPMugaas + Fixed a TODO item about FTP Proxy support with a "Transparent" proxy. I + think you connect to the regular host and the firewall will intercept its + login information. + + Rev 1.103 11/16/2004 7:31:52 AM JPMugaas + Made a comment noting that UserSite is the same as USER after login for later + reference. + + Rev 1.102 11/5/2004 1:54:42 AM JPMugaas + Minor adjustment - should not detect TitanFTPD better (tested at: + ftp.southrivertech.com). + + If MLSD is being used, SITE ZONE will not be issued. It's not needed because + the MLSD spec indicates the time is based on GMT. + + Rev 1.101 10/27/2004 12:58:08 AM JPMugaas + Improvement from Tobias Giesen http://www.superflexible.com + His notation is below: + + "here's a fix for TIdFTP.IndexOfFeatLine. It does not work the + way it is used in TIdFTP.SetModTime, because it only + compares the first word of the FeatLine." + + Rev 1.100 10/26/2004 9:19:10 PM JPMugaas + Fixed references. + + Rev 1.99 9/16/2004 3:24:04 AM JPMugaas + TIdFTP now compresses to the IOHandler and decompresses from the IOHandler. + + Noted some that the ZLib code is based was taken from ZLibEx. + + Rev 1.98 9/13/2004 12:15:42 AM JPMugaas + Now should be able to handle some values better as suggested by Michael J. + Leave. + + Rev 1.97 9/11/2004 10:58:06 AM JPMugaas + FTP now decompresses output directly to the IOHandler. + + Rev 1.96 9/10/2004 7:37:42 PM JPMugaas + Fixed a bug. We needed to set Passthrough instead of calling StartSSL. This + was causing a SSL problem with upload. + + Rev 1.95 8/2/04 5:56:16 PM RLebeau + Tweaks to TIdFTP.InitDataChannel() + + Rev 1.94 7/30/2004 1:55:04 AM DSiders + Corrected DoOnRetrievedDir naming. + + Rev 1.93 7/30/2004 12:36:32 AM DSiders + Corrected spelling in OnRetrievedDir, DoOnRetrievedDir declarations. + + Rev 1.92 7/29/2004 2:15:28 AM JPMugaas + New property for controlling what AUTH command is sent. Fixed some minor + issues with FTP properties. Some were not set to defaults causing + unpredictable results -- OOPS!!! + + Rev 1.91 7/29/2004 12:04:40 AM JPMugaas + New events for Get and Put as suggested by Don Sides and to complement an + event done by APR. + + Rev 1.90 7/28/2004 10:16:14 AM JPMugaas + New events for determining when a listing is finished and when the dir + parsing begins and ends. Dir parsing is done sometimes when DirectoryListing + is referenced. + + Rev 1.89 7/27/2004 2:03:54 AM JPMugaas + New property: + + ExternalIP - used to specify an IP address for the PORT and EPRT commands. + This should be blank unless you are behind a NAT and you need to use PORT + transfers with SSL. You would set ExternalIP to the NAT's IP address on the + Internet. + + The idea is this: + + 1) You set up your NAT to forward a range ports ports to your computer behind + the NAT. + 2) You specify that a port range with the DataPortMin and DataPortMin + properties. + 3) You set ExternalIP to the NAT's Internet IP address. + + I have verified this with Indy and WS FTP Pro behind a NAT router. + + Rev 1.88 7/23/04 7:09:50 PM RLebeau + Bug fix for TFileStream access rights in Get() + + Rev 1.87 7/18/2004 3:00:12 PM DSiders + Added localization comments. + + Rev 1.86 7/16/2004 4:28:40 AM JPMugaas + CCC Support in TIdFTP to complement that capability in TIdFTPServer. + + Rev 1.85 7/13/04 6:48:14 PM RLebeau + Added support for new DataPort and DataPortMin/Max properties + + Rev 1.84 7/6/2004 4:51:46 PM DSiders + Corrected spelling of Challenge in properties, methods, types. + + Rev 1.83 7/3/2004 3:15:50 AM JPMugaas + Checked in so everyone else can work on stuff while I'm away. + + Rev 1.82 6/27/2004 1:45:38 AM JPMugaas + Can now optionally support LastAccessTime like Smartftp's FTP Server could. + I also made the MLST listing object and parser support this as well. + + Rev 1.81 6/20/2004 8:31:58 PM JPMugaas + New events for reporting greeting and after login banners during the login + sequence. + + Rev 1.80 6/20/2004 6:56:42 PM JPMugaas + Start oin attempt to support FXP with Deflate compression. More work will + need to be done. + + Rev 1.79 6/17/2004 3:42:32 PM JPMugaas + Adjusted code for removal of dmBlock and dmCompressed. Made TransferMode a + property. Note that the Set method is odd because I am trying to keep + compatibility with older Indy versions. + + Rev 1.78 6/14/2004 6:19:02 PM JPMugaas + This now refers to TIdStreamVCL when downloading isntead of directly to a + memory stream when compressing data. + + Rev 1.77 6/14/2004 8:34:52 AM JPMugaas + Fix for AV on Put with Passive := True. + + Rev 1.76 6/11/2004 9:34:12 AM DSiders + Added "Do not Localize" comments. + + Rev 1.75 2004.05.20 11:37:16 AM czhower + IdStreamVCL + + Rev 1.74 5/6/2004 6:54:26 PM JPMugaas + FTP Port transfers with TransparentProxies is enabled. This only works if + the TransparentProxy supports a "bind" request. + + Rev 1.73 5/4/2004 11:16:28 AM JPMugaas + TransferTimeout property added and enabled (Bug 96). + + Rev 1.72 5/4/2004 11:07:12 AM JPMugaas + Timeouts should now be reenabled in TIdFTP. + + Rev 1.71 4/19/2004 5:05:02 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.70 2004.04.16 9:31:42 PM czhower + Remove unnecessary duplicate string parsing and replaced with .assign. + + Rev 1.69 2004.04.15 7:09:04 PM czhower + .NET overloads + + Rev 1.68 4/15/2004 9:46:48 AM JPMugaas + List no longer requires a TStrings. It turns out that it was an optional + parameter. + + Rev 1.67 2004.04.15 2:03:28 PM czhower + Removed login param from connect and made it a prop like POP3. + + Rev 1.66 3/3/2004 5:57:40 AM JPMugaas + Some IFDEF excluses were removed because the functionality is now in DotNET. + + Rev 1.65 2004.03.03 11:54:26 AM czhower + IdStream change + + Rev 1.64 2/20/2004 1:01:06 PM JPMugaas + Preliminary FTP PRET command support for using PASV with a distributed FTP + server (Distributed PASV - + http://drftpd.org/wiki/wiki.phtml?title=Distributed_PASV). + + Rev 1.63 2/17/2004 12:25:52 PM JPMugaas + The client now supports MODE Z (deflate) uploads and downloads as specified + by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + + Rev 1.62 2004.02.03 5:45:10 PM czhower + Name changes + + Rev 1.61 2004.02.03 2:12:06 PM czhower + $I path change + + Rev 1.60 1/27/2004 10:17:10 PM JPMugaas + Fix from Steve Loft for a server that sends something like this: + "227 Passive mode OK (195,92,195,164,4,99 )" + + Rev 1.59 1/27/2004 3:59:28 PM SPerry + StringStream ->IdStringStream + + Rev 1.58 24/01/2004 19:13:58 CCostelloe + Cleaned up warnings + + Rev 1.57 1/21/2004 2:27:50 PM JPMugaas + Bullete Proof FTPD and Titan FTP support SITE ZONE. Saw this in a command + database in StaffFTP. + InitComponent. + + Rev 1.56 1/19/2004 9:05:38 PM JPMugaas + Fixes to FTP Set Date functionality. + Introduced properties for Time Zone information from the server. The way it + works is this, if TIdFTP detects you are using "Serv-U" or SITE ZONE is + listed in the FEAT reply, Indy obtains the time zone information with the + SITE ZONE command and makes the appropriate calculation. Indy then uses this + information to calculate a timestamp to send to the server with the MDTM + command. You can also use the Time Zone information yourself to convert the + FTP directory listing item timestamps into GMT and than convert that to your + local time. + FTP Voyager uses SITE ZONE as I've described. + + Rev 1.55 1/19/2004 4:39:08 AM JPMugaas + You can now set the time for a file on the server. Note that these methods + try to treat the time as relative to GMT. + + Rev 1.54 1/17/2004 9:09:30 PM JPMugaas + Should now compile. + + Rev 1.53 1/17/2004 7:48:02 PM JPMugaas + FXP site to site transfer code was redone for improvements with FXP with TLS. + It actually works and I verified with RaidenFTPD + (http://www.raidenftpd.com/) and the Indy FTP server components. I also + lowered the requirements for TLS FXP transfers. The requirements now are: + 1) Only server (either the recipient or the sendor) has to support SSCN + + or + + 2) The server receiving a PASV must support CPSV and the transfer is done + with IPv4. + + Rev 1.52 1/9/2004 2:51:26 PM JPMugaas + Started IPv6 support. + + Rev 1.51 11/27/2003 4:55:28 AM JPMugaas + Made STOU functionality separate from PUT functionality. Put now requires a + destination filename except where a source-file name is given. In that case, + the default is the filename from the source string. + + Rev 1.50 10/26/2003 04:28:50 PM JPMugaas + Reworked Status. + + The old one was problematic because it assumed that STAT was a request to + send a directory listing through the control channel. This assumption is not + correct. It provides a way to get a freeform status report from a server. + With a Path parameter, it should work like a LIST command except that the + control connection is used. We don't support that feature and you should use + our LIst method to get the directory listing anyway, IMAO. + + Rev 1.49 10/26/2003 9:17:46 PM BGooijen + Compiles in DotNet, and partially works there + + Rev 1.48 10/24/2003 12:43:48 PM JPMugaas + Should work again. + + Rev 1.47 2003.10.24 10:43:04 AM czhower + TIdSTream to dos + + Rev 1.46 10/20/2003 03:06:10 PM JPMugaas + SHould now work. + + Rev 1.45 10/20/2003 01:00:38 PM JPMugaas + EIdException no longer raised. Some things were being gutted needlessly. + + Rev 1.44 10/19/2003 12:58:20 PM DSiders + Added localization comments. + + Rev 1.43 2003.10.14 9:56:50 PM czhower + Compile todos + + Rev 1.42 2003.10.12 3:50:40 PM czhower + Compile todos + + Rev 1.41 10/10/2003 11:32:26 PM SPerry + - + + Rev 1.40 10/9/2003 10:17:02 AM JPMugaas + Added overload for GetLoginPassword for providing a challanage string which + doesn't have to the last command reply. + Added CLNT support. + + Rev 1.39 10/7/2003 05:46:20 AM JPMugaas + SSCN Support added. + + Rev 1.38 10/6/2003 08:56:44 PM JPMugaas + Reworked the FTP list parsing framework so that the user can obtain the list + of capabilities from a parser class with TIdFTP. This should permit the user + to present a directory listing differently for each parser (some FTP list + parsers do have different capabilities). + + Rev 1.37 10/1/2003 12:51:18 AM JPMugaas + SSL with active (PORT) transfers now should work again. + + Rev 1.36 9/30/2003 09:50:38 PM JPMugaas + FTP with TLS should work better. It turned out that we were negotiating it + several times causing a hang. I also made sure that we send PBSZ 0 and PROT + P for both implicit and explicit TLS. Data ports should work in PASV again. + + Rev 1.35 9/28/2003 11:41:06 PM JPMugaas + Reworked Eldos's proposed FTP fix as suggested by Henrick Hellstrm by moving + all of the IOHandler creation code to InitDataChannel. This should reduce + the likelihood of error. + + Rev 1.33 9/18/2003 11:22:40 AM JPMugaas + Removed a temporary workaround for an OnWork bug that was in the Indy Core. + That bug was fixed so there's no sense in keeping a workaround here. + + Rev 1.32 9/12/2003 08:05:30 PM JPMugaas + A temporary fix for OnWork events not firing. The bug is that OnWork events + aren't used in IOHandler where ReadStream really is located. + + Rev 1.31 9/8/2003 02:33:00 AM JPMugaas + OnCustomFTPProxy added to allow Indy to support custom FTP proxies. When + using this event, you are responsible for programming the FTP Proxy and FTP + Server login sequence. + GetLoginPassword method function for returning the password used when logging + into a FTP server which handles OTP calculation. This way, custom firewall + support can handle One-Time-Password system transparently. You do have to + send the User ID before calling this function because the OTP challenge is + part of the reply. + + Rev 1.30 6/10/2003 11:10:00 PM JPMugaas + Made comments about our loop that tries several AUTH command variations. + Some servers may only accept AUTH SSL while other servers only accept AUTH + TLS. + + Rev 1.29 5/26/2003 12:21:54 PM JPMugaas + + Rev 1.28 5/25/2003 03:54:20 AM JPMugaas + + Rev 1.27 5/19/2003 08:11:32 PM JPMugaas + Now should compile properly with new code in Core. + + Rev 1.26 5/8/2003 11:27:42 AM JPMugaas + Moved feature negoation properties down to the ExplicitTLSClient level as + feature negotiation goes hand in hand with explicit TLS support. + + Rev 1.25 4/5/2003 02:06:34 PM JPMugaas + TLS handshake itself can now be handled. + + Rev 1.24 4/4/2003 8:01:32 PM BGooijen + now creates iohandler for dataconnection + + Rev 1.23 3/31/2003 08:40:18 AM JPMugaas + Fixed problem with QUIT command. + + Rev 1.22 3/27/2003 3:41:28 PM BGooijen + Changed because some properties are moved to IOHandler + + Rev 1.21 3/27/2003 05:46:24 AM JPMugaas + Updated framework with an event if the TLS negotiation command fails. + Cleaned up some duplicate code in the clients. + + Rev 1.20 3/26/2003 04:19:20 PM JPMugaas + Cleaned-up some code and illiminated some duplicate things. + + Rev 1.19 3/24/2003 04:56:10 AM JPMugaas + A typecast was incorrect and could cause a potential source of instability if + a TIdIOHandlerStack was not used. + + Rev 1.18 3/16/2003 06:09:58 PM JPMugaas + Fixed port setting bug. + + Rev 1.17 3/16/2003 02:40:16 PM JPMugaas + FTP client with new design. + + Rev 1.16 3/16/2003 1:02:44 AM BGooijen + Added 2 events to give the user more control to the dataconnection, moved + SendTransferType, enabled ssl + + Rev 1.15 3/13/2003 09:48:58 AM JPMugaas + Now uses an abstract SSL base class instead of OpenSSL so 3rd-party vendors + can plug-in their products. + + Rev 1.14 3/7/2003 11:51:52 AM JPMugaas + Fixed a writeln bug and an IOError issue. + + Rev 1.13 3/3/2003 07:06:26 PM JPMugaas + FFreeIOHandlerOnDisconnect to FreeIOHandlerOnDisconnect at Bas's instruction + + Rev 1.12 2/21/2003 06:54:36 PM JPMugaas + The FTP list processing has been restructured so that Directory output is not + done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so + that the code is more scalable. + + Rev 1.11 2/17/2003 04:45:36 PM JPMugaas + Now temporarily change the transfer mode to ASCII when requesting a DIR. + TOPS20 does not like transfering dirs in binary mode and it might be a good + idea to do it anyway. + + Rev 1.10 2/16/2003 03:22:20 PM JPMugaas + Removed the Data Connection assurance stuff. We figure things out from the + draft specificaiton, the only servers we found would not send any data after + the new commands were sent, and there were only 2 server types that supported + it anyway. + + Rev 1.9 2/16/2003 10:51:08 AM JPMugaas + Attempt to implement: + + http://www.ietf.org/internet-drafts/draft-ietf-ftpext-data-connection-assuranc + e-00.txt + + Currently commented out because it does not work. + + Rev 1.8 2/14/2003 11:40:16 AM JPMugaas + Fixed compile error. + + Rev 1.7 2/14/2003 10:38:32 AM JPMugaas + Removed a problematic override for GetInternelResponse. It was messing up + processing of the FEAT. + + Rev 1.6 12-16-2002 20:48:10 BGooijen + now uses TIdIOHandler.ConstructIOHandler to construct iohandlers + IPv6 works again + Independant of TIdIOHandlerStack again + + Rev 1.5 12-15-2002 23:27:26 BGooijen + now compiles on Indy 10, but some things like IPVersion still need to be + changed + + Rev 1.4 12/15/2002 04:07:02 PM JPMugaas + Started port to Indy 10. Still can not complete it though. + + Rev 1.3 12/6/2002 05:29:38 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.2 12/1/2002 04:18:02 PM JPMugaas + Moved all dir parsing code to one place. Reworked to use more than one line + for determining dir format type along with flfNextLine dir format type. + + Rev 1.1 11/14/2002 04:02:58 PM JPMugaas + Removed cludgy code that was a workaround for the RFC Reply limitation. That + is no longer limited. + + Rev 1.0 11/14/2002 02:20:00 PM JPMugaas + +2002-10-25 - J. Peter Mugaas + - added XCRC support - specified by "GlobalSCAPE Secure FTP Server Users Guide" + which is available at http://www.globalscape.com + and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm + - added COMB support - specified by "GlobalSCAPE Secure FTP Server Users Guide" + which is available at http://www.globalscape.com + and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm + +2002-10-24 - J. Peter Mugaas + - now supports RFC 2640 - FTP Internalization + +2002-09-18 + _ added AFromBeginning parameter to InternalPut to correctly honor the AAppend parameter of Put + +2002-09-05 - J. Peter Mugaas + - now complies with RFC 2389 - Feature negotiation mechanism for the File Transfer Protocol + - now complies with RFC 2428 - FTP Extensions for IPv6 and NATs + +2002-08-27 - Andrew P.Rybin + - proxy support fix (non-standard ftp port's) + +2002-01-xx - Andrew P.Rybin + - Proxy support, OnAfterGet (ex:decrypt, set srv timestamp) + - J.Peter Mugaas: not readonly ProxySettings + + A Neillans - 10/17/2001 + Merged changes submitted by Andrew P.Rybin + Correct command case problems - some servers expect commands in Uppercase only. + + SP - 06/08/2001 + Added a few more functions + + Doychin - 02/18/2001 + OnAfterLogin event handler and Login method + OnAfterLogin is executed after successfull login but before setting up the + connection properties. This event can be used to provide FTP proxy support + from the user application. Look at the FTP demo program for more information + on how to provide such support. + + Doychin - 02/17/2001 + New onFTPStatus event + New Quote method for executing commands not implemented by the compoent + +-CleanDir contributed by Amedeo Lanza +} + +unit IdFTP; + +{ + TODO: Change the FTP demo to demonstrate the use of the new events and add proxy support +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, IdGlobal, IdCustomTransparentProxy, IdExceptionCore, + IdExplicitTLSClientServerBase, IdFTPCommon, IdFTPList, IdFTPListParseBase, + IdException, IdIOHandler, IdIOHandlerSocket, IdReplyFTP, IdBaseComponent, + IdReplyRFC, IdReply, IdSocketHandle, IdTCPConnection, IdTCPClient, + IdThreadSafe, IdZLibCompressorBase; + +type + //APR 011216: + TIdFtpProxyType = ( + fpcmNone,//Connect method: + fpcmUserSite, //Send command USER user@hostname - USER after login (see: http://isservices.tcd.ie/internet/command_config.php) + fpcmSite, //Send command SITE (with logon) + fpcmOpen, //Send command OPEN + fpcmUserPass,//USER user@firewalluser@hostname / PASS pass@firewallpass + fpcmTransparent, //First use the USER and PASS command with the firewall username and password, and then with the target host username and password. + fpcmUserHostFireWallID, //USER hostuserId@hostname firewallUsername + fpcmNovellBorder, //Novell BorderManager Proxy + fpcmHttpProxyWithFtp, //HTTP Proxy with FTP support. Will be supported in Indy 10 + fpcmCustomProxy // use OnCustomFTPProxy to customize the proxy login + ); //TIdFtpProxyType + + //This has to be in the same order as TLS_AUTH_NAMES + TAuthCmd = (tAuto, tAuthTLS, tAuthSSL, tAuthTLSC, tAuthTLSP); + +const + Id_TIdFTP_TransferType = {ftBinary} ftASCII; // RLebeau 1/22/08: per RFC 959 + Id_TIdFTP_Passive = False; + Id_TIdFTP_UseNATFastTrack = False; + Id_TIdFTP_HostPortDelimiter = ':'; + Id_TIdFTP_DataConAssurance = False; + Id_TIdFTP_DataPortProtection = ftpdpsClear; + // + DEF_Id_TIdFTP_Implicit = False; + DEF_Id_FTP_UseExtendedDataPort = False; + DEF_Id_TIdFTP_UseExtendedData = False; + DEF_Id_TIdFTP_UseMIS = True; + DEF_Id_FTP_UseCCC = False; + DEF_Id_FTP_AUTH_CMD = tAuto; + DEF_Id_FTP_ListenTimeout = 10000; // ten seconds + { +Soem firewalls don't handle control connections properly during long data transfers. +They will timeout the control connection because it's idle and making it worse is that they +will chop off a connection instead of closing it causing TIdFTP to wait forever for nothing. + + } + DEF_Id_FTP_READTIMEOUT = 60000; //one minute + DEF_Id_FTP_UseHOST = True; + DEF_Id_FTP_PassiveUseControlHost = False; + DEF_Id_FTP_AutoIssueFEAT = True; + DEF_Id_FTP_AutoLogin = True; + +type + //Added by SP + TIdCreateFTPList = procedure(ASender: TObject; var VFTPList: TIdFTPListItems) of object; + //TIdCheckListFormat = procedure(ASender: TObject; const ALine: String; var VListFormat: TIdFTPListFormat) of object; + TOnAfterClientLogin = TNotifyEvent; + TIdFtpAfterGet = procedure(ASender: TObject; AStream: TStream) of object; //APR + TIdOnDataChannelCreate = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object; + TIdOnDataChannelDestroy = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object; + TIdNeedAccountEvent = procedure(ASender: TObject; var VAcct: string) of object; + + TIdFTPBannerEvent = procedure (ASender: TObject; const AMsg : String) of object; + + TIdFTPClientIdentifier = class (TPersistent) + protected + FClientName : String; + FClientVersion : String; + FPlatformDescription : String; + procedure SetClientName(const AValue: String); + procedure SetClientVersion(const AValue: String); + procedure SetPlatformDescription(const AValue: String); + function GetClntOutput: String; + public + procedure Assign(Source: TPersistent); override; + property ClntOutput : String read GetClntOutput; + published + property ClientName : String read FClientName write SetClientName; + property ClientVersion : String read FClientVersion write SetClientVersion; + property PlatformDescription : String read FPlatformDescription write SetPlatformDescription; + end; + + TIdFtpProxySettings = class (TPersistent) + protected + FHost, FUserName, FPassword: String; + FProxyType: TIdFtpProxyType; + FPort: TIdPort; + public + procedure Assign(Source: TPersistent); override; + published + property ProxyType: TIdFtpProxyType read FProxyType write FProxyType; + property Host: String read FHost write FHost; + property UserName: String read FUserName write FUserName; + property Password: String read FPassword write FPassword; + property Port: TIdPort read FPort write FPort; + end; + + TIdFTPTZInfo = class(TPersistent) + protected + FGMTOffset : TDateTime; + FGMTOffsetAvailable : Boolean; + public + procedure Assign(Source: TPersistent); override; + published + property GMTOffset : TDateTime read FGMTOffset write FGMTOffset; + property GMTOffsetAvailable : Boolean read FGMTOffsetAvailable write FGMTOffsetAvailable; + end; + + TIdFTPKeepAlive = class(TPersistent) + protected + FUseKeepAlive: Boolean; + FIdleTimeMS: Integer; + FIntervalMS: Integer; + public + procedure Assign(Source: TPersistent); override; + published + property UseKeepAlive: Boolean read FUseKeepAlive write FUseKeepAlive; + property IdleTimeMS: Integer read FIdleTimeMS write FIdleTimeMS; + property IntervalMS: Integer read FIntervalMS write FIntervalMS; + end; + + TIdFTP = class(TIdExplicitTLSClient) + protected + FAutoLogin: Boolean; + FAutoIssueFEAT : Boolean; + FCurrentTransferMode : TIdFTPTransferMode; + FClientInfo : TIdFTPClientIdentifier; + + FDataSettingsSent: Boolean; // only send SSL data settings once per connection + FUsingSFTP : Boolean; //enable SFTP internel flag + FUsingCCC : Boolean; //are we using FTP with SSL on a clear control channel? + FUseHOST: Boolean; + FServerHOST: String; + FCanUseMLS : Boolean; //can we use MLISx instead of LIST + FUsingExtDataPort : Boolean; //are NAT Extensions (RFC 2428 available) flag + FUsingNATFastTrack : Boolean;//are we using NAT fastrack feature + FCanResume: Boolean; + FListResult: TStrings; + FLoginMsg: TIdReplyFTP; + + FPassive: Boolean; + FPassiveUseControlHost: Boolean; + + FDataPortProtection : TIdFTPDataPortSecurity; + FAUTHCmd : TAuthCmd; + FDataPort: TIdPort; + FDataPortMin: TIdPort; + FDataPortMax: TIdPort; + FDefStringEncoding: IIdTextEncoding; + FExternalIP : String; + FResumeTested: Boolean; + FServerDesc: string; + FSystemDesc: string; + FTransferType: TIdFTPTransferType; + FTransferTimeout : Integer; + FListenTimeout : Integer; + FDataChannel: TIdTCPConnection; + FDirectoryListing: TIdFTPListItems; + FDirFormat : String; + FListParserClass : TIdFTPListParseClass; + FOnAfterClientLogin: TNotifyEvent; + FOnCreateFTPList: TIdCreateFTPList; + FOnBeforeGet: TNotifyEvent; + FOnBeforePut: TIdFtpAfterGet; + //in case someone needs to do something special with the data being uploaded + FOnAfterGet: TIdFtpAfterGet; //APR + FOnAfterPut: TNotifyEvent; //JPM at Don Sider's suggestion + FOnNeedAccount: TIdNeedAccountEvent; + FOnCustomFTPProxy : TNotifyEvent; + FOnDataChannelCreate: TIdOnDataChannelCreate; + FOnDataChannelDestroy: TIdOnDataChannelDestroy; + FProxySettings: TIdFtpProxySettings; + + FUseExtensionDataPort : Boolean; + FTryNATFastTrack : Boolean; + FUseMLIS : Boolean; + FLangsSupported : TStrings; + FUseCCC: Boolean; + //is the SSCN Client method on for this connection? + FSSCNOn : Boolean; + FIsCompressionSupported : Boolean; + + FOnBannerBeforeLogin : TIdFTPBannerEvent; + FOnBannerAfterLogin : TIdFTPBannerEvent; + FOnBannerWarning : TIdFTPBannerEvent; + + FTZInfo : TIdFTPTZInfo; + + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FCompressor : TIdZLibCompressorBase; + //ZLib settings + FZLibCompressionLevel : Integer; //7 + FZLibWindowBits : Integer; //-15 + FZLibMemLevel : Integer; //8 + FZLibStratagy : Integer; //0 - default + + //dir events for some GUI programs. + //The directory was Retrieved from the FTP server. + FOnRetrievedDir : TNotifyEvent; + //parsing is done only when DirectoryListing is referenced + FOnDirParseStart : TNotifyEvent; + FOnDirParseEnd : TNotifyEvent; + + //we probably need an Abort flag so we know when an abort is sent. + //It turns out that one server will send a 550 or 451 error followed by an + //ABOR successfull + FAbortFlag : TIdThreadSafeBoolean; + + FAccount: string; + FNATKeepAlive: TIdFTPKeepAlive; + // + procedure DoOnDataChannelCreate; + procedure DoOnDataChannelDestroy; + + procedure DoOnRetrievedDir; + procedure DoOnDirParseStart; + procedure DoOnDirParseEnd; + + procedure FinalizeDataOperation; + procedure SetTZInfo(const Value: TIdFTPTZInfo); + function IsSiteZONESupported : Boolean; + function IndexOfFeatLine(const AFeatLine : String):Integer; + procedure ClearSSCN; + function SetSSCNToOn : Boolean; + procedure SendInternalPassive(const ACmd : String; var VIP: string; var VPort: TIdPort); + procedure SendCPassive(var VIP: string; var VPort: TIdPort); + function FindAuthCmd : String; + // + function GetReplyClass: TIdReplyClass; override; + // + procedure ParseFTPList; + procedure SetPassive(const AValue : Boolean); + procedure SetTryNATFastTrack(const AValue: Boolean); + procedure DoTryNATFastTrack; + procedure SetUseExtensionDataPort(const AValue: Boolean); + + procedure SetIPVersion(const AValue: TIdIPVersion); override; + procedure SetIOHandler(AValue: TIdIOHandler); override; + function GetSupportsTLS: Boolean; override; + + procedure ConstructDirListing; + procedure DoAfterLogin; + procedure DoFTPList; + procedure DoCustomFTPProxy; + procedure DoOnBannerAfterLogin(AText : TStrings); + procedure DoOnBannerBeforeLogin(AText : TStrings); + procedure DoOnBannerWarning(AText : TStrings); + procedure SendPBSZ; //protection buffer size + procedure SendPROT; //data port protection + procedure SendDataSettings; //this is for the extensions only; +// procedure DoCheckListFormat(const ALine: String); + function GetDirectoryListing: TIdFTPListItems; +// function GetOnParseCustomListFormat: TIdOnParseCustomListFormat; + procedure InitDataChannel; + //PRET is to help distributed FTP systems by letting them know what you will do + //before issuing a PASV. See: http://drftpd.mog.se/wiki/wiki.phtml?title=Distributed_PASV#PRE_Transfer_Command_for_Distributed_PASV_Transfers + //for a discussion. + procedure SendPret(const ACommand : String); + procedure InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false); + procedure InternalPut(const ACommand: string; ASource: TStream; AFromBeginning: Boolean = True; AResume: Boolean = False); +// procedure SetOnParseCustomListFormat(const AValue: TIdOnParseCustomListFormat); + procedure SendPassive(var VIP: string; var VPort: TIdPort); + procedure SendPort(AHandle: TIdSocketHandle); overload; + procedure SendPort(const AIP : String; const APort : TIdPort); overload; + procedure ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort); + //These two are for RFC 2428.txt + procedure SendEPort(AHandle: TIdSocketHandle); overload; + procedure SendEPort(const AIP : String; const APort : TIdPort; const AIPVersion : TIdIPVersion); overload; + procedure SendEPassive(var VIP: string; var VPort: TIdPort); + function SendHost: Int16; + procedure SetProxySettings(const Value: TIdFtpProxySettings); + procedure SetClientInfo(const AValue: TIdFTPClientIdentifier); + procedure SetCompressor(AValue: TIdZLibCompressorBase); + procedure SendTransferType(AValue: TIdFTPTransferType); + procedure SetTransferType(AValue: TIdFTPTransferType); + procedure DoBeforeGet; virtual; + procedure DoBeforePut(AStream: TStream); virtual; + procedure DoAfterGet(AStream: TStream); virtual; //APR + procedure DoAfterPut; virtual; + class procedure FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean); + class procedure FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String); + class function InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean) : Boolean; + class function InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean; + class function ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean): Boolean; + procedure InitComponent; override; + procedure SetUseTLS(AValue : TIdUseTLS); override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure SetDataPortProtection(AValue : TIdFTPDataPortSecurity); + procedure SetAUTHCmd(const AValue : TAuthCmd); + procedure SetDefStringEncoding(AValue: IIdTextEncoding); + procedure SetUseCCC(const AValue: Boolean); + procedure SetNATKeepAlive(AValue: TIdFTPKeepAlive); + procedure IssueFEAT; + //specific server detection + function IsOldServU: Boolean; + function IsBPFTP : Boolean; + function IsTitan : Boolean; + function IsWSFTP : Boolean; + function IsIIS: Boolean; + function CheckAccount: Boolean; + function IsAccountNeeded : Boolean; + function GetSupportsVerification : Boolean; + public + procedure GetInternalResponse(AEncoding: IIdTextEncoding = nil); override; + function CheckResponse(const AResponse: Int16; const AAllowedResponses: array of Int16): Int16; override; + + function IsExtSupported(const ACmd : String):Boolean; + procedure ExtractFeatFacts(const ACmd : String; AResults : TStrings); + //this function transparantly handles OTP based on the Last command response + //so it needs to be called only after the USER command or equivilent. + + function GetLoginPassword : String; overload; + function GetLoginPassword(const APrompt : String) : String; overload; + procedure Abort; virtual; + + procedure Allocate(AAllocateBytes: Integer); + procedure ChangeDir(const ADirName: string); + procedure ChangeDirUp; + procedure Connect; override; + destructor Destroy; override; + procedure Delete(const AFilename: string); + procedure FileStructure(AStructure: TIdFTPDataStructure); + procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = false); overload; + procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: boolean = false; AResume: Boolean = false); overload; + procedure Help(AHelpContents: TStrings; ACommand: String = ''); + procedure KillDataChannel; virtual; + //.NET Overload + procedure List; overload; + //.NET Overload + procedure List(const ASpecifier: string; ADetails: Boolean = True); overload; + procedure List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); overload; + procedure ExtListDir(ADest: TStrings = nil; const ADirectory: string = ''); + procedure ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string=''); overload; + procedure ExtListItem(ADest: TStrings; const AItem: string = ''); overload; + procedure ExtListItem(AFList : TIdFTPListItems; const AItem : String= ''); overload; + function FileDate(const AFileName : String; const AsGMT : Boolean = False): TDateTime; + + procedure Login; + procedure MakeDir(const ADirName: string); + procedure Noop; + procedure SetCmdOpt(const ACMD, AOptions : String); + procedure Put(const ASource: TStream; const ADestFile: string; + const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload; + procedure Put(const ASourceFile: string; const ADestFile: string = ''; + const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload; + + procedure StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1); overload; + procedure StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1); overload; + + procedure SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String; const ADestFile : String = ''); + procedure SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String; const ADestFile : String = ''); + procedure DisconnectNotifyPeer; override; + procedure Quit; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPECATED_MSG} 'Use Disconnect() instead'{$ENDIF};{$ENDIF} + function Quote(const ACommand: String): Int16; + procedure RemoveDir(const ADirName: string); + procedure Rename(const ASourceFile, ADestFile: string); + function ResumeSupported: Boolean; + function RetrieveCurrentDir: string; + procedure Site(const ACommand: string); + function Size(const AFileName: String): Int64; + procedure Status(AStatusList: TStrings); + procedure StructureMount(APath: String); + procedure TransferMode(ATransferMode: TIdFTPTransferMode); + procedure ReInitialize(ADelay: UInt32 = 10); + procedure SetLang(const ALangTag : String); + function CRC(const AFIleName : String; const AStartPoint : Int64 = 0; const AEndPoint : Int64=0) : Int64; + //verify file was uploaded, this is more comprehensive than the above + function VerifyFile(ALocalFile : TStream; const ARemoteFile : String; + const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload; + function VerifyFile(const ALocalFile, ARemoteFile : String; + const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload; + //file parts must be in order in TStrings parameter + //GlobalScape FTP Pro uses this for multipart simultanious file uploading + procedure CombineFiles(const ATargetFile : String; AFileParts : TStrings); + //Set modified file time. + procedure SetModTime(const AFileName: String; const ALocalTime: TDateTime); + procedure SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime); + // servers that support MDTM yyyymmddhhmmss[+-xxx] and also support LIST -T + //This is true for servers that are known to support these even if they aren't + //listed in the FEAT reply. + function IsServerMDTZAndListTForm : Boolean; + property IsCompressionSupported : Boolean read FIsCompressionSupported; + // + property SupportsVerification : Boolean read GetSupportsVerification; + property CanResume: Boolean read ResumeSupported; + property CanUseMLS : Boolean read FCanUseMLS; + property DirectoryListing: TIdFTPListItems read GetDirectoryListing; + property DirFormat : String read FDirFormat; + property LangsSupported : TStrings read FLangsSupported; + property ListParserClass : TIdFTPListParseClass read FListParserClass write FListParserClass; + property LoginMsg: TIdReplyFTP read FLoginMsg; + property ListResult: TStrings read FListResult; + property SystemDesc: string read FSystemDesc; + property TZInfo : TIdFTPTZInfo read FTZInfo write SetTZInfo; + property UsingExtDataPort : Boolean read FUsingExtDataPort; + property UsingNATFastTrack : Boolean read FUsingNATFastTrack; + property UsingSFTP : Boolean read FUsingSFTP; + property CurrentTransferMode : TIdFTPTransferMode read FCurrentTransferMode write TransferMode; + + published + {$IFDEF DOTNET} + {$IFDEF DOTNET_2_OR_ABOVE} + property IPVersion; + {$ENDIF} + {$ELSE} + property IPVersion; + {$ENDIF} + property AutoIssueFEAT : Boolean read FAutoIssueFEAT write FAutoIssueFEAT default DEF_Id_FTP_AutoIssueFEAT; + property AutoLogin: Boolean read FAutoLogin write FAutoLogin default DEF_Id_FTP_AutoLogin; + // This is an object that can compress and decompress FTP Deflate encoding + property Compressor : TIdZLibCompressorBase read FCompressor write SetCompressor; + property Host; + property UseCCC : Boolean read FUseCCC write SetUseCCC default DEF_Id_FTP_UseCCC; + property Passive: boolean read FPassive write SetPassive default Id_TIdFTP_Passive; + property PassiveUseControlHost: Boolean read FPassiveUseControlHost write FPassiveUseControlHost default DEF_Id_FTP_PassiveUseControlHost; + property DataPortProtection : TIdFTPDataPortSecurity read FDataPortProtection write SetDataPortProtection default Id_TIdFTP_DataPortProtection; + property AUTHCmd : TAuthCmd read FAUTHCmd write SetAUTHCmd default DEF_Id_FTP_AUTH_CMD; + property ConnectTimeout; + property DataPort: TIdPort read FDataPort write FDataPort default 0; + property DataPortMin: TIdPort read FDataPortMin write FDataPortMin default 0; + property DataPortMax: TIdPort read FDataPortMax write FDataPortMax default 0; + property DefStringEncoding : IIdTextEncoding read FDefStringEncoding write SetDefStringEncoding; + property ExternalIP : String read FExternalIP write FExternalIP; + property Password; + property TransferType: TIdFTPTransferType read FTransferType write SetTransferType default Id_TIdFTP_TransferType; + property TransferTimeout: Integer read FTransferTimeout write FTransferTimeout default IdDefTimeout; + property ListenTimeout : Integer read FListenTimeout write FListenTimeout default DEF_Id_FTP_ListenTimeout; + property Username; + property Port default IDPORT_FTP; + property UseExtensionDataPort : Boolean read FUseExtensionDataPort write SetUseExtensionDataPort default DEF_Id_TIdFTP_UseExtendedData; + property UseMLIS : Boolean read FUseMLIS write FUseMLIS default DEF_Id_TIdFTP_UseMIS; + property TryNATFastTrack : Boolean read FTryNATFastTrack write SetTryNATFastTrack default Id_TIdFTP_UseNATFastTrack; + property NATKeepAlive: TIdFTPKeepAlive read FNATKeepAlive write SetNATKeepAlive; + property ProxySettings: TIdFtpProxySettings read FProxySettings write SetProxySettings; + property Account: string read FAccount write FAccount; + property ClientInfo : TIdFTPClientIdentifier read FClientInfo write SetClientInfo; + property UseHOST: Boolean read FUseHOST write FUseHOST default DEF_Id_FTP_UseHOST; + property ServerHOST: String read FServerHOST write FServerHOST; + property UseTLS; + property OnTLSNotAvailable; + + property OnBannerBeforeLogin : TIdFTPBannerEvent read FOnBannerBeforeLogin write FOnBannerBeforeLogin; + property OnBannerAfterLogin : TIdFTPBannerEvent read FOnBannerAfterLogin write FOnBannerAfterLogin; + property OnBannerWarning : TIdFTPBannerEvent read FOnBannerWarning write FOnBannerWarning; + + property OnAfterClientLogin: TOnAfterClientLogin read FOnAfterClientLogin write FOnAfterClientLogin; + property OnCreateFTPList: TIdCreateFTPList read FOnCreateFTPList write FOnCreateFTPList; + property OnAfterGet: TIdFtpAfterGet read FOnAfterGet write FOnAfterGet; //APR + property OnAfterPut: TNotifyEvent read FOnAfterPut write FOnAfterPut; + property OnNeedAccount: TIdNeedAccountEvent read FOnNeedAccount write FOnNeedAccount; + property OnCustomFTPProxy : TNotifyEvent read FOnCustomFTPProxy write FOnCustomFTPProxy; + property OnDataChannelCreate: TIdOnDataChannelCreate read FOnDataChannelCreate write FOnDataChannelCreate; + property OnDataChannelDestroy: TIdOnDataChannelDestroy read FOnDataChannelDestroy write FOnDataChannelDestroy; + //The directory was Retrieved from the FTP server. + property OnRetrievedDir : TNotifyEvent read FOnRetrievedDir write FOnRetrievedDir; + //parsing is done only when DirectoryLiusting is referenced + property OnDirParseStart : TNotifyEvent read FOnDirParseStart write FOnDirParseStart; + property OnDirParseEnd : TNotifyEvent read FOnDirParseEnd write FOnDirParseEnd; + property ReadTimeout default DEF_Id_FTP_READTIMEOUT; + end; + + EIdFTPException = class(EIdException); + EIdFTPFileAlreadyExists = class(EIdFTPException); + EIdFTPMustUseExtWithIPv6 = class(EIdFTPException); + EIdFTPMustUseExtWithNATFastTrack = class(EIdFTPException); + EIdFTPPassiveMustBeTrueWithNATFT = class(EIdFTPException); + EIdFTPServerSentInvalidPort = class(EIdFTPException); + EIdFTPSiteToSiteTransfer = class(EIdFTPException); + EIdFTPSToSNATFastTrack = class(EIdFTPSiteToSiteTransfer); + EIdFTPSToSNoDataProtection = class(EIdFTPSiteToSiteTransfer); + EIdFTPSToSIPProtoMustBeSame = class(EIdFTPSiteToSiteTransfer); + EIdFTPSToSBothMostSupportSSCN = class(EIdFTPSiteToSiteTransfer); + EIdFTPSToSTransModesMustBeSame = class(EIdFTPSiteToSiteTransfer); + EIdFTPOnCustomFTPProxyRequired = class(EIdFTPException); + EIdFTPConnAssuranceFailure = class(EIdFTPException); + EIdFTPWrongIOHandler = class(EIdFTPException); + EIdFTPUploadFileNameCanNotBeEmpty = class(EIdFTPException); + + EIdFTPDataPortProtection = class(EIdFTPException); + EIdFTPNoDataPortProtectionAfterCCC = class(EIdFTPDataPortProtection); + EIdFTPNoDataPortProtectionWOEncryption = class(EIdFTPDataPortProtection); + EIdFTPNoCCCWOEncryption = class(EIdFTPException); + EIdFTPAUTHException = class(EIdFTPException); + EIdFTPNoAUTHWOSSL = class(EIdFTPAUTHException); + EIdFTPCanNotSetAUTHCon = class(EIdFTPAUTHException); + + EIdFTPMissingCompressor = class(EIdFTPException); + EIdFTPCompressorNotReady = class(EIdFTPException); + EIdFTPUnsupportedTransferMode = class(EIdFTPException); + EIdFTPUnsupportedTransferType = class(EIdFTPException); + +implementation + +uses + //facilitate inlining only. + {$IFDEF KYLIXCOMPAT} + Libc, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + Posix.Unistd, + {$ENDIF} + {$IFDEF WINDOWS} + //facilitate inlining only. + Windows, + {$ENDIF} + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + System.Threading, + {$ENDIF} + {$ENDIF} + IdComponent, + IdFIPS, + IdResourceStringsCore, IdIOHandlerStack, IdResourceStringsProtocols, + IdSSL, IdGlobalProtocols, IdHash, IdHashCRC, IdHashSHA, IdHashMessageDigest, + IdStack, IdStackConsts, IdSimpleServer, IdOTPCalculator, SysUtils; + +const + cIPVersions: array[TIdIPVersion] of String = ('1', '2'); {do not localize} + +type + TIdFTPListResult = class(TStringList) + private + FDetails: Boolean; //Did the developer use the NLST command for the last list command + FUsedMLS : Boolean; //Did the developer use MLSx commands for the last list command + public + property Details: Boolean read FDetails; + property UsedMLS: Boolean read FUsedMLS; + end; + +procedure TIdFTP.InitComponent; +begin + inherited InitComponent; + // + FAutoLogin := DEF_Id_FTP_AutoLogin; + FRegularProtPort := IdPORT_FTP; + FImplicitTLSProtPort := IdPORT_ftps; + // + Port := IDPORT_FTP; + Passive := Id_TIdFTP_Passive; + FPassiveUseControlHost := DEF_Id_FTP_PassiveUseControlHost; + + FDataPortProtection := Id_TIdFTP_DataPortProtection; + FUseCCC := DEF_Id_FTP_UseCCC; + FAUTHCmd := DEF_Id_FTP_AUTH_CMD; + FUseHOST := DEF_Id_FTP_UseHOST; + + FDataPort := 0; + FDataPortMin := 0; + FDataPortMax := 0; + FDefStringEncoding := IndyTextEncoding_8Bit; + FUseExtensionDataPort := DEF_Id_TIdFTP_UseExtendedData; + FTryNATFastTrack := Id_TIdFTP_UseNATFastTrack; + FTransferType := Id_TIdFTP_TransferType; + FTransferTimeout := IdDefTimeout; + FListenTimeout := DEF_Id_FTP_ListenTimeout; + FLoginMsg := TIdReplyFTP.Create(nil); + FListResult := TIdFTPListResult.Create; + FLangsSupported := TStringList.Create; + FCanResume := False; + FResumeTested := False; + FProxySettings:= TIdFtpProxySettings.Create; //APR + FClientInfo := TIdFTPClientIdentifier.Create; + FTZInfo := TIdFTPTZInfo.Create; + FTZInfo.FGMTOffsetAvailable := False; + FUseMLIS := DEF_Id_TIdFTP_UseMIS; + FCanUseMLS := False; //initialize MLIS flags + //Settings specified by + // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + FZLibCompressionLevel := DEF_ZLIB_COMP_LEVEL; + FZLibWindowBits := DEF_ZLIB_WINDOW_BITS; //-15 - no extra headers + FZLibMemLevel := DEF_ZLIB_MEM_LEVEL; + FZLibStratagy := DEF_ZLIB_STRATAGY; // - default + // + FAbortFlag := TIdThreadSafeBoolean.Create; + FAbortFlag.Value := False; + + { + Some firewalls don't handle control connections properly during long + data transfers. They will timeout the control connection because it + is idle and making it worse is that they will chop off a connection + instead of closing it, causing TIdFTP to wait forever for nothing. + } + FNATKeepAlive := TIdFTPKeepAlive.Create; + ReadTimeout := DEF_Id_FTP_READTIMEOUT; + + FAutoIssueFEAT := DEF_Id_FTP_AutoIssueFEAT; +end; + +{$IFNDEF HAS_TryEncodeTime} +// TODO: move this to IdGlobal or IdGlobalProtocols... +function TryEncodeTime(Hour, Min, Sec, MSec: Word; out VTime: TDateTime): Boolean; +begin + try + VTime := EncodeTime(Hour, Min, Sec, MSec); + Result := True; + except + Result := False; + end; +end; +{$ENDIF} + +{$IFNDEF HAS_TryStrToInt} +// TODO: use the implementation already in IdGlobalProtocols... +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} + +procedure TIdFTP.Connect; +var + LHost: String; + LPort: TIdPort; + LBuf : String; + LSendQuitOnError: Boolean; + LOffs: Integer; + LRetryWithoutHOST: Boolean; +begin + LSendQuitOnError := False; + + FCurrentTransferMode := dmStream; + FTZInfo.FGMTOffsetAvailable := False; + //FSSCNOn should be set to false to prevent problems. + FSSCNOn := False; + FUsingSFTP := False; + FUsingCCC := False; + FDataSettingsSent := False; + if FUseExtensionDataPort then begin + FUsingExtDataPort := True; + end; + FUsingNATFastTrack := False; + FCapabilities.Clear; + + try + //APR 011216: proxy support + LHost := FHost; + LPort := FPort; + try + //I think fpcmTransparent means to connect to the regular host and the firewalll + //intercepts the login information. + if (ProxySettings.ProxyType <> fpcmNone) and (ProxySettings.ProxyType <> fpcmTransparent) and + (Length(ProxySettings.Host) > 0) then begin + FHost := ProxySettings.Host; + FPort := ProxySettings.Port; + end; + if FUseTLS = utUseImplicitTLS then begin + //at this point, we treat implicit FTP as if it were explicit FTP with TLS + FUsingSFTP := True; + end; + inherited Connect; + finally + FHost := LHost; + FPort := LPort; + end; + + // RLebeau: must not send/receive UTF-8 before negotiating for it... + IOHandler.DefStringEncoding := FDefStringEncoding; + {$IFDEF STRING_IS_ANSI} + IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault; + {$ENDIF} + + // RLebeau: RFC 959 says that the greeting can be preceeded by a 1xx + // reply and that the client should wait for the 220 reply when this + // happens. Also, the RFC says that 120 should be used, but some + // servers use other 1xx codes, such as 130, so handle 1xx generically + + // calling GetInternalResponse() directly to avoid duplicate calls + // to CheckResponse() for the initial response if it is not 1xx + GetInternalResponse; + if (LastCmdResult.NumericCode div 100) = 1 then begin + DoOnBannerWarning(LastCmdResult.FormattedReply); + GetResponse(220); + end else begin + CheckResponse(LastCmdResult.NumericCode, [220]); + end; + + LSendQuitOnError := True; + + FGreeting.Assign(LastCmdResult); + // Save initial greeting for server identification in case FGreeting changes + // in response to the HOST command + if FGreeting.Text.Count > 0 then begin + FServerDesc := FGreeting.Text[0]; + end else begin + FServerDesc := ''; + end; + // Implement HOST command as specified by + // http://tools.ietf.org/html/draft-hethmon-mcmurray-ftp-hosts-01 + // Do not check the response for failures. The draft suggests allowing + // 220 (success) and 500/502 (unsupported), but vsftpd returns 530, and + // whatever ftp.microsoft.com is running returns 504. + if UseHOST then begin + // RLebeau: WS_FTP Server 5.x disconnects if the command fails, + // whereas WS_FTP Server 6+ does not. If the server disconnected + // here, let's mimic FTP Voyager by reconnecting without using + // the HOST command again... + // + // RLebeau 11/18/2013: some other servers also disconnect on a failed + // HOST command, so no longer retrying connect for WSFTP exclusively... + // + // RLebeau 11/22/2014: encountered one case where the server disconnects + // before the reply is received. So checking for that as well... + // + LRetryWithoutHOST := False; + try + if SendHost() <> 220 then begin + IOHandler.CheckForDisconnect(True, True); + end; + except + on E: EIdConnClosedGracefully do begin + LRetryWithoutHOST := True; + end; + on E: EIdSocketError do begin + if (E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET) then begin + LRetryWithoutHOST := True; + end else begin + raise; + end; + end; + end; + if LRetryWithoutHOST then + begin + Disconnect(False); + if Assigned(IOHandler) then begin + IOHandler.InputBuffer.Clear; + end; + UseHOST := False; + try + Connect; + finally + UseHOST := True; + end; + Exit; + end; + end else begin + FGreeting.Assign(LastCmdResult); + end; + DoOnBannerBeforeLogin (FGreeting.FormattedReply); + + // RLebeau: having an AutoIssueFeat property doesn't make sense to + // me. There are commands below that require FEAT's response, but + // if the user sets AutoIssueFeat to False, these commands will not + // be allowed to execute! + + if AutoLogin then begin + Login; + DoAfterLogin; + + //Fast track is set only one time per connection and no more, even + //with REINIT + if TryNATFastTrack then begin + DoTryNATFastTrack; + end; + + if FUseTLS = utUseImplicitTLS then begin + //at this point, we treat implicit FTP as if it were explicit FTP with TLS + FUsingSFTP := True; + end; + + // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this? + // if SendCmd('SYST', [200, 215, 500]) = 500 then begin {do not localize} + //Do not fault if SYST was not understood by the server. Novel Netware FTP + //may not understand SYST. + if SendCmd('SYST') = 500 then begin {do not localize} + FSystemDesc := RSFTPUnknownHost; + end else begin + FSystemDesc := LastCmdResult.Text[0]; + end; + + if IsSiteZONESupported then begin + if SendCmd('SITE ZONE') = 210 then begin {do not localize} + if LastCmdResult.Text.Count > 0 then begin + LBuf := LastCmdResult.Text[0]; + // some servers (Serv-U, etc) use a 'UTC' offset string, ie + // "UTC-300", specifying the number of minutes from UTC. Other + // servers (Apache) use a GMT offset string instead, ie "-0300". + if TextStartsWith(LBuf, 'UTC-') then begin {do not localize} + // Titan FTP 6.26.634 incorrectly returns UTC-2147483647 when it's + // first installed. + FTZInfo.FGMTOffsetAvailable := + TryStrToInt(Copy(LBuf, 4, MaxInt), LOffs) and + TryEncodeTime(Abs(LOffs) div 60, Abs(LOffs) mod 60, 0, 0, FTZInfo.FGMTOffset); + if FTZInfo.FGMTOffsetAvailable and (LOffs < 0) then + FTZInfo.FGMTOffset := -FTZInfo.FGMTOffset + end else begin + FTZInfo.FGMTOffsetAvailable := True; + FTZInfo.GMTOffset := GmtOffsetStrToDateTime(LBuf); + end; + end; + end; + end; + + SendTransferType(FTransferType); + DoStatus(ftpReady, [RSFTPStatusReady]); + end else begin + // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this? + // if SendCmd('SYST', [200, 215, 500]) = 500 then begin {do not localize} + //Do not fault if SYST was not understood by the server. Novel Netware FTP + //may not understand SYST. + if SendCmd('SYST') = 500 then begin {do not localize} + FSystemDesc := RSFTPUnknownHost; + end else begin + FSystemDesc := LastCmdResult.Text[0]; + end; + if FAutoIssueFEAT then begin + IssueFEAT; + end; + end; + except + Disconnect(LSendQuitOnError); // RLebeau: do not send the QUIT command if the greeting was not received + raise; + end; +end; + +function TIdFTP.SendHost: Int16; +var + LHost: String; +begin + LHost := FServerHOST; + if LHost = '' then begin + LHost := FHost; + end; + if Socket <> nil then begin + if LHost = Socket.Binding.PeerIP then begin + LHost := '[' + LHost + ']'; {do not localize} + end; + end; + Result := SendCmd('HOST ' + LHost); {do not localize} +end; + +procedure TIdFTP.SetTransferType(AValue: TIdFTPTransferType); +begin + if AValue <> FTransferType then begin + if not Assigned(FDataChannel) then begin + if Connected then begin + SendTransferType(AValue); + end; + FTransferType := AValue; + end; + end; +end; + +procedure TIdFTP.SendTransferType(AValue: TIdFTPTransferType); +var + s: string; +begin + s := ''; + case AValue of + ftAscii: s := 'A'; {do not localize} + ftBinary: s := 'I'; {do not localize} + else + raise EIdFTPUnsupportedTransferType.Create(RSFTPUnsupportedTransferType); + end; + SendCmd('TYPE ' + s, 200); {do not localize} +end; + +function TIdFTP.ResumeSupported: Boolean; +begin + if not FResumeTested then begin + FResumeTested := True; + FCanResume := Quote('REST 1') = 350; {do not localize} + Quote('REST 0'); {do not localize} + end; + Result := FCanResume; +end; + +procedure TIdFTP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = False); +begin + //for SSL FXP, we have to do it here because InternalGet is used by the LIST command + //where SSCN is ignored. + ClearSSCN; + AResume := AResume and CanResume; + DoBeforeGet; + // RLebeau 7/26/06: do not do this! It breaks the ability to resume files + // ADest.Position := 0; + InternalGet('RETR ' + ASourceFile, ADest, AResume); + DoAfterGet(ADest); +end; + +procedure TIdFTP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean = False; + AResume: Boolean = False); +var + LDestStream: TStream; +begin + AResume := AResume and CanResume; + if ACanOverwrite and (not AResume) then begin + SysUtils.DeleteFile(ADestFile); + LDestStream := TIdFileCreateStream.Create(ADestFile); + end + else if (not ACanOverwrite) and AResume then begin + LDestStream := TIdAppendFileStream.Create(ADestFile); + end + else if not FileExists(ADestFile) then begin + LDestStream := TIdFileCreateStream.Create(ADestFile); + end + else begin + raise EIdFTPFileAlreadyExists.Create(RSDestinationFileAlreadyExists); + end; + try + Get(ASourceFile, LDestStream, AResume); + finally + FreeAndNil(LDestStream); + end; +end; + +procedure TIdFTP.DoBeforeGet; +begin + if Assigned(FOnBeforeGet) then begin + FOnBeforeGet(Self); + end; +end; + +procedure TIdFTP.DoBeforePut(AStream: TStream); +begin + if Assigned(FOnBeforePut) then begin + FOnBeforePut(Self, AStream); + end; +end; + +procedure TIdFTP.DoAfterGet(AStream: TStream);//APR +begin + if Assigned(FOnAfterGet) then begin + FOnAfterGet(Self, AStream); + end; +end; + +procedure TIdFTP.DoAfterPut; +begin + if Assigned(FOnAfterPut) then begin + FOnAfterPut(Self); + end; +end; + +procedure TIdFTP.ConstructDirListing; +begin + if not Assigned(FDirectoryListing) then begin + if not IsDesignTime then begin + DoFTPList; + end; + if not Assigned(FDirectoryListing) then begin + FDirectoryListing := TIdFTPListItems.Create; + end; + end else begin + FDirectoryListing.Clear; + end; +end; + +procedure TIdFTP.List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); {do not localize} +var + LDest: TMemoryStream; + LTrans : TIdFTPTransferType; +begin + if ADetails and UseMLIS and FCanUseMLS then begin + ExtListDir(ADest, ASpecifier); + Exit; + end; + // Note that for LIST, it might be best to put the connection in ASCII mode + // because some old servers such as TOPS20 might require this. We restore + // it if the original mode was not ASCII. It's a good idea to do this + // anyway because some clients still do this such as WS_FTP Pro and + // Microsoft's FTP Client. + LTrans := TransferType; + if LTrans <> ftASCII then begin + Self.TransferType := ftASCII; + end; + try + LDest := TMemoryStream.Create; + try + InternalGet(Trim(iif(ADetails, 'LIST', 'NLST') + ' ' + ASpecifier), LDest); {do not localize} + FreeAndNil(FDirectoryListing); + FDirFormat := ''; + LDest.Position := 0; + FListResult.Text := ReadStringFromStream(LDest, -1, IOHandler.DefStringEncoding{$IFDEF STRING_IS_ANSI}, IOHandler.DefAnsiEncoding{$ENDIF}); + TIdFTPListResult(FListResult).FDetails := ADetails; + TIdFTPListResult(FListResult).FUsedMLS := False; + // FDirFormat will be updated in ParseFTPList... + finally + FreeAndNil(LDest); + end; + if ADest <> nil then begin + ADest.Assign(FListResult); + end; + DoOnRetrievedDir; + finally + if LTrans <> ftASCII then begin + TransferType := LTrans; + end; + end; +end; + +const + AbortedReplies : array [0..5] of Int16 = + (226,426, 450,451,425,550); + //226 was added because one server will return that twice if you aborted + //during an upload. + AcceptableAbortReplies : array [0..8] of Int16 = + (225, 226, 250, 426, 450,451,425,550,552); + //GlobalScape Secure FTP Server returns a 552 for an aborted file + +procedure TIdFTP.FinalizeDataOperation; +var + LResponse : Int16; +begin + DoOnDataChannelDestroy; + if FDataChannel <> nil then begin + {$IFNDEF USE_OBJECT_ARC} + FDataChannel.IOHandler.Free; + {$ENDIF} + FDataChannel.IOHandler := nil; + FreeAndNil(FDataChannel); + end; + { +This is a bug fix for servers will do something like this: + +[2] Mon 06Jun05 13:33:28 - (000007) PASV +[6] Mon 06Jun05 13:33:28 - (000007) 227 Entering Passive Mode (192,168,1,107,4,22) +[2] Mon 06Jun05 13:33:28 - (000007) RETR test.txt.txt +[6] Mon 06Jun05 13:33:28 - (000007) 550 /test.txt.txt: No such file or directory. +[2] Mon 06Jun05 13:34:28 - (000007) QUIT +[6] Mon 06Jun05 13:34:28 - (000007) 221 Goodbye! +[5] Mon 06Jun05 13:34:28 - (000007) Closing connection for user TEST (00:01:08 connected) + } + if (LastCmdResult.NumericCode div 100) > 2 then + begin + DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]); + Exit; + end; + DoStatus(ftpReady, [RSFTPStatusDoneTransfer]); + // 226 = download successful, 225 = Abort successful} + if FAbortFlag.Value then begin + LResponse := GetResponse(AcceptableAbortReplies); +//Experimental - + if PosInSmallIntArray(LResponse,AbortedReplies) > -1 then begin + GetResponse([226, 225]); + end; +//IMPORTANT!!! KEEP THIS COMMENT!!! +// +//This is a workaround for a problem. When uploading a file on +//one FTP server and aborting that upload, I got this: +// +//Sent 3/9/2005 10:34:58 AM: STOR -------- +//Recv 3/9/2005 10:34:58 AM: 150 Opening BINARY mode data connection for [3513]Red_Glas.zip +//Sent 3/9/2005 10:34:59 AM: ABOR +//Recv 3/9/2005 10:35:00 AM: 226 Transfer complete. +//Recv 3/9/2005 10:35:00 AM: 226 Abort successful +// +//but at ftp.ipswitch.com (a WS_FTP Server 5.0.4 (2555009845) server ), +//I was getting this when aborting a download +// +//Sent 3/9/2005 12:43:41 AM: RETR imail6.pdf +//Recv 3/9/2005 12:43:41 AM: 150 Opening BINARY data connection for imail6.pdf (2150082 bytes) +//Sent 3/9/2005 12:43:42 AM: ABOR +//Recv 3/9/2005 12:43:42 AM: 226 abort successful +//Recv 3/9/2005 12:43:43 AM: 425 transfer canceled +// + if LResponse = 226 then begin + if IOHandler.Readable(10) then begin + GetResponse(AbortedReplies); + end; + end; + DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]); +//end experimental section + end else begin + //ftp.marist.edu returns 250 + GetResponse([226, 225, 250]); + end; +end; + +procedure TIdFTP.InternalPut(const ACommand: string; ASource: TStream; + AFromBeginning: Boolean = True; AResume: Boolean = False); + {$IFNDEF MSWINDOWS} + procedure WriteStreamFromBeginning; + var + LBuffer: TIdBytes; + LBufSize: Integer; + begin + // Copy entire stream without relying on ASource.Size so Unix-to-DOS + // conversion can be done on the fly. + BeginWork(wmWrite, ASource.Size); + try + SetLength(LBuffer, FDataChannel.IOHandler.SendBufferSize); + while True do begin + LBufSize := ASource.Read(LBuffer[0], Length(LBuffer)); + if LBufSize > 0 then + FDataChannel.IOHandler.Write(LBuffer, LBufSize) + else + Break; + end; + finally + EndWork(wmWrite); + end; + end; + {$ENDIF} +var + LIP: string; + LPort: TIdPort; + LPasvCl : TIdTCPClient; + LPortSv : TIdSimpleServer; + // under ARC, convert a weak reference to a strong reference before working with it + LCompressor : TIdZLibCompressorBase; +begin + FAbortFlag.Value := False; + LCompressor := nil; + + if FCurrentTransferMode = dmDeflate then begin + LCompressor := FCompressor; + if not Assigned(LCompressor) then begin + raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor); + end; + if not LCompressor.IsReady then begin + raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady); + end; + end; + + //for SSL FXP, we have to do it here because there is no command were a client + //submits data through a data port where the SSCN setting is ignored. + ClearSSCN; + DoStatus(ftpTransfer, [RSFTPStatusStartTransfer]); + // try + if FPassive then begin + SendPret(ACommand); + if FUsingExtDataPort then begin + SendEPassive(LIP, LPort); + end else begin + SendPassive(LIP, LPort); + end; + if AResume then begin + Self.SendCmd('REST ' + IntToStr(ASource.Position), [350]); {do not localize} + end; + IOHandler.WriteLn(ACommand); + + if Socket <> nil then begin + FDataChannel := TIdTCPClient.Create(nil); + end else begin + FDataChannel := nil; + end; + + LPasvCl := TIdTCPClient(FDataChannel); + try + InitDataChannel; + + if (Self.Socket <> nil) and PassiveUseControlHost then begin + //Do not use an assignment from Self.Host + //because a DNS name may not resolve to the same + //IP address every time. This is the case where + //the workload is distributed around several servers. + //Besides, we already know the Peer's IP address so + //why waste time querying it. + LIP := Self.Socket.Binding.PeerIP; + end; + + if LPasvCl <> nil then begin + LPasvCl.Host := LIP; + LPasvCl.Port := LPort; + + DoOnDataChannelCreate; + + LPasvCl.Connect; + end; + try + Self.GetResponse([110, 125, 150]); + try + if FDataChannel <> nil then begin + if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin + TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).Passthrough := False; + end; + if Assigned(LCompressor) then begin + LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler, + FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy); + end else begin + if AFromBeginning then begin + {$IFNDEF MSWINDOWS} + WriteStreamFromBeginning; + {$ELSE} + FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning + {$ENDIF} + end else begin + FDataChannel.IOHandler.Write(ASource, -1, False); // from current position + end; + end; + end; + except + on E: EIdSocketError do + begin + // If 10038 - abort was called. Server will return 225 + if E.LastError <> 10038 then begin + raise; + end; + end; + end; + finally + if LPasvCl <> nil then begin + LPasvCl.Disconnect(False); + end; + end; + finally + FinalizeDataOperation; + end; + end else begin + if Socket <> nil then begin + FDataChannel := TIdSimpleServer.Create(nil); + end else begin + FDataChannel := nil; + end; + + LPortSv := TIdSimpleServer(FDataChannel); + try + InitDataChannel; + + if LPortSv <> nil then begin + LPortSv.BoundIP := Self.Socket.Binding.IP; + LPortSv.BoundPort := FDataPort; + LPortSv.BoundPortMin := FDataPortMin; + LPortSv.BoundPortMax := FDataPortMax; + + DoOnDataChannelCreate; + + LPortSv.BeginListen; + if FUsingExtDataPort then begin + SendEPort(LPortSv.Binding); + end else begin + SendPort(LPortSv.Binding); + end; + end else begin + // TODO: + { + if FUsingExtDataPort then begin + SendEPort(?); + end else begin + SendPort(?); + end; + } + end; + + if AResume then begin + Self.SendCmd('REST ' + IntToStr(ASource.Position), [350]); {do not localize} + end; + Self.SendCmd(ACommand, [125, 150]); + + if LPortSv <> nil then begin + LPortSv.Listen(ListenTimeout); + if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin + TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False; + end; + if Assigned(LCompressor) then begin + LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler, + FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy); + end else begin + if AFromBeginning then begin + {$IFNDEF MSWINDOWS} + WriteStreamFromBeginning; + {$ELSE} + FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning + {$ENDIF} + end else begin + FDataChannel.IOHandler.Write(ASource, -1, False); // from current position + end; + end; + end; + finally + FinalizeDataOperation; + end; + end; + { This will silently ignore the STOR request if the server has forcibly disconnected + (kicked or timed out) before the request starts + except + //Note that you are likely to get an exception you abort a transfer + //hopefully, this will make things work better. + on E: EIdConnClosedGracefully do begin + end; + end;} + +{ commented out because we might need to revert back to this + if new code fails. + if (LResponse = 426) or (LResponse = 450) then + begin + // some servers respond with 226 on ABOR + GetResponse([226, 225]); + DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]); + end; + } +end; + + +procedure TIdFTP.InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false); +var + LIP: string; + LPort: TIdPort; + LPasvCl : TIdTCPClient; + LPortSv : TIdSimpleServer; + // under ARC, convert a weak reference to a strong reference before working with it + LCompressor: TIdZLibCompressorBase; +begin + FAbortFlag.Value := False; + LCompressor := nil; + + if FCurrentTransferMode = dmDeflate then begin + LCompressor := FCompressor; + if not Assigned(LCompressor) then begin + raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor); + end; + if not LCompressor.IsReady then begin + raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady); + end; + end; + + DoStatus(ftpTransfer, [RSFTPStatusStartTransfer]); + if FPassive then begin + SendPret(ACommand); + //PASV or EPSV + if FUsingExtDataPort then begin + SendEPassive(LIP, LPort); + end else begin + SendPassive(LIP, LPort); + end; + + if Socket <> nil then begin + FDataChannel := TIdTCPClient.Create(nil); + end else begin + FDataChannel := nil; + end; + + LPasvCl := TIdTCPClient(FDataChannel); + try + InitDataChannel; + + if (Self.Socket <> nil) and PassiveUseControlHost then begin + //Do not use an assignment from Self.Host + //because a DNS name may not resolve to the same + //IP address every time. This is the case where + //the workload is distributed around several servers. + //Besides, we already know the Peer's IP address so + //why waste time querying it. + LIP := Self.Socket.Binding.PeerIP; + end; + + if LPasvCl <> nil then begin + LPasvCl.Host := LIP; + LPasvCl.Port := LPort; + + DoOnDataChannelCreate; + + LPasvCl.Connect; + end; + try + if AResume then begin + Self.SendCmd('REST ' + IntToStr(ADest.Position), [350]); {do not localize} + end; + // APR: Ericsson Switch FTP + // + // RLebeau: some servers send 450 when no files are + // present, so do not read the stream in that case + if Self.SendCmd(ACommand, [125, 150, 154, 450]) <> 450 then + begin + if LPasvCl <> nil then begin + if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin + TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).Passthrough := False; + end; + if Assigned(LCompressor) then begin + LCompressor.DecompressFTPFromIO(LPasvCl.IOHandler, ADest, FZLibWindowBits); + end else begin + LPasvCl.IOHandler.ReadStream(ADest, -1, True); + end; + end; + end; + finally + if LPasvCl <> nil then begin + LPasvCl.Disconnect(False); + end; + end; + finally + FinalizeDataOperation; + end; + end else begin + // PORT or EPRT + if Socket <> nil then begin + FDataChannel := TIdSimpleServer.Create(nil); + end else begin + FDataChannel := nil; + end; + + LPortSv := TIdSimpleServer(FDataChannel); + try + InitDataChannel; + + if LPortSv <> nil then begin + LPortSv.BoundIP := Self.Socket.Binding.IP; + LPortSv.BoundPort := FDataPort; + LPortSv.BoundPortMin := FDataPortMin; + LPortSv.BoundPortMax := FDataPortMax; + + DoOnDataChannelCreate; + + LPortSv.BeginListen; + if FUsingExtDataPort then begin + SendEPort(LPortSv.Binding); + end else begin + SendPort(LPortSv.Binding); + end; + end else begin + // TODO: + { + if FUsingExtDataPort then begin + SendEPort(?); + end else begin + SendPort(?); + end; + } + end; + + if AResume then begin + SendCmd('REST ' + IntToStr(ADest.Position), [350]); {do not localize} + end; + SendCmd(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP); + + if LPortSv <> nil then begin + LPortSv.Listen(ListenTimeout); + if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin + TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False; + end; + if Assigned(LCompressor) then begin + LCompressor.DecompressFTPFromIO(LPortSv.IOHandler, ADest, FZLibWindowBits); + end else begin + FDataChannel.IOHandler.ReadStream(ADest, -1, True); + end; + end; + finally + FinalizeDataOperation; + end; + end; + + // ToDo: Change that to properly handle response code (not just success or except) + // 226 = download successful, 225 = Abort successful} + //commented out in case we need to revert back to this. +{ LResponse := GetResponse([225, 226, 250, 426, 450]); + if (LResponse = 426) or (LResponse = 450) then begin + GetResponse([226, 225]); + DoStatus(ftpAborted, [RSFTPStatusAbortTransfer]); + end; } +end; + +procedure TIdFTP.DoOnDataChannelCreate; +begin + // While the Control Channel is idle, Enable/disable TCP/IP keepalives. + // They're very small (40-byte) packages and will be sent every + // NATKeepAlive.IntervalMS after the connection has been idle for + // NATKeepAlive.IdleTimeMS. Prior to Windows 2000, the idle and + // timeout values are system wide and have to be set in the registry; + // the default is idle = 2 hours, interval = 1 second. + if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin + Socket.Binding.SetKeepAliveValues(True, NATKeepAlive.IdleTimeMS, NATKeepAlive.IntervalMS); + end; + if Assigned(FOnDataChannelCreate) then begin + OnDataChannelCreate(Self, FDataChannel); + end; +end; + +procedure TIdFTP.DoOnDataChannelDestroy; +begin + if Assigned(FOnDataChannelDestroy) then begin + OnDataChannelDestroy(Self, FDataChannel); + end; + if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin + Socket.Binding.SetKeepAliveValues(False, 0, 0); + end; +end; + +procedure TIdFTP.SetNATKeepAlive(AValue: TIdFTPKeepAlive); +begin + FNATKeepAlive.Assign(AValue); +end; + +{ TIdFtpKeepAlive } + +procedure TIdFtpKeepAlive.Assign(Source: TPersistent); +var + LSource: TIdFTPKeepAlive; +begin + if Source is TIdFTPKeepAlive then begin + LSource := TIdFTPKeepAlive(Source); + FUseKeepAlive := LSource.UseKeepAlive; + FIdleTimeMS := LSource.IdleTimeMS; + FIntervalMS := LSource.IntervalMS; + end else begin + inherited Assign(Source); + end; +end; + +procedure TIdFTP.DisconnectNotifyPeer; +begin + if IOHandler.Connected then begin + IOHandler.WriteLn('QUIT'); {do not localize} + IOHandler.CheckForDataOnSource(100); + if not IOHandler.InputBufferIsEmpty then begin + GetInternalResponse; + end; + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +procedure TIdFTP.Quit; +{$I IdDeprecatedImplBugOn.inc} +begin + Disconnect; +end; + +procedure TIdFTP.KillDataChannel; +begin + // Had kill the data channel () + if Assigned(FDataChannel) then begin + FDataChannel.Disconnect(False); //FDataChannel.IOHandler.DisconnectSocket; {//BGO} + end; +end; + +// IMPORTANT!!! THis is for later reference. +// +// Note that we do not send the Telnet IP and Sync as suggestedc by RFC 959. +// We do not do so because some servers will mistakenly assume that the sequences +// are part of the command and than give a syntax error. +// I noticed this with FTPSERVE IBM VM Level 510, Microsoft FTP Service (Version 5.0), +// GlobalSCAPE Secure FTP Server (v. 2.0), and Pure-FTPd [privsep] [TLS]. +// +// Thus, I feel that sending sequences is just going to aggravate this situation. +// It is probably the reason why some FTP clients no longer are sending Telnet IP +// and Sync with the ABOR command. +procedure TIdFTP.Abort; +begin + // only send the abort command. The Data channel is supposed to disconnect + if Connected then begin + IOHandler.WriteLn('ABOR'); {do not localize} + end; + // Kill the data channel: usually, the server doesn't close it by itself + KillDataChannel; + if Assigned(FDataChannel) then begin + FAbortFlag.Value := True; + end else begin + GetResponse([]); + end; +end; + +procedure TIdFTP.SendPort(AHandle: TIdSocketHandle); +begin + if FExternalIP <> '' then begin + SendPort(FExternalIP, AHandle.Port); + end else begin + SendPort(AHandle.IP, AHandle.Port); + end; +end; + +procedure TIdFTP.SendPort(const AIP: String; const APort: TIdPort); +begin + SendDataSettings; + SendCmd('PORT ' + ReplaceAll(AIP, '.', ',') {do not localize} + + ',' + IntToStr(APort div 256) + ',' + IntToStr(APort mod 256), [200]); {do not localize} +end; + +procedure TIdFTP.InitDataChannel; +var + LSSL : TIdSSLIOHandlerSocketBase; +begin + if FDataChannel = nil then begin + Exit; + end; + if FDataPortProtection = ftpdpsPrivate then begin + LSSL := TIdSSLIOHandlerSocketBase(IOHandler); + FDataChannel.IOHandler := LSSL.Clone; + //we have to delay the actual negotiation until we get the reply and + //and just before the readString + TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).Passthrough := True; + end else begin + FDataChannel.IOHandler := TIdIOHandler.MakeDefaultIOHandler(Self); + end; + if FDataChannel is TIdTCPClient then + begin + TIdTCPClient(FDataChannel).IPVersion := IPVersion; + TIdTCPClient(FDataChannel).ReadTimeout := FTransferTimeout; + //Now SocksInfo are multi-thread safe + FDataChannel.IOHandler.ConnectTimeout := IOHandler.ConnectTimeout; + end + else if FDataChannel is TIdSimpleServer then + begin + TIdSimpleServer(FDataChannel).IPVersion := IPVersion; + end; + if Assigned(FDataChannel.Socket) and Assigned(Socket) then + begin + FDataChannel.Socket.TransparentProxy := Socket.TransparentProxy; + end; + FDataChannel.IOHandler.ReadTimeout := FTransferTimeout; + FDataChannel.IOHandler.SendBufferSize := IOHandler.SendBufferSize; + FDataChannel.IOHandler.RecvBufferSize := IOHandler.RecvBufferSize; + FDataChannel.IOHandler.LargeStream := True; + // FDataChannel.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; + // FDataChannel.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault; + FDataChannel.WorkTarget := Self; +end; + +procedure TIdFTP.Put(const ASource: TStream; const ADestFile: string; + const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); +begin + if ADestFile = '' then begin + raise EIdFTPUploadFileNameCanNotBeEmpty.Create(RSFTPFileNameCanNotBeEmpty); + end; + if AStartPos > -1 then begin + ASource.Position := AStartPos; + end; + DoBeforePut(ASource); //APR); + if AAppend then begin + InternalPut('APPE ' + ADestFile, ASource, False, False); {Do not localize} + end else begin + InternalPut('STOR ' + ADestFile, ASource, AStartPos = -1, AStartPos > -1); {Do not localize} + end; + DoAfterPut; +end; + +procedure TIdFTP.Put(const ASourceFile: string; const ADestFile: string = ''; + const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); +var + LSourceStream: TStream; + LDestFileName : String; +begin + LDestFileName := ADestFile; + if LDestFileName = '' then begin + LDestFileName := ExtractFileName(ASourceFile); + end; + LSourceStream := TIdReadFileNonExclusiveStream.Create(ASourceFile); + try + Put(LSourceStream, LDestFileName, AAppend, AStartPos); + finally + FreeAndNil(LSourceStream); + end; +end; + +procedure TIdFTP.StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1); +begin + if AStartPos > -1 then begin + ASource.Position := AStartPos; + end; + DoBeforePut(ASource); + InternalPut('STOU', ASource, AStartPos = -1, False); {Do not localize} + DoAfterPut; +end; + +procedure TIdFTP.StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1); +var + LSourceStream: TStream; +begin + LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile); + try + StoreUnique(LSourceStream, AStartPos); + finally + FreeAndNil(LSourceStream); + end; +end; + +procedure TIdFTP.SendInternalPassive(const ACmd: String; var VIP: string; + var VPort: TIdPort); + + function IsRoutableAddress(AIP: string): Boolean; + begin + Result := not TextStartsWith(AIP, '127') and // Loopback 127.0.0.0-127.255.255.255 + not TextStartsWith(AIP, '10.') and // Private 10.0.0.0-10.255.255.255 + not TextStartsWith(AIP, '169.254') and // Link-local 169.254.0.0-169.254.255.255 + not TextStartsWith(AIP, '192.168') and // Private 192.168.0.0-192.168.255.255 + not (TextStartsWith(AIP, '172') and (AIP[7] = '.') and // Private 172.16.0.0-172.31.255.255 + (IndyStrToInt(Copy(AIP, 5, 2)) in [16..31])) + end; + +var + i, bLeft, bRight: integer; + s: string; +begin + SendDataSettings; + SendCmd(ACmd, 227); {do not localize} + s := Trim(LastCmdResult.Text[0]); + // Case 1 (Normal) + // 227 Entering passive mode(100,1,1,1,23,45) + bLeft := IndyPos('(', s); {do not localize} + bRight := IndyPos(')', s); {do not localize} + // Microsoft FTP Service may include a leading ( but not a trailing ), + // so handle any combination of "(..)", "(..", "..)", and ".." + if bLeft = 0 then bLeft := RPos(#32, S); + if bRight = 0 then bRight := Length(S) + 1; + S := Copy(S, bLeft + 1, bRight - bLeft - 1); + VIP := ''; {do not localize} + for i := 1 to 4 do begin + VIP := VIP + '.' + Fetch(s, ','); {do not localize} + end; + IdDelete(VIP, 1, 1); + // Server sent an unroutable address (private/reserved/etc). Use the IP we + // connected to instead + if not IsRoutableAddress(VIP) and IsRoutableAddress(Socket.Binding.PeerIP) then begin + VIP := Socket.Binding.PeerIP; + end; + // Determine port + VPort := TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF) shl 8; {do not localize} + //use trim as one server sends something like this: + //"227 Passive mode OK (195,92,195,164,4,99 )" + VPort := VPort or TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF); {Do not translate} +end; + +procedure TIdFTP.SendPassive(var VIP: string; var VPort: TIdPort); +begin + SendInternalPassive('PASV', VIP, VPort); {do not localize} +end; + +procedure TIdFTP.SendCPassive(var VIP: string; var VPort: TIdPort); +begin + SendInternalPassive('CPSV', VIP, VPort); {do not localize} +end; + +procedure TIdFTP.Noop; +begin + SendCmd('NOOP', 200); {do not localize} +end; + +procedure TIdFTP.MakeDir(const ADirName: string); +begin + SendCmd('MKD ' + ADirName, 257); {do not localize} +end; + +function TIdFTP.RetrieveCurrentDir: string; +begin + SendCmd('PWD', 257); {do not localize} + Result := LastCmdResult.Text[0]; + IdDelete(Result, 1, IndyPos('"', Result)); // Remove first doublequote {do not localize} + Result := Copy(Result, 1, IndyPos('"', Result) - 1); // Remove anything from second doublequote {do not localize} // to end of line + // TODO: handle embedded quotation marks. RFC 959 allows them to be present +end; + +procedure TIdFTP.RemoveDir(const ADirName: string); +begin + SendCmd('RMD ' + ADirName, 250); {do not localize} +end; + +procedure TIdFTP.Delete(const AFilename: string); +begin + // Linksys NSLU2 NAS returns 200, Ultimodule IDAL returns 257 + SendCmd('DELE ' + AFilename, [200, 250, 257]); {do not localize} +end; + +(* +CHANGE WORKING DIRECTORY (CWD) + + This command allows the user to work with a different + directory or dataset for file storage or retrieval without + altering his login or accounting information. Transfer + parameters are similarly unchanged. The argument is a + pathname specifying a directory or other system dependent + file group designator. + +CWD + 250 + 500, 501, 502, 421, 530, 550 +*) +procedure TIdFTP.ChangeDir(const ADirName: string); +begin + SendCmd('CWD ' + ADirName, [200, 250, 257]); //APR: Ericsson Switch FTP {do not localize} +end; + +(* +CHANGE TO PARENT DIRECTORY (CDUP) + + This command is a special case of CWD, and is included to + simplify the implementation of programs for transferring + directory trees between operating systems having different + syntaxes for naming the parent directory. The reply codes + shall be identical to the reply codes of CWD. See + Appendix II for further details. + +CDUP + 200 + 500, 501, 502, 421, 530, 550 +*) +procedure TIdFTP.ChangeDirUp; +begin + // RFC lists 200 as the proper response, but in another section says that it can return the + // same as CWD, which expects 250. That is it contradicts itself. + // MS in their infinite wisdom chnaged IIS 5 FTP to return 250. + SendCmd('CDUP', [200, 250]); {do not localize} +end; + +procedure TIdFTP.Site(const ACommand: string); +begin + SendCmd('SITE ' + ACommand, 200); {do not localize} +end; + +procedure TIdFTP.Rename(const ASourceFile, ADestFile: string); +begin + SendCmd('RNFR ' + ASourceFile, 350); {do not localize} + SendCmd('RNTO ' + ADestFile, 250); {do not localize} +end; + +function TIdFTP.Size(const AFileName: String): Int64; +var + LTrans : TIdFTPTransferType; + SizeStr: String; +begin + Result := -1; + // RLebeau 03/13/2009: some servers refuse to accept the SIZE command in + // ASCII mode, returning a "550 SIZE not allowed in ASCII mode" reply. + // We put the connection in BINARY mode, even though no data connection is + // actually being used. We restore it if the original mode was not BINARY. + // It's a good idea to do this anyway because some other clients do this + // as well. + LTrans := TransferType; + if LTrans <> ftBinary then begin + Self.TransferType := ftBinary; + end; + try + if SendCmd('SIZE ' + AFileName) = 213 then begin {do not localize} + SizeStr := Trim(LastCmdResult.Text.Text); + IdDelete(SizeStr, 1, IndyPos(' ', SizeStr)); // delete the response {do not localize} + Result := IndyStrToInt64(SizeStr, -1); + end; + finally + if LTrans <> ftBinary then begin + TransferType := LTrans; + end; + end; +end; + +//Added by SP +procedure TIdFTP.ReInitialize(ADelay: UInt32 = 10); +begin + IndySleep(ADelay); //Added + if SendCmd('REIN', [120, 220, 500]) <> 500 then begin {do not localize} + FLoginMsg.Clear; + FCanResume := False; + if Assigned(FDirectoryListing) then begin + FDirectoryListing.Clear; + end; + FUsername := ''; {do not localize} + FPassword := ''; {do not localize} + FPassive := Id_TIdFTP_Passive; + FCanResume := False; + FResumeTested := False; + FSystemDesc := ''; + FTransferType := Id_TIdFTP_TransferType; + IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; + {$IFDEF STRING_IS_ANSI} + IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault; + {$ENDIF} + if FUsingSFTP and (FUseTLS <> utUseImplicitTLS) then begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True; + FUsingSFTP := False; + FUseCCC := False; + end; + end; +end; + +procedure TIdFTP.Allocate(AAllocateBytes: Integer); +begin + SendCmd('ALLO ' + IntToStr(AAllocateBytes), [200]); {do not localize} +end; + +procedure TIdFTP.Status(AStatusList: TStrings); +begin + if SendCmd('STAT', [211, 212, 213, 500]) <> 500 then begin {do not localize} + AStatusList.Text := LastCmdResult.Text.Text; + end; +end; + +procedure TIdFTP.Help(AHelpContents: TStrings; ACommand: String = ''); {do not localize} +begin + if SendCmd(Trim('HELP ' + ACommand), [211, 214, 500]) <> 500 then begin {do not localize} + AHelpContents.Text := LastCmdResult.Text.Text; + end; +end; + +function TIdFTP.CheckAccount: Boolean; +begin + if (FAccount = '') and Assigned(FOnNeedAccount) then begin + FOnNeedAccount(Self, FAccount); + end; + Result := FAccount <> ''; +end; + +procedure TIdFTP.StructureMount(APath: String); +begin + SendCmd('SMNT ' + APath, [202, 250, 500]); {do not localize} +end; + +procedure TIdFTP.FileStructure(AStructure: TIdFTPDataStructure); +const + StructureTypes: array[TIdFTPDataStructure] of String = ('F', 'R', 'P'); {do not localize} +begin + SendCmd('STRU ' + StructureTypes[AStructure], [200, 500]); {do not localize} + { TODO: Needs to be finished } +end; + +procedure TIdFTP.TransferMode(ATransferMode: TIdFTPTransferMode); +var + s: String; +begin + if FCurrentTransferMode <> ATransferMode then begin + s := ''; + case ATransferMode of +// dmBlock: begin +// s := 'B'; {do not localize} +// end; +// dmCompressed: begin +// s := 'C'; {do not localize} +// end; + dmStream: begin + s := 'S'; {do not localize} + end; + dmDeflate: begin + if not Assigned(FCompressor) then begin + raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor); + end; + if Self.IsCompressionSupported then begin + s := 'Z'; {Do not localize} + end; + end; + end; + if s = '' then begin + raise EIdFTPUnsupportedTransferMode.Create(RSFTPUnsupportedTransferMode); + end; + SendCmd('MODE ' + s, 200); {do not localize} + FCurrentTransferMode := ATransferMode; + end; +end; + +destructor TIdFTP.Destroy; +begin + FreeAndNil(FClientInfo); + FreeAndNil(FListResult); + FreeAndNil(FLoginMsg); + FreeAndNil(FDirectoryListing); + FreeAndNil(FLangsSupported); + FreeAndNil(FProxySettings); //APR + FreeAndNil(FTZInfo); + FreeAndNil(FAbortFlag); + FreeAndNil(FNATKeepAlive); + inherited Destroy; +end; + +function TIdFTP.Quote(const ACommand: String): Int16; +begin + Result := SendCmd(ACommand); +end; + +procedure TIdFTP.IssueFEAT; +var + LClnt: String; + LBuf : String; + i : Integer; +begin + //Feat data + + SendCmd('FEAT'); {do not localize} + FCapabilities.Clear; + + //Ipswitch's FTP WS-FTP Server may issue 221 as success + if LastCmdResult.NumericCode in [211,221] then begin + FCapabilities.AddStrings(LastCmdResult.Text); + + //we remove the first and last lines because we only want the list + if FCapabilities.Count > 0 then begin + FCapabilities.Delete(0); + end; + if FCapabilities.Count > 0 then begin + FCapabilities.Delete(FCapabilities.Count-1); + end; + end; + + if FUsingExtDataPort then begin + FUsingExtDataPort := IsExtSupported('EPRT') and IsExtSupported('EPSV'); {do not localize} + end; + + FCanUseMLS := IsExtSupported('MLSD') or IsExtSupported('MLST'); {do not localize} + ExtractFeatFacts('LANG', FLangsSupported); {do not localize} + + //see if compression is supported. + //we parse this way because IxExtensionSupported can only work + //with one word. + FIsCompressionSupported := False; + for i := 0 to FCapabilities.Count-1 do begin + LBuf := Trim(FCapabilities[i]); + if LBuf = 'MODE Z' then begin {do not localize} + FIsCompressionSupported := True; + Break; + end; + end; + + // send the CLNT command before sending the OPTS UTF8 command. + // some servers need this in order to work around a bug in + // Microsoft Internet Explorer's UTF-8 handling + if IsExtSupported('CLNT') then begin {do not localize} + LClnt := FClientInfo.ClntOutput; + if LClnt = '' then begin + LClnt := gsIdProductName + ' ' + gsIdVersion; + end; + SendCmd('CLNT ' + LClnt); {do not localize} + end; + + if IsExtSupported('UTF8') then begin {do not localize} + // RLebeau 10/1/13: per RFC 2640, OPTS commands are no longer used to + // activate UTF-8. If the server reports the 'UTF8' capability, it is + // required to detect and accept UTF-8 encoded paths/filenames... + { + // trying non-standard UTF-8 extension first, many servers use this... + // Cerberus and RaidenFTP return 220, but TitanFTP and Gene6 return 200 instead... + if not SendCmd('OPTS UTF8 ON') in [200, 220] then begin {do not localize + // trying draft-ietf-ftpext-utf-8-option-00.txt next... + if SendCmd('OPTS UTF-8 NLST') <> 200 then begin {do not localize + Exit; + end; + end; + } + IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; + end; +end; + +procedure TIdFTP.Login; +var + i : Integer; + LResp : Word; + LCmd : String; + + function FtpHost: String; + begin + if FPort = IDPORT_FTP then begin + Result := FHost; + end else begin + Result := FHost + Id_TIdFTP_HostPortDelimiter + IntToStr(FPort); + end; + end; + +begin +//This has to be here because the Rein command clears encryption. +//RFC 4217 + //TLS part + FUsingSFTP := False; + if UseTLS in ExplicitTLSVals then begin + if FAUTHCmd = tAuto then begin + {Note that we can not call SupportsTLS at all. That depends upon the FEAT response + and unfortunately, some servers such as WS_FTP Server 4.0.0 (78162662) + will not accept a FEAT command until you login. In other words, you have to do + this by trial and error. + } + + //334 has to be accepted because of a broekn implementation + //see: http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad + + {Note that we have to try several commands because some servers use AUTH TLS while others use + AUTH SSL. GlobalScape's FTP Server only uses AUTH SSL while IpSwitch's uses AUTH TLS (the correct behavior). + We try two other commands for historical reasons. + } + for i := 0 to 3 do begin + LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[i]); {do not localize} + if (LResp = 234) or (LResp = 334) then begin + //okay. do the handshake + TLSHandshake; + FUsingSFTP := True; + //we are done with the negotiation, let's close this. + Break; + end; + //see if the error was not any type of syntax error code + //if it wasn't, we fail the command. + if (LResp div 500) <> 1 then begin + ProcessTLSNegCmdFailed; + Break; + end; + end; + end else begin + LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[Ord(FAUTHCmd)-1]); {do not localize} + if (LResp = 234) or (LResp = 334) then begin + //okay. do the handshake + TLSHandshake; + FUsingSFTP := True; + end else begin + ProcessTLSNegCmdFailed; + end; + end; + end; + // TODO: should this be moved inside the 'if UseTLS in ExplicitTLSVals' block? + if not FUsingSFTP then begin + ProcessTLSNotAvail; + end; + //login + case ProxySettings.ProxyType of + fpcmNone: + begin + LCmd := MakeXAUTCmd( Greeting.Text.Text , FUserName, GetLoginPassword); + if (LCmd <> '') and (not GetFIPSMode ) then begin + if SendCmd(LCmd, [230, 232, 331]) = 331 then begin {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end else begin + if SendCmd('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230, 332]); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + end; + fpcmUserSite: + begin + //This also supports WinProxy + if Length(ProxySettings.UserName) > 0 then begin + if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + if SendCmd('USER ' + FUserName + '@' + FtpHost, [230, 232, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230, 331]); {do not localize} + if IsAccountNeeded then + begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + fpcmSite: + begin + if Length(ProxySettings.UserName) > 0 then begin + if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize} + end; + end; + SendCmd('SITE ' + FtpHost); // ? Server Reply? 220? {do not localize} + if SendCmd('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230, 332]); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + fpcmOpen: + begin + if Length(ProxySettings.UserName) > 0 then begin + if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230, 332]); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + SendCmd('OPEN ' + FtpHost);//? Server Reply? 220? {do not localize} + if SendCmd('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230, 332]); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + fpcmUserPass: //USER user@firewalluser@hostname / PASS pass@firewallpass + begin + if SendCmd(IndyFormat('USER %s@%s@%s', + [FUserName, ProxySettings.UserName, FtpHost]), [230, 232, 331]) = 331 then begin {do not localize} + if Length(ProxySettings.Password) > 0 then begin + SendCmd('PASS ' + GetLoginPassword + '@' + ProxySettings.Password, [230, 332]); {do not localize} + end else begin + //// needs otp //// + SendCmd('PASS ' + GetLoginPassword, [230,332]); {do not localize} + end; + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + fpcmTransparent: + begin + //I think fpcmTransparent means to connect to the regular host and the firewalll + //intercepts the login information. + if Length(ProxySettings.UserName) > 0 then begin + if SendCmd('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + ProxySettings.Password, [230,332]); {do not localize} + end; + end; + if SendCmd('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize} + SendCmd('PASS ' + GetLoginPassword, [230,332]); {do not localize} + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + fpcmUserHostFireWallID : //USER hostuserId@hostname firewallUsername + begin + if SendCmd(Trim('USER ' + Username + '@' + FtpHost + ' ' + ProxySettings.UserName), [230, 331]) = 331 then begin {do not localize} + if SendCmd('PASS ' + GetLoginPassword, [230,232,202,332]) = 332 then begin + SendCmd('ACCT ' + ProxySettings.Password, [230,232,332]); + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + end; + fpcmNovellBorder : //Novell Border PRoxy + begin +{Done like this: + +USER ProxyUserName$ DestFTPUserName$DestFTPHostName + +PASS UsereDirectoryPassword$ DestFTPPassword + +Novell BorderManager 3.8 Proxy and Firewall Overview and Planning Guide +Copyright 1997-1998, 2001, 2002-2003, 2004 Novell, Inc. All rights reserved. +=== +From a WS-FTP Pro firescript at: + +http://support.ipswitch.com/kb/WS-20050315-DM01.htm + +send ("USER %FwUserId$%HostUserId$%HostAddress") + +//send ("PASS %FwPassword$%HostPassword") + +} + if SendCmd(Trim('USER ' + ProxySettings.UserName + '$' + Username + '$' + FtpHost), [230, 331]) = 331 then begin {do not localize} + if SendCmd('PASS ' + ProxySettings.UserName + '$' + GetLoginPassword, [230,232,202,332]) = 332 then begin + if IsAccountNeeded then begin + if CheckAccount then begin + SendCmd('ACCT ' + FAccount, [202, 230, 500]); {do not localize} + end else begin + RaiseExceptionForLastCmdResult; + end; + end; + end; + end; + end; + fpcmHttpProxyWithFtp : + begin +{GET ftp://XXX:YYY@indy.nevrona.com/ HTTP/1.0 +Host: indy.nevrona.com +User-Agent: Mozilla/4.0 (compatible; Wincmd; Windows NT) +Proxy-Authorization: Basic B64EncodedUserPass== +Connection: close} + raise EIdSocksServerCommandError.Create(RSSocksServerCommandError); + end;//fpcmHttpProxyWithFtp + fpcmCustomProxy : + begin + DoCustomFTPProxy; + end; + end;//case + + FLoginMsg.Assign(LastCmdResult); + DoOnBannerAfterLogin(FLoginMsg.FormattedReply); + //should be here because this can be issued more than once per connection. + + if FAutoIssueFEAT then begin + IssueFEAT; + end; + + SendTransferType(FTransferType); +end; + +procedure TIdFTP.DoAfterLogin; +begin + if Assigned(FOnAfterClientLogin) then begin + OnAfterClientLogin(Self); + end; +end; + +procedure TIdFTP.DoFTPList; +begin + if Assigned(FOnCreateFTPList) then begin + FOnCreateFTPList(Self, FDirectoryListing); + end; +end; + +function TIdFTP.GetDirectoryListing: TIdFTPListItems; +begin + if FDirectoryListing = nil then begin + if Assigned(FOnDirParseStart) then begin + FOnDirParseStart(Self); + end; + ConstructDirListing; + ParseFTPList; + end; + Result := FDirectoryListing; +end; + +procedure TIdFTP.SetProxySettings(const Value: TIdFtpProxySettings); +begin + FProxySettings.Assign(Value); +end; + +{ TIdFtpProxySettings } + +procedure TIdFtpProxySettings.Assign(Source: TPersistent); +var + LSource: TIdFtpProxySettings; +begin + if Source is TIdFtpProxySettings then begin + LSource := TIdFtpProxySettings(Source); + FProxyType := LSource.ProxyType; + FHost := LSource.Host; + FUserName := LSource.UserName; + FPassword := LSource.Password; + FPort := LSource.Port; + end else begin + inherited Assign(Source); + end; +end; + +procedure TIdFTP.SendPBSZ; +begin + {NOte that PBSZ - protection buffer size must always be zero for FTP TLS} + if FUsingSFTP or (FUseTLS = utUseImplicitTLS) then begin + //protection buffer size + SendCmd('PBSZ 0'); {do not localize} + end; +end; + +procedure TIdFTP.SendPROT; +begin + case FDataPortProtection of + ftpdpsClear : SendCmd('PROT C', 200); //'C' - Clear - neither Integrity nor Privacy {do not localize} + // NOT USED - 'S' - Safe - Integrity without Privacy + // NOT USED - 'E' - Confidential - Privacy without Integrity + // 'P' - Private - Integrity and Privacy + ftpdpsPrivate : SendCmd('PROT P', 200); {do not localize} + end; +end; + +procedure TIdFTP.SendDataSettings; +begin + if FUsingSFTP then begin + if not FDataSettingsSent then begin + FDataSettingsSent := True; + SendPBSZ; + SendPROT; + if FUseCCC then begin + FUsingCCC := (SendCmd('CCC') div 100) = 2; {do not localize} + if FUsingCCC then begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True; + end; + end; + end; + end; +end; + +procedure TIdFTP.SetIOHandler(AValue: TIdIOHandler); +begin + inherited SetIOHandler(AValue); + // UseExtensionDataPort must be true for IPv6 connections. + // PORT and PASV can not communicate IPv6 Addresses + if Socket <> nil then begin + if Socket.IPVersion = Id_IPv6 then begin + FUseExtensionDataPort := True; + end; + end; +end; + +procedure TIdFTP.SetUseExtensionDataPort(const AValue: Boolean); +begin + if (not AValue) and (IPVersion = Id_IPv6) then begin + raise EIdFTPMustUseExtWithIPv6.Create(RSFTPMustUseExtWithIPv6); + end; + if TryNATFastTrack then begin + raise EIdFTPMustUseExtWithNATFastTrack.Create(RSFTPMustUseExtWithNATFastTrack); + end; + FUseExtensionDataPort := AValue; +end; + +procedure TIdFTP.ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort); +var + bLeft, bRight, LPort: Integer; + delim : Char; + s : String; +begin + s := Trim(AReply); + // "229 Entering Extended Passive Mode (|||59028|)" + bLeft := IndyPos('(', s); {do not localize} + bRight := IndyPos(')', s); {do not localize} + s := Copy(s, bLeft + 1, bRight - bLeft - 1); + delim := s[1]; // normally is | but the RFC say it may be different + Fetch(S, delim); + Fetch(S, delim); + VIP := Fetch(S, delim); + if VIP = '' then begin + VIP := Host; + end; + s := Trim(Fetch(S, delim)); + LPort := IndyStrToInt(s, 0); + if (LPort < 1) or (LPort > 65535) then begin + raise EIdFTPServerSentInvalidPort.CreateFmt(RSFTPServerSentInvalidPort, [s]); + end; + VPort := TIdPort(LPort and $FFFF); +end; + +procedure TIdFTP.SendEPassive(var VIP: string; var VPort: TIdPort); +begin + SendDataSettings; + //Note that for FTP Proxies, it is not desirable for the server to choose + //the EPSV data port IP connection type. We try to if we can. + if FProxySettings.ProxyType <> fpcmNone then begin + if SendCMD('EPSV ' + cIPVersions[IPVersion]) <> 229 then begin {do not localize} + //Raidon and maybe a few others may honor EPSV but not with the proto numbers + SendCMD('EPSV'); {do not localize} + end; + end else begin + SendCMD('EPSV'); {do not localize} + end; + if LastCmdResult.NumericCode <> 229 then begin + SendPassive(VIP, VPort); + FUsingExtDataPort := False; + Exit; + end; + try + ParseEPSV(LastCmdResult.Text[0], VIP, VPort); + except + SendCmd('ABOR'); {do not localize} + raise; + end; +end; + +procedure TIdFTP.SendEPort(AHandle: TIdSocketHandle); +begin + SendDataSettings; + if FExternalIP <> '' then begin + SendEPort(FExternalIP, AHandle.Port, AHandle.IPVersion); + end else begin + SendEPort(AHandle.IP, AHandle.Port, AHandle.IPVersion); + end; +end; + +procedure TIdFTP.SendEPort(const AIP: String; const APort: TIdPort; const AIPVersion: TIdIPVersion); +begin + if SendCmd('EPRT |' + cIPVersions[AIPVersion] + '|' + AIP + '|' + IntToStr(APort) + '|') <> 200 then begin {do not localize} + SendPort(AIP, APort); + FUsingExtDataPort := False; + end; +end; + +procedure TIdFTP.SetPassive(const AValue: Boolean); +begin + if (not AValue) and TryNATFastTrack then begin + raise EIdFTPPassiveMustBeTrueWithNATFT.Create(RSFTPFTPPassiveMustBeTrueWithNATFT); + end; + FPassive := AValue; +end; + +procedure TIdFTP.SetTryNATFastTrack(const AValue: Boolean); +begin + FTryNATFastTrack := AValue; + if FTryNATFastTrack then begin + FPassive := True; + FUseExtensionDataPort := True; + end; +end; + +procedure TIdFTP.DoTryNATFastTrack; +begin + if IsExtSupported('EPSV') then begin {do not localize} + if SendCmd('EPSV ALL') = 229 then begin {do not localize} + //Surge FTP treats EPSV ALL as if it were a standard EPSV + //We send ABOR in that case so it can close the data connection it created + SendCmd('ABOR'); {do not localize} + end; + FUsingNATFastTrack := True; + end; +end; + +procedure TIdFTP.SetCmdOpt(const ACmd, AOptions: String); +begin + SendCmd('OPTS ' + ACmd + ' ' + AOptions, 200); {do not localize} +end; + +procedure TIdFTP.ExtListDir(ADest: TStrings = nil; const ADirectory: string = ''); +var + LDest: TMemoryStream; + LEncoding: IIdTextEncoding; +begin + // RLebeau 6/4/2009: According to RFC 3659 Section 7.2: + // + // The data connection opened for a MLSD response shall be a connection + // as if the "TYPE L 8", "MODE S", and "STRU F" commands had been given, + // whatever FTP transfer type, mode and structure had actually been set, + // and without causing those settings to be altered for future commands. + // That is, this transfer type shall be set for the duration of the data + // connection established for this command only. While the content of + // the data sent can be viewed as a series of lines, implementations + // should note that there is no maximum line length defined. + // Implementations should be prepared to deal with arbitrarily long + // lines. + + LDest := TMemoryStream.Create; + try + InternalGet(Trim('MLSD ' + ADirectory), LDest); {do not localize} + FreeAndNil(FDirectoryListing); + FDirFormat := ''; + DoOnRetrievedDir; + LDest.Position := 0; + // RLebeau: using IndyTextEncoding_8Bit here. TIdFTPListParseBase will + // decode UTF-8 sequences later on... + LEncoding := IndyTextEncoding_8Bit; + FListResult.Text := ReadStringFromStream(LDest, -1, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + LEncoding := nil; + TIdFTPListResult(FListResult).FDetails := True; + TIdFTPListResult(FListResult).FUsedMLS := True; + FDirFormat := MLST; + finally + FreeAndNil(LDest); + end; + if Assigned(ADest) then begin //APR: User can use ListResult and DirectoryListing + ADest.Assign(FListResult); + end; +end; + +procedure TIdFTP.ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string); +var + i : Integer; +begin + ADest.Clear; + SendCmd(Trim('MLST ' + AItem), 250, IndyTextEncoding_8Bit); {do not localize} + for i := 0 to LastCmdResult.Text.Count -1 do begin + if IndyPos(';', LastCmdResult.Text[i]) > 0 then begin + ADest.Add(LastCmdResult.Text[i]); + end; + end; + if Assigned(AFList) then begin + IdFTPListParseBase.ParseListing(ADest, AFList, 'MLST'); {do not localize} + end; +end; + +procedure TIdFTP.ExtListItem(ADest: TStrings; const AItem: string); +begin + ExtListItem(ADest, nil, AItem); +end; + +procedure TIdFTP.ExtListItem(AFList: TIdFTPListItems; const AItem: String); +var + LBuf : TStrings; +begin + LBuf := TStringList.Create; + try + ExtListItem(LBuf, AFList, AItem); + finally + FreeAndNil(LBuf); + end; +end; + +function TIdFTP.IsExtSupported(const ACmd: String): Boolean; +var + i : Integer; + LBuf : String; +begin + Result := False; + for i := 0 to FCapabilities.Count -1 do begin + LBuf := TrimLeft(FCapabilities[i]); + if TextIsSame(Fetch(LBuf), ACmd) then begin + Result := True; + Exit; + end; + end; +end; + +function TIdFTP.FileDate(const AFileName: String; const AsGMT: Boolean): TDateTime; +var + LBuf : String; +begin + //Do not use the FEAT list because some servers + //may support it even if FEAT isn't supported + if SendCmd('MDTM ' + AFileName) = 213 then begin {do not localize} + LBuf := LastCmdResult.Text[0]; + LBuf := Trim(LBuf); + if AsGMT then begin + Result := FTPMLSToGMTDateTime(LBuf); + end else begin + Result := FTPMLSToLocalDateTime(LBuf); + end; + end else begin + Result := 0; + end; +end; + +procedure TIdFTP.SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String; + const ADestFile : String = ''); +{ +SiteToSiteUpload + + From: PASV To: PORT - ATargetUsesPasv = False + From: RETR To: STOR + +SiteToSiteDownload + + From: PORT To: PASV - ATargetUsesPasv = True + From: RETR To: STOR +} +begin + if ValidateInternalIsTLSFXP(Self, AToSite, True) then begin + InternalEncryptedTLSFXP(Self, AToSite, ASourceFile, ADestFile, True); + end else begin + InternalUnencryptedFXP(Self, AToSite, ASourceFile, ADestFile, True); + end; +end; + +procedure TIdFTP.SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String; + const ADestFile : String = ''); +{ + The only use of this function is to get the passive mode on the other connection. + Because not all hosts allow it. This way you get a second chance. + If uploading from host A doesn't work, try downloading from host B +} +begin + if ValidateInternalIsTLSFXP(AFromSite, Self, True) then begin + InternalEncryptedTLSFXP(AFromSite, Self, ASourceFile, ADestFile, False); + end else begin + InternalUnencryptedFXP(AFromSite, Self, ASourceFile, ADestFile, False); + end; +end; + +procedure TIdFTP.ExtractFeatFacts(const ACmd: String; AResults: TStrings); +var + i : Integer; + LBuf, LFact : String; +begin + AResults.Clear; + for i := 0 to FCapabilities.Count -1 do begin + LBuf := FCapabilities[i]; + if TextIsSame(Fetch(LBuf), ACmd) then begin + LBuf := Trim(LBuf); + while LBuf <> '' do begin + LFact := Trim(Fetch(LBuf, ';')); + if LFact <> '' then begin + AResults.Add(LFact); + end; + end; + Exit; + end; + end; +end; + +procedure TIdFTP.SetLang(const ALangTag: String); +begin + if IsExtSupported('LANG') then begin {do not localize} + SendCMD('LANG ' + ALangTag, 200); {do not localize} + end; +end; + +function TIdFTP.CRC(const AFIleName : String; const AStartPoint : Int64 = 0; + const AEndPoint : Int64 = 0) : Int64; +var + LCmd : String; + LCRC : String; +begin + Result := -1; + if IsExtSupported('XCRC') then begin {do not localize} + LCmd := 'XCRC "' + AFileName + '"'; {do not localize} + if AStartPoint <> 0 then begin + LCmd := LCmd + ' ' + IntToStr(AStartPoint); + if AEndPoint <> 0 then begin + LCmd := LCmd + ' ' + IntToStr(AEndPoint); + end; + end; + if SendCMD(LCMD) = 250 then begin + LCRC := Trim(LastCmdResult.Text.Text); + IdDelete(LCRC, 1, IndyPos(' ', LCRC)); // delete the response + Result := IndyStrToInt64('$' + LCRC, -1); + end; + end; +end; + +procedure TIdFTP.CombineFiles(const ATargetFile: String; AFileParts: TStrings); +var + i : Integer; + LCmd: String; +begin + if IsExtSupported('COMB') and (AFileParts.Count > 0) then begin {do not localize} + LCmd := 'COMB "' + ATargetFile + '"'; {do not localize} + for i := 0 to AFileParts.Count -1 do begin + LCmd := LCmd + ' ' + AFileParts[i]; + end; + SendCmd(LCmd, 250); + end; +end; + +procedure TIdFTP.ParseFTPList; +begin + DoOnDirParseStart; + try + // Parse directory listing + if FListResult.Count > 0 then begin + if TIdFTPListResult(FListResult).UsedMLS then begin + FDirFormat := MLST; + // TODO: set the FListParserClass as well.. + IdFTPListParseBase.ParseListing(FListResult, FDirectoryListing, MLST); + end else begin + CheckListParseCapa(FListResult, FDirectoryListing, FDirFormat, + FListParserClass, SystemDesc, TIdFTPListResult(FListResult).Details); + end; + end else begin + FDirFormat := ''; + end; + finally + DoOnDirParseEnd; + end; +end; + +function TIdFTP.GetSupportsTLS: Boolean; +begin + Result := (FindAuthCmd <> ''); +end; + +function TIdFTP.FindAuthCmd: String; +var + i : Integer; + LBuf : String; + LWord : String; +begin + Result := ''; + for i := 0 to FCapabilities.Count -1 do begin + LBuf := TrimLeft(FCapabilities[i]); + if TextIsSame(Fetch(LBuf), 'AUTH') then begin {do not localize} + repeat + LWord := Trim(Fetch(LBuf, ';')); + if PosInStrArray(LWord, TLS_AUTH_NAMES, False) > -1 then begin + Result := 'AUTH ' + LWord; {do not localize} + Exit; + end; + until LBuf = ''; + Break; + end; + end; +end; + +procedure TIdFTP.DoCustomFTPProxy; +begin + if Assigned(FOnCustomFTPProxy) then begin + FOnCustomFTPProxy(Self); + end else begin + raise EIdFTPOnCustomFTPProxyRequired.Create(RSFTPOnCustomFTPProxyReq); + end; +end; + +function TIdFTP.GetLoginPassword: String; +begin + Result := GetLoginPassword(LastCmdResult.Text.Text); +end; + +function TIdFTP.GetLoginPassword(const APrompt: String): String; +begin + if TIdOTPCalculator.IsValidOTPString(APrompt) then begin + TIdOTPCalculator.GenerateSixWordKey(APrompt, FPassword, Result); + end else begin + Result := FPassword; + end; +end; + +function TIdFTP.SetSSCNToOn : Boolean; +begin + Result := FUsingSFTP; + if not Result then begin + Exit; + end; + Result := (DataPortProtection = ftpdpsPrivate); + if not Result then begin + Exit; + end; + Result := not IsExtSupported(SCCN_FEAT); + if not Result then begin + Exit; + end; + if not FSSCNOn then begin + SendCmd(SSCN_ON, SSCN_OK_REPLY); + FSSCNOn := True; + end; +end; + +procedure TIdFTP.ClearSSCN; +begin + if FSSCNOn then begin + SendCmd(SSCN_OFF, SSCN_OK_REPLY); + end; +end; + +procedure TIdFTP.SetClientInfo(const AValue: TIdFTPClientIdentifier); +begin + FClientInfo.Assign(AValue); +end; + +procedure TIdFTP.SetCompressor(AValue: TIdZLibCompressorBase); +var + // under ARC, convert a weak reference to a strong reference before working with it + LCompressor: TIdZLibCompressorBase; +begin + LCompressor := FCompressor; + + if LCompressor <> AValue then begin + // under ARC, all weak references to a freed object get nil'ed automatically + + {$IFNDEF USE_OBJECT_ARC} + if Assigned(LCompressor) then begin + LCompressor.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FCompressor := AValue; + + if Assigned(AValue) then begin + {$IFNDEF USE_OBJECT_ARC} + AValue.FreeNotification(Self); + {$ENDIF} + end + else if Connected then begin + TransferMode(dmStream); + end; + end; +end; + +procedure TIdFTP.GetInternalResponse(AEncoding: IIdTextEncoding = nil); +var + LLine: string; + LResponse: TStringList; + LReplyCode: string; +begin + CheckConnected; + LResponse := TStringList.Create; + try + // Some servers with bugs send blank lines before reply. Dont remember + // which ones, but I do remember we changed this for a reason + // + // RLebeau 9/14/06: this can happen in between lines of the reply as well + + // RLebeau 3/9/09: according to RFC 959, when reading a multi-line reply, + // we are supposed to look at the first line's reply code and then keep + // reading until that specific reply code is encountered again, and + // everything in between is the text. So, do not just look for arbitrary + // 3-digit values on each line, but instead look for the specific reply + // code... + + LLine := IOHandler.ReadLnWait(MaxInt, AEncoding); + LResponse.Add(LLine); + + if CharEquals(LLine, 4, '-') then begin + LReplyCode := Copy(LLine, 1, 3); + repeat + LLine := IOHandler.ReadLnWait(MaxInt, AEncoding); + LResponse.Add(LLine); + until TIdReplyFTP(FLastCmdResult).IsEndReply(LReplyCode, LLine); + end; + + //Note that FormattedReply uses an assign in it's property set method. + FLastCmdResult.FormattedReply := LResponse; + finally + FreeAndNil(LResponse); + end; +end; + +function TIdFTP.CheckResponse(const AResponse: Int16; + const AAllowedResponses: array of Int16): Int16; +var + i: Integer; +begin + // any FTP command can return a 421 reply if the server is going to shut + // down the command connection. This way, we can close the connection + // immediately instead of waiting for a future action that would raise + // an EIdConnClosedGracefully exception instead... + + if AResponse = 421 then + begin + // check if the caller explicitally wants to handle 421 replies... + if High(AAllowedResponses) > -1 then begin + for i := Low(AAllowedResponses) to High(AAllowedResponses) do begin + if AResponse = AAllowedResponses[i] then begin + Result := AResponse; + Exit; + end; + end; + end; + Disconnect(False); + if IOHandler <> nil then begin + IOHandler.InputBuffer.Clear; + end; + RaiseExceptionForLastCmdResult; + end; + + Result := inherited CheckResponse(AResponse, AAllowedResponses); +end; + +function TIdFTP.GetReplyClass: TIdReplyClass; +begin + Result := TIdReplyFTP; +end; + +procedure TIdFTP.SetIPVersion(const AValue: TIdIPVersion); +begin + if AValue <> FIPVersion then begin + inherited SetIPVersion(AValue); + if IPVersion = Id_IPv6 then begin + UseExtensionDataPort := True; + end; + end; +end; + +class function TIdFTP.InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP; + const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean; +{ + +SiteToSiteUpload + + From: PASV To: PORT - ATargetUsesPasv = False + From: RETR To: STOR + +SiteToSiteDownload + + From: PORT To: PASV - ATargetUsesPasv = True + From: RETR To: STOR + + +To do FXP transfers with TLS FTP, you have to have one computer do the +TLS handshake as a client (ssl_connect). Thus, one of the following conditions must be meet. + +1) SSCN must be supported on one of the FTP servers + +or + +2) If IPv4 is used, the computer receiving a "PASV" command must support + CPSV. CPSV will NOT work with IPv6. + +IMAO, when doing FXP transfers, you should use SSCN whenever possible as +SSCN will support IPv6 and SSCN may be in wider use than CPSV. CPSV should +only be used as a fallback if SSCN isn't supported by both servers and IPv4 +is being used. +} +var + LIP : String; + LPort : TIdPort; +begin + Result := True; + if AFromSite.SetSSCNToOn then begin + AToSite.ClearSSCN; + end + else if AToSite.SetSSCNToOn then begin + AFromSite.ClearSSCN; + end + else if AToSite.IPVersion = Id_IPv4 then begin + if ATargetUsesPasv then begin + AToSite.SendCPassive(LIP, LPort); + AFromSite.SendPort(LIP, LPort); + end else begin + AFromSite.SendCPassive(LIP, LPort); + AToSite.SendPort(LIP, LPort); + end; + end; + FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile); +end; + +class function TIdFTP.InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP; + const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean; +{ +SiteToSiteUpload + + From: PASV To: PORT - ATargetUsesPasv = False + From: RETR To: STOR + +SiteToSiteDownload + + From: PORT To: PASV - ATargetUsesPasv = True + From: RETR To: STOR +} +begin + FXPSetTransferPorts(AFromSite, AToSite, ATargetUsesPasv); + FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile); + Result := True; +end; + +class function TIdFTP.ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP; + const ATargetUsesPasv : Boolean): Boolean; +{ +SiteToSiteUpload + + From: PASV To: PORT - ATargetUsesPasv = False + From: RETR To: STOR + +SiteToSiteDownload + + From: PORT To: PASV - ATargetUsesPasv = True + From: RETR To: STOR + +This will raise an exception if FXP can not be done. Result = True for encrypted +or False for unencrypted. + +Note: + +The following is required: + SiteToSiteUpload + Source must do P +} +begin + if ATargetUsesPasv then begin + if AToSite.UsingNATFastTrack then begin + raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack); + end; + end else begin + if AFromSite.UsingNATFastTrack then begin + raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack); + end; + end; + + if AFromSite.IPVersion <> AToSite.IPVersion then begin + raise EIdFTPStoSIPProtoMustBeSame.Create(RSFTPSToSProtosMustBeSame); + end; + if AFromSite.CurrentTransferMode <> AToSite.CurrentTransferMode then begin + raise EIdFTPSToSTransModesMustBeSame.Create(RSFTPSToSTransferModesMusbtSame); + end; + if AFromSite.FUsingSFTP <> AToSite.FUsingSFTP then begin + raise EIdFTPSToSNoDataProtection.Create(RSFTPSToSNoDataProtection); + end; + + Result := AFromSite.FUsingSFTP and AToSite.FUsingSFTP; + if Result then begin + if not (AFromSite.IsExtSupported('SSCN') or AToSite.IsExtSupported('SSCN')) then begin {do not localize} + //Second chance fallback, is CPSV supported on the server where PASV would + // be sent + if AToSite.IPVersion = Id_IPv4 then begin + if ATargetUsesPasv then begin + if not AToSite.IsExtSupported('CPSV') then begin {do not localize} + raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported); + end; + end else begin + if not AFromSite.IsExtSupported('CPSV') then begin {do not localize} + raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported); + end; + end; + end; + end; + end; +end; + +class procedure TIdFTP.FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String); +var + LDestFile : String; +begin + LDestFile := ADestFile; + if LDestFile = '' then begin + LDestFile := ASourceFile; + end; + AToSite.SendCmd('STOR ' + LDestFile, [110, 125, 150]); {do not localize} + try + AFromSite.SendCmd('RETR ' + ASourceFile, [110, 125, 150]); {do not localize} + except + AToSite.Abort; + raise; + end; + AToSite.GetInternalResponse; + AFromSite.GetInternalResponse; + AToSite.CheckResponse(AToSite.LastCmdResult.NumericCode, [225, 226, 250]); + AFromSite.CheckResponse(AFromSite.LastCmdResult.NumericCode, [225, 226, 250]); +end; + +class procedure TIdFTP.FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv: Boolean); +var + LIP : String; + LPort : TIdPort; +{ +{ +SiteToSiteUpload + + From: PASV To: PORT - ATargetUsesPasv = False + From: RETR To: STOR + +SiteToSiteDownload + + From: PORT To: PASV - ATargetUsesPasv = True + From: RETR To: STOR +} +begin + if ATargetUsesPasv then begin + if AToSite.UsingExtDataPort then begin + AToSite.SendEPassive(LIP, LPort); + end else begin + AToSite.SendPassive(LIP, LPort); + end; + if AFromSite.UsingExtDataPort then begin + AFromSite.SendEPort(LIP, LPort, AToSite.IPVersion); + end else begin + AFromSite.SendPort(LIP, LPort); + end; + end else begin + if AFromSite.UsingExtDataPort then begin + AFromSite.SendEPassive(LIP, LPort); + end else begin + AFromSite.SendPassive(LIP, LPort); + end; + if AToSite.UsingExtDataPort then begin + AToSite.SendEPort(LIP, LPort, AFromSite.IPVersion); + end else begin + AToSite.SendPort(LIP, LPort); + end; + end; +end; + +{ TIdFTPClientIdentifier } + +procedure TIdFTPClientIdentifier.Assign(Source: TPersistent); +var + LSource: TIdFTPClientIdentifier; +begin + if Source is TIdFTPClientIdentifier then begin + LSource := TIdFTPClientIdentifier(Source); + ClientName := LSource.ClientName; + ClientVersion := LSource.ClientVersion; + PlatformDescription := LSource.PlatformDescription; + end else begin + inherited Assign(Source); + end; +end; + +//assume syntax such as this: +//214 Syntax: CLNT [ ] (Set client name) +function TIdFTPClientIdentifier.GetClntOutput: String; +begin + if FClientName <> '' then begin + Result := FClientName; + if FClientVersion <> '' then begin + Result := Result + ' ' + FClientVersion; + if FPlatformDescription <> '' then begin + Result := Result + ' ' + FPlatformDescription; + end; + end; + end else begin + Result := ''; + end; +end; + +procedure TIdFTPClientIdentifier.SetClientName(const AValue: String); +begin + FClientName := Trim(AValue); + // Don't call Fetch; it prevents multi-word client names +end; + +procedure TIdFTPClientIdentifier.SetClientVersion(const AValue: String); +begin + FClientVersion := Trim(AValue); +end; + +procedure TIdFTPClientIdentifier.SetPlatformDescription(const AValue: String); +begin + FPlatformDescription := AValue; +end; + +{Note about SetTime procedures: + +The first syntax is one used by current Serv-U versions and servers that report "MDTM YYYYMMDDHHMMSS[+-TZ];filename " in their FEAT replies is: + +1) MDTM [Time in GMT format] Filename + +some Bullete Proof FTPD versions, Indy's FTP Server component, and servers reporting "MDTM YYYYMMDDHHMMSS[+-TZ] filename" in their FEAT replies uses an older Syntax which is: + +2) MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename + +and then there is the classic + +3) MDTM [local timestamp] Filename + +So for example, if I was a file dated Jan 3, 5:00:00 pm from my computer in the Eastern Standard Time (-5 hours from Universal Time), the 3 syntaxes +Indy would use are: + +Syntax 1: + +1) MDTM 0103220000 MyFile.exe (notice the 22 hour) + +Syntax 2: + +2) MDTM 0103170000-300 MyFile.exe (notice the 17 hour and the -300 offset) + +Syntax 3; + +3) MDTM 0103170000 MyFile.exe (notice the 17 hour) + +Note from: +http://www.ftpvoyager.com/releasenotes10x.asp +==== +Added support for RFC change and the MDTM. MDTM requires sending the server +GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with +Serv-U automatically by checking the Serv-U version number and by checking the +response to the FEAT command for MDTM. Servers returning "MDTM" or +"MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers +returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a +and time is GMT (UTC). +=== +} +procedure TIdFTP.SetModTime(const AFileName: String; const ALocalTime: TDateTime); +var + LCmd: String; +begin + //use MFMT instead of MDTM because that always takes the time as Universal + //time (the most accurate). + if IsExtSupported('MFMT') then begin {do not localize} + LCmd := 'MFMT ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize} + end + + //Syntax 1 - MDTM [Time in GMT format] Filename + else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize} + //we use the new method + LCmd := 'MDTM ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize} + end + + //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename + //use old method for old versions of Serv-U and BPFTP Server + else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize} + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, True) + ' ' + AFileName; {do not localize} + end + + //syntax 3 - MDTM [local timestamp] Filename + else if FTZInfo.FGMTOffsetAvailable then begin + //send it relative to the server's time-zone + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime - OffSetFromUTC + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize} + end + + else begin + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, False) + ' ' + AFileName; {do not localize} + end; + + // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213 + SendCmd(LCmd, [200, 213, 253]); +end; + +{ +Note from: +http://www.ftpvoyager.com/releasenotes10x.asp +==== +Added support for RFC change and the MDTM. MDTM requires sending the server +GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with +Serv-U automatically by checking the Serv-U version number and by checking the +response to the FEAT command for MDTM. Servers returning "MDTM" or +"MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers +returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a +and time is GMT (UTC). +=== +} +procedure TIdFTP.SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime); +var + LCmd: String; +begin + //use MFMT instead of MDTM because that always takes the time as Universal + //time (the most accurate). + if IsExtSupported('MFMT') then begin {do not localize} + LCmd := 'MFMT ' + FTPGMTDateTimeToMLS(AGMTTime) + ' ' + AFileName; {do not localize} + end + + //Syntax 1 - MDTM [Time in GMT format] Filename + else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize} + //we use the new method + LCmd := 'MDTM ' + FTPGMTDateTimeToMLS(AGMTTime, False) + ' ' + AFileName; {do not localize} + end + + //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename + //use old method for old versions of Serv-U and BPFTP Server + else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize} + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(AGMTTime + OffSetFromUTC, False, True) + ' ' + AFileName; {do not localize} + end + + //syntax 3 - MDTM [local timestamp] Filename + else if FTZInfo.FGMTOffsetAvailable then begin + //send it relative to the server's time-zone + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(AGMTTime + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize} + end + + else begin + LCmd := 'MDTM '+ FTPDateTimeToMDTMD(AGMTTime + OffSetFromUTC, False, False) + ' ' + AFileName; {do not localize} + end; + + // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213 + SendCmd(LCmd, [200, 213, 253]); +end; + +{Improvement from Tobias Giesen http://www.superflexible.com +His notation is below: + +"here's a fix for TIdFTP.IndexOfFeatLine. It does not work the +way it is used in TIdFTP.SetModTime, because it only +compares the first word of the FeatLine." } +function TIdFTP.IndexOfFeatLine(const AFeatLine: String): Integer; +var + LBuf : String; + LNoSpaces: Boolean; +begin + LNoSpaces := IndyPos(' ', AFeatLine) = 0; + for Result := 0 to FCapabilities.Count -1 do begin + LBuf := TrimLeft(FCapabilities[Result]); + // RLebeau: why Fetch() if no spaces are present? + if LNoSpaces then begin + LBuf := Fetch(LBuf); + end; + if TextIsSame(AFeatLine, LBuf) then begin + Exit; + end; + end; + Result := -1; +end; + +{ TIdFTPTZInfo } + +procedure TIdFTPTZInfo.Assign(Source: TPersistent); +var + LSource: TIdFTPTZInfo; +begin + if Source is TIdFTPTZInfo then begin + LSource := TIdFTPTZInfo(Source); + FGMTOffset := LSource.GMTOffset; + FGMTOffsetAvailable := LSource.GMTOffsetAvailable; + end else begin + inherited Assign(Source); + end; +end; + +function TIdFTP.IsSiteZONESupported: Boolean; +var + LFacts : TStrings; + i : Integer; +begin + Result := False; + if IsServerMDTZAndListTForm then begin + Result := True; + Exit; + end; + LFacts := TStringList.Create; + try + ExtractFeatFacts('SITE', LFacts); + for i := 0 to LFacts.Count-1 do begin + if TextIsSame(LFacts[i], 'ZONE') then begin {do not localize} + Result := True; + Exit; + end; + end; + finally + FreeAndNil(LFacts); + end; +end; + +procedure TIdFTP.SetTZInfo(const Value: TIdFTPTZInfo); +begin + FTZInfo.Assign(Value); +end; + +function TIdFTP.IsOldServU: Boolean; +begin + Result := TextStartsWith(FServerDesc, 'Serv-U '); {do not localize} +end; + +function TIdFTP.IsBPFTP : Boolean; +begin + Result := TextStartsWith(FServerDesc, 'BPFTP Server '); {do not localize} +end; + +function TIdFTP.IsTitan : Boolean; +begin + Result := TextStartsWith(FServerDesc, 'TitanFTP server ') or {do not localize} + TextStartsWith(FServerDesc, 'Titan FTP Server '); {do not localize} +end; + +function TIdFTP.IsWSFTP : Boolean; +begin + Result := IndyPos('WS_FTP Server', FServerDesc) > 0; {do not localize} +end; + +function TIdFTP.IsIIS: Boolean; +begin + Result := TextStartsWith(FServerDesc, 'Microsoft FTP Service'); {do not localize} +end; +function TIdFTP.IsServerMDTZAndListTForm: Boolean; +begin + Result := IsOldServU or IsBPFTP or IsTitan; +end; + +procedure TIdFTP.Notification(AComponent: TComponent; Operation: TOperation); +begin + if (Operation = opRemove) and (AComponent = FCompressor) then begin + SetCompressor(nil); + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdFTP.SendPret(const ACommand: String); +begin + if IsExtSupported('PRET') then begin {do not localize} + //note that we don't check for success or failure here + //as some servers might fail and then succede with the transfer. + //Pret might not work for some commands. + SendCmd('PRET ' + ACommand); {do not localize} + end; +end; + +procedure TIdFTP.List; +begin + List(nil); +end; + +procedure TIdFTP.List(const ASpecifier: string; ADetails: Boolean); +begin + List(nil, ASpecifier, ADetails); +end; + +procedure TIdFTP.DoOnBannerAfterLogin(AText: TStrings); +begin + if Assigned(OnBannerAfterLogin) then begin + OnBannerAfterLogin(Self, AText.Text); + end; +end; + +procedure TIdFTP.DoOnBannerBeforeLogin(AText: TStrings); +begin + if Assigned(OnBannerBeforeLogin) then begin + OnBannerBeforeLogin(Self, AText.Text); + end; +end; + +procedure TIdFTP.DoOnBannerWarning(AText: TStrings); +begin + if Assigned(OnBannerWarning) then begin + OnBannerWarning(Self, AText.Text); + end; +end; + +procedure TIdFTP.SetDataPortProtection(AValue: TIdFTPDataPortSecurity); +begin + if IsLoading then begin + FDataPortProtection := AValue; + Exit; + end; + if FDataPortProtection <> AValue then begin + if FUseTLS = utNoTLSSupport then begin + raise EIdFTPNoDataPortProtectionWOEncryption.Create(RSFTPNoDataPortProtectionWOEncryption); + end; + if FUsingCCC then begin + raise EIdFTPNoDataPortProtectionAfterCCC.Create(RSFTPNoDataPortProtectionAfterCCC); + end; + FDataPortProtection := AValue; + end; +end; + +procedure TIdFTP.SetAUTHCmd(const AValue : TAuthCmd); +begin + if IsLoading then begin + FAUTHCmd := AValue; + Exit; + end; + if FAUTHCmd <> AValue then begin + if FUseTLS = utNoTLSSupport then begin + raise EIdFTPNoAUTHWOSSL.Create(RSFTPNoAUTHWOSSL); + end; + if FUsingSFTP then begin + raise EIdFTPCanNotSetAUTHCon.Create(RSFTPNoAUTHCon); + end; + FAUTHCmd := AValue; + end; +end; + +procedure TIdFTP.SetDefStringEncoding(AValue: IIdTextEncoding); +begin + FDefStringEncoding := AValue; + if IOHandler <> nil then begin + IOHandler.DefStringEncoding := FDefStringEncoding; + end; +end; + +procedure TIdFTP.SetUseTLS(AValue: TIdUseTLS); +begin + inherited SetUseTLS(AValue); + if IsLoading then begin + Exit; + end; + if AValue = utNoTLSSupport then begin + FDataPortProtection := Id_TIdFTP_DataPortProtection; + FUseCCC := DEF_Id_FTP_UseCCC; + FAUTHCmd := DEF_Id_FTP_AUTH_CMD; + end; +end; + +procedure TIdFTP.SetUseCCC(const AValue: Boolean); +begin + if (not IsLoading) and (FUseTLS = utNoTLSSupport) then begin + raise EIdFTPNoCCCWOEncryption.Create(RSFTPNoCCCWOEncryption); + end; + FUseCCC := AValue; +end; + +procedure TIdFTP.DoOnRetrievedDir; +begin + if Assigned(OnRetrievedDir) then begin + OnRetrievedDir(Self); + end; +end; + +procedure TIdFTP.DoOnDirParseEnd; +begin + if Assigned(FOnDirParseEnd) then begin + FOnDirParseEnd(Self); + end; +end; + +procedure TIdFTP.DoOnDirParseStart; +begin + if Assigned(FOnDirParseStart) then begin + FOnDirParseStart(Self); + end; +end; + +//we do this to match some WS-FTP Pro firescripts I saw +function TIdFTP.IsAccountNeeded: Boolean; +begin + Result := LastCmdResult.NumericCode = 332; + if not Result then begin + if IndyPos('ACCOUNT', LastCmdResult.Text.Text) > 0 then begin {do not localize} + Result := FAccount <> ''; + end; + end; +end; + +//we can use one of three commands for verifying a file or stream +function TIdFTP.GetSupportsVerification: Boolean; +begin + Result := Connected; + if Result then begin + Result := TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512'); + if not Result then begin + Result := TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256'); + end; + if not Result then begin + Result := IsExtSupported('XSHA1') or + (IsExtSupported('XMD5') and (not GetFIPSMode)) or + IsExtSupported('XCRC'); + end; + end; +end; + +function TIdFTP.VerifyFile(const ALocalFile, ARemoteFile: String; const AStartPoint, AByteCount: TIdStreamSize): Boolean; +var + LLocalStream: TStream; + LRemoteFileName : String; +begin + LRemoteFileName := ARemoteFile; + if LRemoteFileName = '' then begin + LRemoteFileName := ExtractFileName(ALocalFile); + end; + LLocalStream := TIdReadFileExclusiveStream.Create(ALocalFile); + try + Result := VerifyFile(LLocalStream, LRemoteFileName, AStartPoint, AByteCount); + finally + FreeAndNil(LLocalStream); + end; +end; + +{ +This procedure can use three possible commands to verify file integriety and the +syntax does very amoung these. The commands are: + +XSHA1 - get SHA1 checksum for a file or file part +XMD5 - get MD5 checksum for a file or file part +XCRC - get CRC32 checksum + +The command preference is from first to last (going from longest length to shortest). +} +function TIdFTP.VerifyFile(ALocalFile: TStream; const ARemoteFile: String; + const AStartPoint, AByteCount: TIdStreamSize): Boolean; +var + LRemoteCRC : String; + LLocalCRC : String; + LCmd : String; + LRemoteFile: String; + LStartPoint : TIdStreamSize; + LByteCount : TIdStreamSize; //used instead of AByteCount so we don't exceed the file size + LHashClass: TIdHashClass; + LHash: TIdHash; +begin + LLocalCRC := ''; + LRemoteCRC := ''; + + if AStartPoint > -1 then begin + ALocalFile.Position := AStartPoint; + end; + + LStartPoint := ALocalFile.Position; + LByteCount := ALocalFile.Size - LStartPoint; + + if (LByteCount > AByteCount) and (AByteCount > 0) then begin + LByteCount := AByteCount; + end; + + //just in case the server doesn't support file names in quotes. + if IndyPos(' ', ARemoteFile) > 0 then begin + LRemoteFile := '"' + ARemoteFile + '"'; + end else begin + LRemoteFile := ARemoteFile; + end; + + if TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512') then begin + //XSHA256 pathname [ startposition endposition] + LCmd := 'XSHA512 ' + LRemoteFile; + if AByteCount > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount); + end + else if AStartPoint > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint); + end; + LHashClass := TIdHashSHA512; + end + else if TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256') then begin + //XSHA256 pathname [ startposition endposition] + LCmd := 'XSHA256 ' + LRemoteFile; + if AByteCount > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount); + end + else if AStartPoint > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint); + end; + LHashClass := TIdHashSHA256; + end + else if IsExtSupported('XSHA1') then begin + //XMD5 "filename" startpos endpos + //I think there's two syntaxes to this: + // + //Raiden Syntax if FEAT line contains " XMD5 filename;start;end" + // + //or what's used by some other servers if "FEAT line contains XMD5" + // + //XCRC "filename" [startpos] [number of bytes to calc] + + if IndexOfFeatLine('XSHA1 filename;start;end') > -1 then begin + LCmd := 'XSHA1 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1); + end else + begin + //BlackMoon FTP Server uses this one. + LCmd := 'XSHA1 ' + LRemoteFile; + if AByteCount > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount); + end + else if AStartPoint > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint); + end; + end; + LHashClass := TIdHashSHA1; + end + else if IsExtSupported('XMD5') and (not GetFIPSMode) then begin + //XMD5 "filename" startpos endpos + //I think there's two syntaxes to this: + // + //Raiden Syntax if FEAT line contains " XMD5 filename;start;end" + // + //or what's used by some other servers if "FEAT line contains XMD5" + // + //XCRC "filename" [startpos] [number of bytes to calc] + + if IndexOfFeatLine('XMD5 filename;start;end') > -1 then begin + LCmd := 'XMD5 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1); + end else + begin + //BlackMoon FTP Server uses this one. + LCmd := 'XMD5 ' + LRemoteFile; + if AByteCount > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount); + end + else if AStartPoint > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint); + end; + end; + LHashClass := TIdHashMessageDigest5; + end else + begin + LCmd := 'XCRC ' + LRemoteFile; + if AByteCount > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount); + end + else if AStartPoint > 0 then begin + LCmd := LCmd + ' ' + IntToStr(LStartPoint); + end; + LHashClass := TIdHashCRC32; + end; + + LHash := LHashClass.Create; + try + LLocalCRC := LHash.HashStreamAsHex(ALocalFile, LStartPoint, LByteCount); + finally + LHash.Free; + end; + + if SendCmd(LCmd) = 250 then begin + LRemoteCRC := Trim(LastCmdResult.Text.Text); + IdDelete(LRemoteCRC, 1, IndyPos(' ', LRemoteCRC)); // delete the response + Result := TextIsSame(LLocalCRC, LRemoteCRC); + end else begin + Result := False; + end; +end; + +end. + + diff --git a/indy/Protocols/IdFTPBaseFileSystem.pas b/indy/Protocols/IdFTPBaseFileSystem.pas new file mode 100644 index 0000000..b2f4426 --- /dev/null +++ b/indy/Protocols/IdFTPBaseFileSystem.pas @@ -0,0 +1,152 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 8/24/2003 06:49:54 PM JPMugaas + API Change in the FileSystem component so that a thread is passed instead of + some data from the thread. This should also make the API's easier to manage + than before and provide more flexibility for developers writing their own + file system components. + + Rev 1.7 3/10/2003 05:09:10 PM JPMugaas + MLST now works as expected with the file system. Note that the MLST means + simply to give information about an item instead of its contents. + GetRealFileName in IdFTPFileSystem now can accept the wildcard *. + When doing dirs in EPLF, only information about a directory is retruned if it + is specified. + + Rev 1.6 3/6/2003 10:59:58 AM JPMugaas + Now handles the MFMT command and the MFCT (Modified Date fact) command. + + Rev 1.5 3/6/2003 08:26:20 AM JPMugaas + Bug fixes. + + FTP COMB command can now work in the FTPFileSystem component. + + Rev 1.4 3/5/2003 03:28:06 PM JPMugaas + MD5, MMD5, and XCRC are now supported in the Virtual File System. + + Rev 1.3 3/2/2003 04:54:26 PM JPMugaas + Now does recursive dir lists with the Virtual File System layer as well as + honors other switches. + + Rev 1.2 3/2/2003 02:20:24 PM JPMugaas + Updated FTP File system. It now raises exceptions for errors plus load and + save have been implemented. I also implemented RMDIR. + + Rev 1.1 3/2/2003 02:20:12 AM JPMugaas + Updated with some enw functionality. + + Rev 1.0 11/13/2002 08:28:28 AM JPMugaas + Initial import from FTP VC. +} + +{*===========================================================================*} +{* DESCRIPTION *} +{*****************************************************************************} +{* PROJECT : Indy 10 *} +{* AUTHOR : Bas Gooijen *} +{* MAINTAINER : Bas Gooijen *} +{*...........................................................................*} +{* DESCRIPTION *} +{* *} +{* Abstract base class for TIdFTPFileSystem *} +{* *} +{*...........................................................................*} +{* HISTORY *} +{* DATE VERSION AUTHOR REASONS *} +{* *} +{* 01/10/2002 1.0 Bas Gooijen Initial start *} +{*****************************************************************************} + +unit IdFTPBaseFileSystem; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdBaseComponent, + IdException, + IdFTPList, + IdFTPListOutput, + IdFTPServerContextBase; + +type + TIdFTPBaseFileSystem = class(TIdBaseComponent) + protected + procedure ErrPermissionDenied; + procedure ErrCantRemoveDir; + procedure ErrFileNotFound; + procedure ErrNotAFile; + procedure ErrNotADir; + public + procedure ChangeDir(AContext : TIdFTPServerContextBase; var VDirectory: TIdFTPFileName); virtual; abstract; + procedure GetFileSize(AContext : TIdFTPServerContextBase; const AFilename: TIdFTPFileName; var VFileSize: Int64); virtual; abstract; + procedure GetFileDate(AContext : TIdFTPServerContextBase; const AFilename: TIdFTPFileName; var VFileDate: TDateTime); virtual; abstract; + procedure ListDirectory(AContext : TIdFTPServerContextBase; const APath: TIdFTPFileName; ADirectoryListing: TIdFTPListOutput; const ACmd, ASwitches : String); virtual; abstract; + procedure RenameFile(AContext : TIdFTPServerContextBase; const ARenameToFile: TIdFTPFileName); virtual; abstract; + procedure DeleteFile(AContext : TIdFTPServerContextBase; const APathName: TIdFTPFileName); virtual; abstract; + procedure RetrieveFile(AContext : TIdFTPServerContextBase; const AFileName: TIdFTPFileName; var VStream: TStream); virtual; abstract; + procedure StoreFile(AContext : TIdFTPServerContextBase; const AFileName: TIdFTPFileName; AAppend: Boolean; var VStream: TStream); virtual; abstract; + procedure MakeDirectory(AContext : TIdFTPServerContextBase; var VDirectory: TIdFTPFileName); virtual; abstract; + procedure RemoveDirectory(AContext : TIdFTPServerContextBase; var VDirectory: TIdFTPFileName); virtual; abstract; + procedure SetModifiedFileDate(AContext : TIdFTPServerContextBase; const AFileName: TIdFTPFileName; var VDateTime: TDateTime); virtual; abstract; + procedure GetCRCCalcStream(AContext : TIdFTPServerContextBase; const AFileName: TIdFTPFileName; var VStream : TStream); virtual; abstract; + procedure CombineFiles(AContext : TIdFTPServerContextBase; + const ATargetFileName: TIdFTPFileName; AParts: TStrings); virtual; abstract; + + end; + EIdFileSystemException = class(EIdException); + EIdFileSystemPermissionDenied = class(EIdFileSystemException); + EIdFileSystemFileNotFound = class(EIdFileSystemException); + EIdFileSystemNotAFile = class(EIdFileSystemException); + EIdFileSystemNotADir = class(EIdFileSystemException); + EIdFileSystemCannotRemoveDir = class(EIdFileSystemException); + +implementation +uses IdResourceStringsProtocols; +{ TIdFTPBaseFileSystem } + +procedure TIdFTPBaseFileSystem.ErrCantRemoveDir; +begin + raise EIdFileSystemCannotRemoveDir.Create(RSFTPFSysErrMsg); +end; + +procedure TIdFTPBaseFileSystem.ErrFileNotFound; +begin + raise EIdFileSystemFileNotFound.Create(RSFTPFSysErrMsg); +end; + +procedure TIdFTPBaseFileSystem.ErrNotADir; +begin + raise EIdFileSystemNotADir.Create(RSFTPFSysErrMsg); +end; + +procedure TIdFTPBaseFileSystem.ErrNotAFile; +begin + raise EIdFileSystemNotAFile.Create(RSFTPFSysErrMsg); +end; + +procedure TIdFTPBaseFileSystem.ErrPermissionDenied; +begin + raise EIdFileSystemPermissionDenied.Create(RSFTPFSysErrMsg); +end; + +end. + + diff --git a/indy/Protocols/IdFTPCommon.pas b/indy/Protocols/IdFTPCommon.pas new file mode 100644 index 0000000..e197893 --- /dev/null +++ b/indy/Protocols/IdFTPCommon.pas @@ -0,0 +1,2475 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.40 3/3/2005 10:12:38 AM JPMugaas + Fix for compiler warning about DotNET and ByteType. + + Rev 1.39 12/8/2004 10:38:40 AM JPMugaas + Adjustment for PC-NFS. Time is returned with an "a" or "p" instead of AM or + PM. + + Rev 1.38 11/24/2004 12:26:18 PM JPMugaas + Removed dead code that caused a NET portability warning. + + Rev 1.37 11/22/2004 7:44:26 PM JPMugaas + Modified IsYYMMDD to accept 2 digit years. + + Rev 1.35 10/27/2004 1:05:08 AM JPMugaas + "SungDong Kim" indicated a problem with Korean in + IsTotalLine. He suggested specifically testing for multibyte characters. + + This is tentative. + + Rev 1.34 10/26/2004 9:19:12 PM JPMugaas + Fixed references. + + Rev 1.33 9/7/2004 10:01:12 AM JPMugaas + FIxed problem parsing: + + drwx------ 1 user group 0 Sep 07 09:20 xxx + + It was mistakenly being detected as Windows NT because there was a - in the + fifth and eigth position in the string. The fix is to detect to see if the + other chactors in thbat column are numbers. + + I did the same thing to the another part of the detection so that something + similar doesn't happen there with "-" in Unix listings causing false + WindowsNT detection. + + Rev 1.32 8/1/2004 1:07:36 AM JPMugaas + Fix for XBox dir listing problem seen in Unix-xbox-MediaCenter.txt + + Rev 1.31 7/30/2004 5:50:54 AM JPMugaas + Fix for UnquotedChar. It was returning nothing instead of what the string + without quotes. + + Rev 1.30 7/29/2004 1:33:08 AM JPMugaas + Reordered AUTH command values for a new property under development. This + should make things more logical. + + Rev 1.29 6/29/2004 4:09:02 PM JPMugaas + OPTS MODE Z now supported as per draft-preston-ftpext-deflate-02.txt. This + should keep FTP Voyager 11 happy. + + Rev 1.28 6/17/2004 3:38:42 PM JPMugaas + Removed Transfer Mode's dmBlock and dmCompressed since we did not support + those at all. + + Rev 1.27 6/15/2004 7:18:58 PM JPMugaas + Compiler defines removed. + + Rev 1.26 6/15/2004 6:35:30 PM JPMugaas + Change in ZLib parameter values. Window Bits is now positive. We make it + negative as part of a workaround and then upload with the ZLib headers. + + Rev 1.25 6/7/2004 3:47:50 PM JPMugaas + VMS Recursive Dir listings now supported. This is done with a [...]. Note + that VMS does have some strange syntaxes with their file system. + + Rev 1.24 6/5/2004 7:39:58 AM JPMugaas + Exposes Posix constants because I need them for something else in my private + work. + + Rev 1.23 6/4/2004 4:15:42 PM JPMugaas + A ChModNumber conversion function wasn't returning anything. + Added an overloaded function for cases where all of the permissions should be + in one string (such as displaying in a ListView column). + + Rev 1.22 2/17/2004 12:25:38 PM JPMugaas + The client now supports MODE Z (deflate) uploads and downloads as specified + by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + + Rev 1.21 2/12/2004 11:34:26 PM JPMugaas + FTP Deflate preliminary support. Work still needs to be done for upload and + downloading. + + Rev 1.20 2004.02.03 5:44:42 PM czhower + Name changes + + Rev 1.19 2004.02.03 2:12:08 PM czhower + $I path change + + Rev 1.18 2004.01.23 2:37:24 AM czhower + DCCIL compile fix. + + Rev 1.17 2004.01.22 5:27:24 PM czhower + Fixed compile errors. + + Rev 1.16 1/22/2004 4:16:46 PM SPerry + fixed set problems + + Rev 1.15 1/19/2004 8:57:20 PM JPMugaas + Rearranged functions to be in a more sensible way. + + Rev 1.14 1/19/2004 4:35:30 AM JPMugaas + FTPDateTimeToMDTMD was created for converting a TDateTime into a time value + for MDTM. + MinutesFromGMT was moved from IdFTPServer because the client now may use it. + + Rev 1.13 1/17/2004 7:37:32 PM JPMugaas + Removed some warnings. + + Rev 1.12 1/16/2004 12:23:52 AM JPMugaas + New functions for MDTM set date functionality. + + Rev 1.11 10/26/2003 9:18:10 PM BGooijen + Compiles in DotNet, and partially works there + + Rev 1.10 10/19/2003 1:11:06 PM DSiders + Added localization comments. + + Rev 1.9 10/7/2003 05:46:34 AM JPMugaas + SSCN Support added. + + Rev 1.8 10/1/2003 05:29:50 PM JPMugaas + Y2KDate will now adjust date if there's 3 diigits instead of 4. This is + required for the OS/2 FTP LIST parser. + + Rev 1.7 10/1/2003 12:57:12 AM JPMugaas + Routines for Sterling Commerce FTP Server support. + + Rev 1.6 6/27/2003 06:06:50 AM JPMugaas + Should now compile with the IsNumeric code move. + + Rev 1.5 3/12/2003 03:22:32 PM JPMugaas + The FTP Server can now handle masks better including file extensions. + + Rev 1.4 2/24/2003 07:19:32 AM JPMugaas + Added routine for determining if a Unix file is "hidden". This is determined + by a "." starting a filename. + + Rev 1.3 2/19/2003 02:04:24 AM JPMugaas + Added more routines from IdFTPList for the new framework. + + Rev 1.2 2/17/2003 04:43:38 PM JPMugaas + TOPS20 support + + Rev 1.1 2/14/2003 05:41:36 PM JPMugaas + Moved everything from IdFTPUtils to IdFTPCommon at Kudzu's suggestion. + + Rev 1.0 11/13/2002 08:28:38 AM JPMugaas + Initial import from FTP VC. +} + +unit IdFTPCommon; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdGlobalProtocols, + SysUtils; + +type + TIdFTPTransferType = (ftASCII, ftBinary); + TIdFTPDataStructure = (dsFile, dsRecord, dsPage); + //dmBlock, dmCompressed were removed because we don't use them and they aren't supported on most + //FTP Servers anyway. + TIdFTPTransferMode = (dmStream, dmDeflate); // (dmBlock, dmCompressed, dmStream, dmDeflate); + {Note that some FTP extensions might use some data port protection values that + are defined but not used. For memoment, I commented those out. Leave the comments + in just in case someone may need those later } + TIdFTPDataPortSecurity = ( ftpdpsClear, //'C' - Clear - neither Integrity nor Privacy + + //NOT USED - 'S' - Safe - Integrity without Privacy + //NOT USED - 'E' - Confidential - Privacy without Integrity + ftpdpsPrivate //'P' - Private - Integrity and Privacy + ); + +{From: + http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad +} +const + TLS_AUTH_NAMES : Array [0..3] of string = + ('TLS', {implies clear data port in some implementations} {Do not translate} + 'SSL', {implies private data port in some implementations} {Do not translate} + 'TLS-C', {implies clear data port in some implementations} {Do not translate} + 'TLS-P'); {implies private data port in some implementations} {Do not translate} + +{ +We hard-code these path specifiers because they are used for specific servers +irregardless of what the client's Operating system is. It's based on the server. +} + +const + // based on http://www.raidenftpd.com/kb/kb000000037.htm + // entry in FEAT response indicating SSCN is supported + SCCN_FEAT = 'SSCN'; {do not localize} + + // client method - SSL Connect + // turn on SSCN client method in FTP Server - secure server-to-server transfer + SSCN_ON = 'SSCN ON'; {do not localize} + //server mthod - SSL Accept method + // turn off SSCN client method in FTP Server - secure server-to-server transfer + SSCN_OFF = 'SSCN OFF'; {do not localize} + + SSCN_OK_REPLY = 200; + SSCN_ERR_NEGOTIATION_REPLY = 421; + +{ + VMS Stuff from http://www.djesys.com/vms/freevms/mentor/vms_path.html + + Path/filename separators, which could be different from path/subpath separators on + some systems + } +const + PATH_FILENAME_SEP_UNIX = '/'; + PATH_FILENAME_SEP_DOS = '\'; + PATH_FILENAME_SEP_VMS = ']'; + +{dir/subdir separators} +const + PATH_SUBDIR_SEP_UNIX = PATH_FILENAME_SEP_UNIX; + PATH_SUBDIR_SEP_DOS = PATH_FILENAME_SEP_DOS; + PATH_SUBDIR_SEP_VMS = '.'; + +{device/dir separator} +const + PATH_DEVICE_SEP_UNIX = ''; //Unix treats devices as part of one big hierarchy as part of the file system - leave emtpy + PATH_DEVICE_SEP_DOS = ':'; + PATH_DEVICE_SEP_VMS = ':['; + +{ + sample VMS fully qualified filename: + + DKA0:[MYDIR.SUBDIR1.SUBDIR2]MYFILE.TXT;1 + + Note VMS uses 39 chars for name and type + + valid chars are: + letters A through Z + numbers 0 through 9 + underscore ( _ ) + hyphen ( -) + dollar sign ( $ ) + + See: http://www.uh.edu/infotech/services/documentation/vms/v0505.html +} + +{ global file specification for all files } + + UNIX_ALL_FILES = '*'; + MS_DOS_ALL_FILES = '*.*'; + VMS_ALL_FILES = '*.*;*'; + + CUR_DIR = '.'; + PARENT_DIR = '..'; + + VMS_RELPATH_PREFIX = '[.'; + + MS_DOS_CURDIR = CUR_DIR + PATH_FILENAME_SEP_DOS; + UNIX_CURDIR = CUR_DIR + PATH_FILENAME_SEP_UNIX; + + UNIX_DIR_SIZE = 512; + + VMS_BLOCK_SIZE = 512; + + //1/1/1970 - EPL time stamps are based on this value +const + EPLF_BASE_DATE = 25569; + +const + //Settings specified by + // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + { + DEF_ZLIB_COMP_LEVEL = 7; + DEF_ZLIB_WINDOW_BITS = -15; //-15 - no extra headers + DEF_ZLIB_MEM_LEVEL = 8; + DEF_ZLIB_STRATAGY = 0; // - default + } + { + Settings specified by + //http://www1.ietf.org/internet-drafts/draft-preston-ftpext-deflate-02.txt + //and for some compatibility with one version of Noisette Software Corporation's ShareIt + //FTP Server + } + DEF_ZLIB_COMP_LEVEL = 7; + DEF_ZLIB_WINDOW_BITS = 15; //-15 - no extra headers + DEF_ZLIB_MEM_LEVEL = 8; // Z_DEFLATED + DEF_ZLIB_STRATAGY = 0; //Z_DEFAULT_STRATEGY - default + DEF_ZLIB_METHOD = 8; // Z_DEFLATED + +type + TIdVSEPQDisposition = ( + IdPQAppendable, + IdPQProcessAndDelete, + IdPQHoldUntilReleased, + IdPQProcessAndKeep, + IdPQLeaveUntilReleased, + IdPQErrorHoldUntilDK, + IdPQGetOrErrorHoldUntilDK, + IdPQJobProcessing, + IdPQSpoolOutputToInputD, + IdPQSurpressOutputSpooling, + IdPQSpoolOutputToTape); + + +const + VSERootDirItemTypes : array [0..5] of String = + ('', {do not localize} // treat as dir + '', {do not localize} // treat as dir + '', {do not localize} // treat as dir + '', {do not localize} // treat as dir + '', {do not localize} // treat as dir + 'Entry Seq VSAM'); {do not localize} // treat as file + + {From: http://groups.google.com/groups?q=MVS+JES+FTP+DIR+Output&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=4qf4b8%246i7%40dsk92.itg.ti.com&rnum=1} + MVS_JES_Status : array [0..3] of string = + ('INPUT', {do not localize} //job received but not run yet + 'HELD', {do not localize} //job is in hold status + 'ACTIVE', {do not localize} //job is running + 'OUTPUT'); {do not localize} //job has finished and has output available + + { Note from stame article: + + To retrieve the entire job issue the GET command with the .x: + + get j26494.x f:/job26494 + + To retrieve only the third output file of your job: + + get j26494.3 f:job26494.3 + + } + + { From: + + http://publibz.boulder.ibm.com:80/cgi-bin/bookmgr_OS390/BOOKS/IESPME20/A.0?DT=20010927093004#HDRDISPX + + } + VSE_PowerQueue_Dispositions : array [1..11] of char = ( + 'A', {do not localize} // (Local only) Appendable. Spool data may be added to the job via spool-access support. + 'D', {do not localize} // Process the job and delete it after processing. Default disposition. + 'H', {do not localize} // Hold in queue until released. + 'K', {do not localize} // Process the job and keep it in the queue after processing. (Default disposition for time event scheduling jobs that have to be processed more than once.) + 'L', {do not localize} // Leave in queue until released. + 'X', {do not localize} // (Local only) Hold until disposition is changed to D or K. Temporarily assigned by VSE/POWER when processing fails. + 'Y', {do not localize} { + (Local only) Hold until the disposition is changed to D or K. Applies only to + output being retrieved via the GET service of the spool-access support. + Assigned by VSE/POWER either on request by the retrieving program or, to + certain queue entries, when processing fails. + + Output queue entries may have also been set to a disposition of Y when + ignored records were found and SET IGNREC=DISPY was specified in the + VSE/POWER autostart procedure. + } + '*', {do not localize} // Indicates that a queue entry is being processed. + { + The following local disposition codes may be specified for an output entry, + but they are effective only while the entry is being created. + } + 'I', {do not localize} //Spool this output to the input (reader) queue with disposition D. Applies to punch output. + 'N', {do not localize} //Suppress the spooling of the referenced output when the job entry is being processed. + 'T' {do not localize} //Spool the referenced output to tape. Applies to output. +{ +If a queue entry has a temporary local disposition of A, or X, or Y, VSE/POWER +present the original disposition in the ORGDP=field of a PDISPLAY...,FULL=YES +request. +} + ); + +{TODO: Add method to TIdFTP to set dispositions for VSE Power Queue jobs if possible. + +I think it is done with a PALTER DISP=[disposition code] command but I'm not sure. + +} + +const + UnitreeStoreTypes : array [0..1] of string = + ('AR', 'DK'); {do not localize} + +const + UNIX_LINKTO_SYM = ' -> '; {do not localize} //indicates where a symbolic link points to + CDATE_PART_SEP = '/-'; {Do not localize} + +{*** +Path conversions +***} +function UnixPathToDOSPath(const APath : String):String; +function DOSPathToUnixPath(const APath : String):String; + +{*** +Indy path utility functions +***} +//works like ExtractFilePath except that it will use both "/" and "\" and the last path spec is dropped +function IndyGetFilePath(const AFileName : String):String; +function IndyGetFileName(const AFileName : String):String; +function IndyIsRelativePath(const APathName : String): Boolean; +function IndyGetFileExt(const AFileName : String) : String; +function StripInitPathDelim(const AStr : String): String; +function IsNavPath(const APath : String): Boolean; +function RemoveDuplicatePathSyms(APath : String): String; + +{*** +EPLF time stamp processing +***} +function EPLFDateToLocalDateTime(const AData: String): TDateTIme; +function EPLFDateToGMTDateTime(const AData: String): TDateTime; +function GMTDateTimeToEPLFDate(const ADateTime : TDateTime) : String; +function LocalDateTimeToEPLFDate(const ADateTime : TDateTime) : String; + +{*** +Misc parsing +***} +function PatternsInStr(const ASearchPattern, AString : String): Integer; +function StripSpaces(const AString : String; const ASpaces : UInt32): String; +function StripPath(const AFileName : String; const APathDelim : String = '/'): String; +function CharsInStr(const ASearchChar : Char; const AString : String) : Integer; +function UnfoldLines(const AData : String; ALine : Integer; AStrings : TStrings): String; +function StrPart(var AInput: string; const AMaxLength : Integer; const ADelete: Boolean = IdFetchDeleteDefault) : String; +function FetchLength(var AInput: string; + const AMaxLength : Integer; + const ADelim: string = IdFetchDelimDefault; + const ADelete: Boolean = IdFetchDeleteDefault; + const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String; +function IsLineStr(const AData : String): Boolean; + +{FTP Pattern recognition} +function IsTotalLine(const AData: String): Boolean; +function IsSubDirContentsBanner(const AData: String): Boolean; + +{*** +Quoted strings +***} +procedure ParseQuotedArgs(const AParams : String; AStrings : TStrings); + +{** +Number extraction +**} +function FindDelimInNumbers(const AData : String) : String; +function ExtractNumber(const AData : String; const ARetZero : Boolean = True): Integer; +function StripNo(const AData : String): String; + +{** +Date parsing and processing +**} +function IsValidTimeStamp(const AString : String) : Boolean; +function IsMDTMDate(const ADate : String) : Boolean; +function IsDDMonthYY(const AData : String; const ADelim : String) : Boolean; +function IsMMDDYY(const AData : String; const ADelim : String) : Boolean; +function IsYYYYMMDD(const AData : String) : Boolean; +function Y2Year(const AYear : Integer): Integer; +function DateYYMMDD(const AData: String): TDateTime; +function DateYYStrMonthDD(const AData: String; const ADelim : String='-'): TDateTime; +function DateStrMonthDDYY(const AData:String; const ADelim : String = '-'; const AAddMissingYear : Boolean=False): TDateTime; +function DateDDStrMonthYY(const AData: String; const ADelim : String='-'): TDateTime; +function DateMMDDYY(const AData: String): TDateTime; +function TimeHHMMSS(const AData : String):TDateTime; +function IsIn6MonthWindow(const AMDate : TDateTime):Boolean; +function AddMissingYear(const ADay, AMonth : UInt32): UInt32; +function IsHHMMSS(const AData : String; const ADelim : String) : Boolean; +//This assumes hours in the form 0-23 instead of the 12 AM/PM hour system used in the US. +function MVSDate(const AData: String): TDateTime; +function AS400Date(const AData: String): TDateTime; + +//MDTM Set filedate support and SITE ZONE support +function MinutesFromGMT : Integer; +function MDTMOffset(const AOffs : String) : TDateTime; +function FTPDateTimeToMDTMD(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True; const AIncludeGMTOffset : Boolean=True ): String; +function FTPMDTMToGMTDateTime(const ATimeStamp : String):TDateTime; + + +{*** +platform specific parsing and testing +***} + +{Unix} +function IsValidUnixPerms(AData : String; const AStrict : Boolean = False) : Boolean; +function IsUnixLsErr(const AData: String): Boolean; +function IsUnixExec(const LUPer, LGPer, LOPer : String): Boolean; +function IsUnixHiddenFile(const AFileName : String): Boolean; + + //Chmod converstion routines +procedure ChmodNoToPerms(const AChmodNo : Integer; var VUser, VGroup, VOther : String); overload; +procedure ChmodNoToPerms(const AChmodNo : Integer; var VPermissions : String); overload; +function PermsToChmodNo(const AUser, AGroup, AOther : String): Integer; + +function ChmodNoToModeBits(const AModVal : UInt32): UInt32; +function ModeBitsToChmodNo(const AMode : UInt32): Integer; + +function ModeBitsToPermString(const AMode : UInt32) : String; +function PermStringToModeBits(const APerms : String): UInt32; + +{Novell Netware} +function IsNovelPSPattern(const AStr : String): Boolean; +function IsValidNovellPermissionStr(const AStr : String): Boolean; +function ExtractNovellPerms(const AData : String) : String; + +{QVT/NET} +function ExcludeQVNET(const AData : String) : Boolean; +function ExtractQVNETFileName(const AData : String): String; + +{Mainframe support} +function ExtractRecFormat(const ARecFM : String): String; +//Determines if the line is part of a VM/BFS list - also used by WindowsNT parser +//because two columns are shared +function IsVMBFS(AData : String) : Boolean; +{IBM VSE} +function DispositionCodeToTIdVSEPQDisposition(const ADisp : Char) : TIdVSEPQDisposition; +function TIdVSEPQDispositionDispositionCode(const ADisp : TIdVSEPQDisposition) : Char; + +{EPLF and MLST/MLSD support} +function ParseFacts(AData : String; AResults : TStrings; + const AFactDelim : String = ';'; const ANameDelim : String=' '): String; +function ParseFactsMLS(AData : String; AResults : TStrings; + const AFactDelim : String = ';'; const ANameDelim : String = ' '): String; +{Sterling Commerce support routines} + +function IsValidSterCommFlags(const AString : String) : Boolean; +function IsValidSterCommProt(const AString : String) : Boolean; +function IsValidSterCommData(const AString : String) : Boolean; + +//These are from Borland's LIBC.pas header file +//We rename the constants to prevent any conflicts in Kylix and C++ + +const + Id__S_ISUID = $800; { Set user ID on execution. } + Id__S_ISGID = $400; { Set group ID on execution. } + Id__S_ISVTX = $200; { Save swapped text after use (sticky). } + Id__S_IREAD = $100; { Read by owner. } + Id__S_IWRITE = $80; { Write by owner. } + Id__S_IEXEC = $40; { Execute by owner. } + + { Protection bits. } + + IdS_ISUID = Id__S_ISUID; { Set user ID on execution. } + IdS_ISGID = Id__S_ISGID; { Set group ID on execution. } + + { Save swapped text after use (sticky bit). This is pretty well obsolete. } + IdS_ISVTX = Id__S_ISVTX; + + IdS_IRUSR = Id__S_IREAD; { Read by owner. } + IdS_IWUSR = Id__S_IWRITE; { Write by owner. } + IdS_IXUSR = Id__S_IEXEC; { Execute by owner. } + { Read, write, and execute by owner. } + IdS_IRWXU = Id__S_IREAD or Id__S_IWRITE or Id__S_IEXEC; + + IdS_IREAD = IdS_IRUSR; + IdS_IWRITE = IdS_IWUSR; + IdS_IEXEC = IdS_IXUSR; + + IdS_IRGRP = IdS_IRUSR shr 3; { Read by group. } + IdS_IWGRP = IdS_IWUSR shr 3; { Write by group. } + IdS_IXGRP = IdS_IXUSR shr 3; { Execute by group. } + { Read, write, and execute by group. } + IdS_IRWXG = IdS_IRWXU shr 3; + + IdS_IROTH = IdS_IRGRP shr 3; { Read by others. } + IdS_IWOTH = IdS_IWGRP shr 3; { Write by others. } + IdS_IXOTH = IdS_IXGRP shr 3; { Execute by others. } + { Read, write, and execute by others. } + IdS_IRWXO = IdS_IRWXG shr 3; + +{Some stuff for internationalization provided by Craig Peterson} +const +{$IFDEF STRING_IS_ANSI} + // These are the CJK "month", "day", and "year" characters, which appear after + // a number in the listings. Constants are UTF-8. According to + // www.FileFormat.info the characters for KoreanTotal, KoreanMonth, and + // KoreanDay aren't valid Unicode, but that's what appears in the listing. + KoreanTotal = #$EC#$B4#$9D; // #$CD1D + KoreanMonth = #$EC#$9B#$94; // #$C6D4 Hangul Syllable Ieung Weo Rieul + KoreanDay = #$EC#$9D#$BC; // #$C77C Hangul Syllable Ieung I Rieul + KoreanYear = #$EB#$85#$84; // #$B144 Hangul Syllable Nieun Yeo Nieun + KoreanEUCMonth = #$EB#$BF#$B9; //#$BFF9 + ChineseTotal = #$E6#$80#$BB + #$E6#$95#$B0; + // #$603B CJK Unified Ideograph Collect/Overall + + // #$6570 CJK Unified Ideograph Number/Several/Count + ChineseMonth = #$E6#$9C#$88; // #$6708 CJK Unified Ideograph Month + ChineseDay = #$E6#$97#$A5; // #$65E5 CJK Unified Ideograph Day + ChineseYear = #$E5#$B9#$B4; // #$5E74 CJK Unified Ideograph Year + + JapaneseTotal = #$E5#$90#$88 + #$E8#$A8#$88; + //@$5408 + // + JapaneseMonth = #$E8#$B2#$8E; // #$8c8e Japanse Month symbol + JapaneseDay = #$E9#$8F#$BA; //93fa - Japanese Day Symbol - not valid Unicode + JapaneseYear = #$E9#$91#$8E; //944e - Japanese Year symbol = not valid Unicode + +{$ELSE} + //These are in Unicode since the parsers receive data in Unicode form + KoreanTotal = #$CD1D; // #$CD1D + KoreanMonth = #$C6D4; // #$C6D4 Hangul Syllable Ieung Weo Rieul + KoreanDay = #$C77C; // #$C77C Hangul Syllable Ieung I Rieul + KoreanEUCMonth = #$BFF9; // #$BFF9 EUC-KR Same as #$C6#$D4 + KoreanYear = #$B144; // #$B144 Hangul Syllable Nieun Yeo Nieun + ChineseTotal = #$603B + #$6570; + // #$603B CJK Unified Ideograph Collect/Overall + + // #$6570 CJK Unified Ideograph Number/Several/Count + ChineseMonth = #$6708; // #$6708 CJK Unified Ideograph Month + ChineseDay = #$65E5; // #$65E5 CJK Unified Ideograph Day + ChineseYear = #$5E74; // #$5E74 CJK Unified Ideograph Year + + JapaneseTotal = #$5408 + #$8A08; + //#$5408 + //#$8a08 + JapaneseMonth = #$8C8E; // #$8c8e Japanse Day symbol + JapaneseDay = #$93FA; //93fa - Japanese Day Symbol - not valid Unicode + JapaneseYear = #$944E; //944e - Japanese Year symbol = not valid Unicode +{$ENDIF} + +procedure DeleteSuffix(var VStr : String; const ASuffix : String); {$IFDEF USE_INLINE}inline;{$ENDIF} + +//WS_FTP Pro XAUT Support + +{ +Note that the XAUT Support is from a file located at: + +http://72.32.12.210/archives/fulldisclosure/2004-03/att-1088/xp_ws_ftp_server.zip + + (c)2004 Hugh Mann hughmann@hotmail.com + +The code itself is designed to show a buffer overflow in a version of WS_FTP Server. +I only translated the XAUT logic from that code into Pascal for use in Indy. This +will not exploit any known flaw in the server. + +I did verify that this works with "X2 WS_FTP Server 6.1.1". +} +function ExtractWSFTPServerKey(const AGreeting : String; var VKey : UInt32) : Boolean; +procedure xaut_encrypt(var VDest : TIdBytes; const ASrc : TIdBytes; const AKey : UInt32); +procedure xaut_unpack(var VDest : String; const ASrc : TIdBytes); +procedure xaut_pack(var VDst : TIdBytes; const ASrc : String); +function MakeXAUTCmd(const AGreeting, AUsername, APassword : String; const Ad : UInt32 = 2) : String; +function ExtractAutInfoFromXAUT(const AXAutStr : String; const AKey : UInt32) : String; +function MakeXAUTKey : UInt32; + +const + XAUT_2_KEY = $49327576; +//end XAUT Stuff + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + Posix.SysTime, + Posix.Time, + {$ENDIF} + IdException; + +{WS_FTP Pro XAUT Support} + +function ExtractWSFTPServerKey(const AGreeting : String; var VKey : UInt32) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : String; +begin + Result := False; + if IndyPos('WS_FTP Server', AGreeting) > 0 then begin {Do not localize} + LBuf := AGreeting; + Fetch(LBuf, '('); {do not localize} + LBuf := Fetch(LBuf, ')'); {do not localize} + if IsNumeric(LBuf) then begin + VKey := UInt32(IndyStrToInt64(LBuf, 0)); + Result := True; + end; + end; +end; + +procedure xaut_encrypt(var VDest : TIdBytes; const ASrc : TIdBytes; const AKey : UInt32); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : TIdBytes; + i, l : Integer; +begin + SetLength(LBuf,4); + LBuf[0] := AKey and $FF; + LBuf[1] := (AKey shr 8) and $FF; + LBuf[2] := (AKey shr 16) and $FF; + LBuf[3] := (AKey shr 24) and $FF; + l := Length(ASrc); + SetLength(VDest,l); + for i := 0 to l - 1 do begin + VDest[i] := ASrc[i] xor LBuf[i mod 4]; + end; +end; + +procedure xaut_unpack(var VDest : String; const ASrc : TIdBytes); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i, l : Integer; + LBuf : TIdBytes; +begin + l := Length(ASrc); + SetLength(LBuf, l * 2); + for i := 0 to l-1 do begin + // dest[i*2+0] = ((src[i] >> 4) & 0x0F) + 0x35; + LBuf[(i*2)] := ((ASrc[i] shr 4) and $0F) + $35; + //dst[i*2+1] = (src[i] & 0x0F) + 0x31; + LBuf[(i*2)+1] := ((ASrc[i] and $0F) + $31); + end; + VDest := BytesToString(LBuf); +end; + +procedure xaut_pack(var VDst : TIdBytes; const ASrc : String); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i, l : Integer; + LSrc : TIdBytes; +begin + LSrc := ToBytes(ASrc); + Assert(Length(LSrc) = Length(ASrc),'both LSRC and ASRC must be identical.'); + l := Length(LSrc) div 2; + SetLength(VDst,l); + for i := 0 to l - 1 do begin + VDst[i] := (((LSrc[ (i * 2)] - $35) shl 4) + (LSrc[ (i * 2)+1] - $31)); + end; +end; + +function MakeXAUTCmd(const AGreeting, AUsername, APassword : String; const Ad : UInt32 = 2) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LKey : UInt32; + LDst : TIdBytes; +begin + Result := ''; + if ExtractWSFTPServerKey(AGreeting, LKey) then begin + LDst := ToBytes(AUsername+':'+APassword); + if Ad = 2 then begin + xaut_encrypt(LDst, LDst, XAUT_2_KEY); + end; + xaut_encrypt(LDst, LDst, LKey); + // LCmd := 'XAUT 2 '+ + xaut_unpack(Result, LDst); + Result := 'XAUT ' + IntToStr(Ad) + ' ' + Result; + end; +end; + +function ExtractAutInfoFromXAUT(const AXAutStr : String; const AKey : UInt32) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : TIdBytes; + LNum : UInt32; //first param +begin + Result := AXAutStr; + LNum := UInt32(IndyStrToInt64(Fetch(Result), 0)); + xaut_pack(LBuf, Result); + xaut_encrypt(LBuf, LBuf, AKey); + if LNum = 2 then begin + xaut_encrypt(LBuf, LBuf, XAUT_2_KEY); + end; + Result := BytesToString(LBuf); +end; + +function MakeXAUTKey : UInt32; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Randomize; + repeat + //we probably should avoid numbers that use the high bit to prevent them + //from being expressed negatively and because I'm not sure what integer + //type other programs us. + Result := (UInt32(Random($7F)) shl 24) or + (UInt32(Random($FF)) shl 16) or + (UInt32(Random($FF)) shl 8) or + UInt32(Random($FF)); + until (Result <> XAUT_2_KEY ) and (Result <> 0) +end; + +{Misc Parsing} + +procedure DeleteSuffix(var VStr : String; const ASuffix : String); + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if IndyPos(ASuffix, VStr) = Length(VStr) - Length(ASuffix) + 1 then begin + Delete(VStr, Length(VStr) - Length(ASuffix) + 1, Length(ASuffix)); + end; +end; + +function StripSpaces(const AString : String; const ASpaces : UInt32): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; + L: UInt32; +begin + L := IndyMin(ASpaces, Length(AString)); + for i := 1 to L do begin + if AString[i] <> ' ' then begin + Break; + end; + end; + if i > 1 then begin + Result := Copy(AString, i, MaxInt); + end else begin + Result := AString; + end; +end; + +function StripPath(const AFileName : String; const APathDelim : String = '/'): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : String; +begin + LBuf := AFileName; + repeat + Result := Fetch(LBuf, APathDelim); + until LBuf = ''; +end; + +function CharsInStr(const ASearchChar : Char; const AString : String) : Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + Result := 0; + for i := 1 to Length(AString) do begin + if AString[i] = ASearchChar then begin + Inc(Result); + end; + end; +end; + +function PatternsInStr(const ASearchPattern, AString : String): Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : String; +begin + Result := 0; + LBuf := AString; + repeat + Fetch(LBuf, ASearchPattern); + if LBuf = '' then begin + Break; + end else begin + Inc(Result); + end; + until False; +end; + +function UnfoldLines(const AData : String; ALine : Integer; AStrings : TStrings): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LFoldedLine : String; +begin + Result := AData; + repeat + Inc(ALine); + if ALine = AStrings.Count then begin + Break; + end; + LFoldedLine := AStrings[ALine]; + if LFoldedLine = '' then begin + Exit; + end; + if not CharIsInSet(LFoldedLine, 1, LWS) then begin + Break; + end; + Result := Trim(Result) + ' ' + Trim(LFoldedLine); {Do not Localize} + until False; +end; + +function StrPart(var AInput: string; const AMaxLength : Integer; const ADelete: Boolean = IdFetchDeleteDefault) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := Copy(AInput, 1, AMaxLength); + if ADelete then begin + Delete(AInput, 1, AMaxLength); + end; +end; + +function FetchLength(var AInput: string; const AMaxLength : Integer; const ADelim: string = IdFetchDelimDefault; + const ADelete: Boolean = IdFetchDeleteDefault; const ACaseSensitive: Boolean = IdFetchCaseSensitiveDefault): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + if ADelim = #0 then begin + // AnsiPos does not work with #0 + i := Pos(ADelim, AInput); + end else begin + i := IndyPos(ADelim, AInput); + end; + if (i > AMaxLength) or (i = 0) then begin + Result := Copy(AInput, 1, AMaxLength); + if ADelete then begin + Delete(AInput, 1, AMaxLength); + end; + end else begin + Result := Fetch(AInput, ADelim, ADelete, ACaseSensitive); + end; +end; + +function IsLineStr(const AData : String): Boolean; +//see if this is just a line with spaces, '-', or tabs so we +//can skip it in the parser +const + //Note that there are two separate char codes are rended as '-' in the line below. + //Be careful when editing because the codes are different. + // LineSet = [' ','-','','+']; {Do not Localize} + + // 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... + + LineSet = ' -'+Char($96)+'+'; //BGO: for DotNet, what to do with this {Do not Localize} +var + i: Integer; + LLen: Integer; +Begin + LLen := Length(AData); + if LLen > 0 then begin + Result := True; //only white + for i := 1 to LLen do begin + if not CharIsInSet(AData, i, LineSet) then begin + Result := False; + Exit; + end; + end; + end else begin + Result := True; //empty + end; +end; + +{Number extraction} +function FindDelimInNumbers(const AData : String) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + Result := ''; + for i := 1 to Length(AData) do begin + if not IsNumeric(AData[i]) then begin + Result := AData[i]; + Exit; + end; + end; +end; + +function ExtractNumber(const AData : String; const ARetZero : Boolean = True): Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; + LBuf : String; +begin + LBuf := ''; + for i := 1 to Length(AData) do begin + if IsNumeric(AData[i]) then begin + LBuf := LBuf + AData[i]; + end + else if AData[i] <> ',' then begin + Break; + end; + end; + if ARetZero then begin + Result := IndyStrToInt(LBuf, 0); + end else begin + Result := IndyStrToInt(LBuf, -1); + end; +end; + +function StripNo(const AData : String): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; + LPos : Integer; +begin + LPos := 1; + for i := 1 to Length(AData) do begin + LPos := i; + if (not IsNumeric(AData[i])) and (not CharEquals(AData, i, ',')) then begin + Break; + end; + end; + Result := Copy(AData, LPos, Length(AData)); +end; + +{Path processing} +{ + +Note that for our purposes, Borland's comporable routines +are inadiquate because they always assume the standard system +path separators. In Win32, the routines use '\' instead of '/' and +likewise, in Linux, the routines use '/' instead of '\'. We need to +use both separators because we need to handle both for crossplatform +client/server work. + +} +function LastPathDelim(const APath : String):Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + for i := Length(APath) downto 1 do begin + if CharIsInSet(APath, i, PATH_FILENAME_SEP_DOS + PATH_FILENAME_SEP_UNIX) then begin + Result := i; + Exit; + end; + end; + Result := 0; +end; + +function IndyGetFilePath(const AFileName : String):String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + i := LastPathDelim(AFileName); + if i > 0 then begin + Result := Copy(AFileName, 1, i-1); + end else begin + Result := ''; + end; +end; + +function IndyGetFileName(const AFileName : String):String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + i : Integer; +begin + i := LastPathDelim(AFileName); + if i = 0 then begin + Result := AFileName; + end else begin + Result := Copy(AFileName, i+1, Length(AFileName)); + end; +end; + +function IndyIsRelativePath(const APathName : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if APathName <> '' then begin + Result := CharIsInSet(APathName, 1, PATH_SUBDIR_SEP_UNIX + PATH_SUBDIR_SEP_DOS); + end else begin + Result := False; + end; +end; + +function IndyGetFileExt(const AFileName : String) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +{ +Borland's ExtractFileExtension routine is not adiquate in some cases +because it assumes that there will only be one extension. Some files +have two extensions such as Linux tarballs, ".tar.gz". + +With a file name such as test.tar.gz, Borland's routine returns .gz +instead of .tar.gz + +Sometimes, in order to shoot yourself in the foot, you have to reinvent the +gun, the bullet, and your foot :-). + +} +var + LBuf : String; + LPos : Integer; +begin + Result := ''; + LBuf := IndyGetFileName(AFileName); + LPos := IndyPos('.', LBuf); + if LPos > 0 then begin + Result := Copy(LBuf, LPos, MaxInt); + end; +end; + +function StripInitPathDelim(const AStr : String): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := AStr; + if Result <> '' then begin + //strip off any beggining / or \ + if CharIsInSet(Result, 1, PATH_FILENAME_SEP_UNIX + PATH_FILENAME_SEP_DOS) then begin + IdDelete(Result, 1, 1); + end; + end; +end; + +function IsNavPath(const APath : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LTmp : String; +begin + LTmp := IndyGetFileName(StripInitPathDelim(APath)); + Result := (LTmp = CUR_DIR) or (LTmp = PARENT_DIR); +end; + +// RLebeau 10/26/09: RemoveDuplicatePathSyms() cannot be inlined if it uses +// the const variables declared outside of it, as they are private to this unit +// and not accessible during inlining! + +{ +const + TrailingPathCorrectionOrg : array [0..3] of string = + ('//','\\','/\','\/'); + TrailingPathCorrectionNew : array [0..3] of string = + ('/','\','/','/'); +} + +function RemoveDuplicatePathSyms(APath : String): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + //Result := StringsReplace(APath, TrailingPathCorrectionOrg, TrailingPathCorrectionNew); + Result := StringsReplace(APath, ['//','\\','/\','\/'], ['/','\','/','/']); {do not localize} +end; + +{Path conversion} + +function UnixPathToDOSPath(const APath : String):String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := ReplaceAll(APath, PATH_SUBDIR_SEP_UNIX, PATH_SUBDIR_SEP_DOS); +end; + +function DOSPathToUnixPath(const APath : String):String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := ReplaceAll(APath, PATH_SUBDIR_SEP_DOS, PATH_SUBDIR_SEP_UNIX); +end; + +{Pattern recognition} + +function IsSubDirContentsBanner(const AData: String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + //A line ending in : might be a standard Unix list item where the filename + //ends with a ":". Unix-xbox-MediaCenter.txt is an example. + Result := TextEndsWith(AData, ':') and (not IsValidUnixPerms(AData)); +end; + +function IsTotalLine(const AData: String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + //just in case someone is doing a recursive listing and there's a dir with the name total + Result := (not TextEndsWith(AData, ':')) and + (TextStartsWith(AData, 'TOTAL') or + TextStartsWith(AData, 'GESAMT') or // German + TextStartsWith(AData, 'INSGESAMT') or // German HPUX + (IndyPos(KoreanTotal, AData) = 1) or // Korean (Unicode) + (IndyPos(ChineseTotal, AData) = 1) or // Chinese (Unicode) + TextStartsWith(AData, JapaneseTotal)); +end; + +{Quoted strings} + +procedure ParseQuotedArgs(const AParams : String; AStrings : TStrings); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + lComma, LOpenQuote : Integer; + LBuf : String; + LArg : String; + //filename.ext +//"../SomeDir/A ,File.txt", filename.ext +//filename.ext, ".." +begin + AStrings.Clear; + LBuf := AParams; + repeat + if LBuf = '' then begin + Break; + end; + lComma := IndyPos(',', LBuf); + LOpenQuote := IndyPos('"', LBuf); + if LComma = 0 then begin + LComma := Length(LBuf); + end; + if (LOpenQuote = 0) or (LComma < LOpenQuote) then begin + LArg := TrimLeft(Fetch(LBuf,',')); + end else begin + Fetch(LBuf,'"'); + LArg := '"' + Fetch(LBuf,'"') + '"'; + end; + if LArg <> '' then begin + AStrings.Add(LArg); + end; + until False; +end; + +{$IFNDEF HAS_TryEncodeDate} +// TODO: move this to IdGlobal or IdGlobalProtocols... +function TryEncodeDate(Year, Month, Day: Word; out VDate: TDateTime): Boolean; +begin + try + VDate := EncodeDate(Year, Month, Day); + Result := True; + except + Result := False; + end; +end; +{$ENDIF} + +{EPLF Date processing} + +function EPLFDateToLocalDateTime(const AData: String): TDateTime; +{note - code stolen from TIdTime and modified for our needs.} +const + BASE_DATE = 25569; //Jan 1, 1970 +var + LSecs : Int64; +begin + LSecs := IndyStrToInt(AData); + Result := Extended( ((LSecs)/ (24 * 60 * 60) ) + Int(BASE_DATE)) - IdGlobalProtocols.TimeZoneBias; +end; + +function EPLFDateToGMTDateTime(const AData: String): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +{note - code stolen from TIdTime and modified for our needs.} +var + LSecs : Int64; +begin + LSecs := IndyStrToInt(AData); + Result := Extended( ((LSecs)/ (24 * 60 * 60) ) + Int(EPLF_BASE_DATE)); +end; + +function GMTDateTimeToEPLFDate(const ADateTime : TDateTime) : String; +const + BASE_DATE = 25569; +begin + Result := FloatToStr( Extended(ADateTime - Int(BASE_DATE)) * 24 * 60 * 60); +end; + +function LocalDateTimeToEPLFDate(const ADateTime : TDateTime) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := FloatToStr( Extended(ADateTime + IdGlobalProtocols.TimeZoneBias - Int(EPLF_BASE_DATE)) * 24 * 60 * 60); +end; + +{Date routines} +function IsValidTimeStamp(const AString : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LHour, LMin, LSec : Integer; +begin + Result := False; + // 1234 56 78 90 12 34 + // ---------- --------- + // 1998 11 07 08 52 15 + LMonth := IndyStrToInt(Copy(AString, 5, 2), 0); + if (LMonth < 1) or (LMonth > 12) then begin + Exit; + end; + LDay := IndyStrToInt(Copy(AString, 7, 2), 0); + if (LDay < 1) or (LDay > 31) then begin + Exit; + end; + LHour := IndyStrToInt(Copy(AString, 9, 2), 0); + if (LHour < 0) or (LHour > 24) then begin + Exit; + end; + LMin := IndyStrToInt(Copy(AString, 11, 2), 0); + if (LMin < 0) or (LMin > 59) then begin + Exit; + end; + LSec := IndyStrToInt(Copy(AString, 13, 2), 0); + if (LSec < 0) or (LSec > 59) then begin + Exit; + end; + Result := True; +end; + +function IsMDTMDate(const ADate : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +{ +Note from FTP Voyager knowlege base: + +MDTM + +This is from the UNIX world and it lets you query the server for the modification date and time of a file or directory. Unlike UNIX, FTP Serv-U also lets the client set the modification date and time of files on the server, if the user has sufficient access rights to do this. Its use is in synchronizing uploaded files with those on the client. Normally FTP has no way to explicitly set the date of uploaded files, they simply get the date they were created on the server. MDTM lets the client change that so they get the date of the original file on the server. Works for directories too. The syntax to set the date and time is: + + +MDTM yyyymmddhhmmss[+-xxx] +Where yyyymmddhhmmss is a line of text with the year, month, day, hour, minutes, and seconds the file should get set to. The next part, [+-xxx], is optional time zone information of the FTP client in minutes relative to UTC. + +If the client provides this info FTP Serv-U takes care to convert the date and time to the proper local time at the server, so dates and times are kept consistent (a file created at 4 in the morning in the Eastern US would be created at 10 in Central Europe). If no time zone info is given FTP Serv-U assumes you are specifying local time at the server. + +An example, showing how to set the time if the client is in the Eastern US during summer time: MDTM 19980719103029-240. This sets the date and time to 19 July 1998, 10:30am 29 seconds, and indicates the client is 240 behind UT +} +var + LBuffer, LMSecPart : String; +begin + Result := False; + LBuffer := ADate; + if IndyPos('-', LBuffer) > 0 then begin + LMSecPart := LBuffer; + LBuffer := Fetch(LMSecPart, '-'); + if not IsNumeric(LMSecPart) then begin + Exit; + end; + end; + if IndyPos('+', LBuffer) > 0 then begin + LMSecPart := LBuffer; + LBuffer := Fetch(LMSecPart, '+'); + if not IsNumeric(LMSecPart) then begin + Exit; + end; + end; + if IndyPos('.', LBuffer) > 0 then begin + LMSecPart := Fetch(LBuffer, '.'); + end; + if Length(LBuffer) <> 14 then begin + Exit; + end; + if not IsNumeric(LBuffer) then begin + Exit; + end; + if (LMSecPart <> '') and (not IsNumeric(LMSecPart)) then begin + Exit; + end; + Result := IsValidTimeStamp(LBuffer); +end; + +function MDTMOffset(const AOffs : String) : TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LOffs : Integer; +begin + LOffs := IndyStrToInt(AOffs); + {We use ABS because EncodeTime will only accept positve values} + Result := EncodeTime(Abs(LOffs) div 60, Abs(LOffs) mod 60, 0, 0); + if LOffs > 0 then begin + Result := 0 - Result; + end; +end; + +function MinutesFromGMT : Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LD : TDateTime; + LHour, LMin, LSec, LMSec : Word; +begin + LD := OffsetFromUTC; + DecodeTime(LD, LHour, LMin, LSec, LMSec); + if LD < 0.0 then begin + Result := 0 - (LHour * 60 + LMin); + end else begin + Result := LHour * 60 + LMin; + end; +end; + +function FTPDateTimeToMDTMD(const ATimeStamp : TDateTime; const AIncludeMSecs : Boolean=True; const AIncludeGMTOffset : Boolean=True): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LYear, LMonth, LDay, + LHour, LMin, LSec, LMSec : Word; + LOfs : Integer; +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]); {Do not translate} + if AIncludeMSecs then begin + Result := Result + IndyFormat('.%3d', [LMSec]); {Do not translate} + end; + if AIncludeGMTOffset then begin + LOfs := MinutesFromGMT; + if LOfs < 0 then begin + Result := Result + IntToStr(LOfs); + end else begin + Result := Result + '+' + IntToStr(LOfs); + end; + end; + Result := ReplaceAll(Result, ' ', '0'); +end; + +function FTPMDTMToGMTDateTime(const ATimeStamp : String):TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LYear, LMonth, LDay, LHour, LMin, LSec, LMSec : Integer; + LBuffer : String; + LOffset : String; +begin + Result := 0; + LBuffer := ATimeStamp; + if LBuffer <> '' then begin + //extract any offset + if IndyPos('-', LBuffer) > 0 then begin + LOffset := LBuffer; + LBuffer := Fetch(LOffset, '-'); + LOffset := '-' + LOffset; + end; + if IndyPos('+', LBuffer) > 0 then begin + LOffset := LBuffer; + LBuffer := Fetch(LOffset, '+'); + end; + // 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); + if LOffset = '' then begin + Result := Result - OffsetFromUTC; + end else begin + Result := Result - MDTMOffset(LOffset); + end; + end; +end; + +function IsYYYYMMDD(const AData : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//Does it look something like this: +//2002-09-02 +// +//or +// +//90-05-19 +//1234567890 +begin + Result := CharIsInSet(AData, 5, CDATE_PART_SEP) and CharIsInSet(AData, 8, CDATE_PART_SEP); + if Result then begin + Result := IsNumeric(AData, 4) and IsNumeric(AData, 2, 6) and IsNumeric(AData, 2, 9); + end; + if not Result then begin + Result := CharIsInSet(AData, 3, CDATE_PART_SEP) and CharIsInSet(AData, 6, CDATE_PART_SEP); + if Result then begin + Result := IsNumeric(AData, 2) and IsNumeric(AData, 2, 4) and IsNumeric(AData, 2, 7); + end; + end; +end; + +function IsDDMonthYY(const AData : String; const ADelim : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf, LPt : String; +begin + Result := False; + if PatternsInStr(ADelim, AData) = 2 then begin + LBuf := AData; + LPt := Fetch(LBuf,ADelim); + //day + if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 32) then begin + //month + LPt := Fetch(LBuf, ADelim); + if StrToMonth(LPt) > 0 then begin + //year + LPt := Fetch(LBuf, ADelim); + Result := IsNumeric(LPt); + end; + end; + end; +end; + +function IsMMDDYY(const AData : String; const ADelim : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf, LPt : String; +begin + Result := False; + if PatternsInStr(ADelim, AData) = 2 then begin + LBuf := AData; + LPt := Fetch(LBuf, ADelim); + if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 13) then begin + LPt := Fetch(LBuf, ADelim); + if (IndyStrToInt(LPt, 0) > 0) and (IndyStrToInt(LPt, 0) < 33) then begin + Result := IsNumeric(LBuf); + end; + end; + end; +end; + +function Y2Year(const AYear : Integer): Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +{ +This function ensures that 2 digit dates returned +by some FTP servers are interpretted just like Borland's year +handling routines. +} +{$IFDEF HAS_TFormatSettings_Object} +{For Delphi XE, we have a format settings object that includes a member +for two digit year processing. Use that instead because that is thread-safe. + +Also note, that in this version, TFormatSettings is not an object at all, it's a +record with associated functions and procedures plus a creator. Since we allocate +it on the stack with the definition, we can't "free" it with FreeAndNil. } +var + LFormatSettings: SysUtils.TFormatSettings; +{$ENDIF} +begin + Result := AYear; + //Y2K Complience for current code + //Note that some OS/2 servers return years greater than 100 for + //years such as 2000 and 2003 + if Result < 1000 then begin + {$IFDEF HAS_TFormatSettings_Object} + LFormatSettings:= TFormatSettings.Create(''); //use default locale + if LFormatSettings.TwoDigitYearCenturyWindow > 0 then begin + if Result > LFormatSettings.TwoDigitYearCenturyWindow then begin + {$ELSE} + if TwoDigitYearCenturyWindow > 0 then begin + if Result > TwoDigitYearCenturyWindow then begin + {$ENDIF} + Inc(Result, ((IndyCurrentYear div 100)-1)*100); + end else begin + Inc(Result, (IndyCurrentYear div 100)*100); + end; + end else begin + Inc(Result, (IndyCurrentYear div 100)*100); + end; + {$IFDEF HAS_TFormatSettings_Object} + + {$ENDIF} + end; +end; + +function DateYYMMDD(const AData: String): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LYear : Integer; + LBuffer : String; + LDelim : String; +begin + LBuffer := AData; + LDelim := FindDelimInNumbers(AData); + LYear := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LMonth := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LDay := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function DateYYStrMonthDD(const AData: String; const ADelim : String = '-'): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LYear : Integer; + LBuffer : String; +begin + LBuffer := AData; + LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + LMonth := StrToMonth(Trim(Fetch(LBuffer,ADelim))); + LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function DateStrMonthDDYY(const AData:String; const ADelim : String = '-'; const AAddMissingYear : Boolean = False): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LYear : Integer; + LBuffer : String; + LMnth : String; +begin + LBuffer := AData; + LMnth := Trim(Fetch(LBuffer,ADelim)); + LMonth := IndyStrToInt(LMnth, 0); + if LMonth = 0 then begin + LMonth := StrToMonth(LMnth); + end; + LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + if AAddMissingYear and (LYear = 0) then begin + LYear := AddMissingYear(LDay, LMonth); + end; + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function DateDDStrMonthYY(const AData: String; const ADelim : String='-'): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LYear : Integer; + LBuffer : String; +begin + LBuffer := AData; + LDay := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + LMonth := StrToMonth(Trim(Fetch(LBuffer,ADelim))); + LYear := IndyStrToInt(Fetch(LBuffer,ADelim), 0); + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function DateMMDDYY(const AData: String): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LMonth, LDay, LYear : Integer; + LBuffer : String; + LDelim : String; +begin + LBuffer := AData; + LDelim := FindDelimInNumbers(AData); + LMonth := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LDay := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LYear := IndyStrToInt(Fetch(LBuffer,LDelim), 0); + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function TimeHHMMSS(const AData : String):TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LCHour, LCMin, LCSec, LCMSec : Word; + LHour, LMin, LSec, LMSec : Word; + LBuffer : String; + LDelim : String; + LPM : Boolean; + LAM : Boolean; //necessary because we have to remove 12 hours if the time was 12:01:00 AM +begin + LPM := False; + LAM := False; + LBuffer := UpperCase(AData); + if IndyPos('PM', LBuffer) > 0 then begin {do not localize} + LPM := True; + LBuffer := Fetch(LBuffer, 'PM'); {do not localize} + end; + if IndyPos('AM', LBuffer) > 0 then begin {do not localize} + LAM := True; + LBuffer := Fetch(LBuffer, 'AM'); {do not localize} + end; + //one server only gives an a or p instead of am or pm + if IndyPos('P', LBuffer) > 0 then begin {do not localize} + LPM := True; + LBuffer := Fetch(LBuffer,'P'); {do not localize} + end; + if IndyPos('A', LBuffer) > 0 then begin {do not localize} + LAM := True; + LBuffer := Fetch(LBuffer, 'A'); {do not localize} + end; + LBuffer := Trim(LBuffer); + DecodeTime(Now, LCHour, LCMin, LCSec, LCMSec); + LDelim := FindDelimInNumbers(AData); + LHour := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + LMin := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + 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 pritty accurate + if LHour < 12 then begin + Inc(LHour, 12); + end; + end; + if LAM then begin + if LHour = 12 then begin + LHour := 0; + end; + end; + LSec := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + LMSec := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + Result := EncodeTime(LHour, LMin, LSec, LMSec); +end; + +function IsIn6MonthWindow(const AMDate : TDateTime):Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//based on http://www.opengroup.org/onlinepubs/007908799/xbd/utilconv.html#usg +//For dates, we display the time only if the date is within 6 monthes of the current +//date. Otherwise, we send the year. +var + LCurMonth, LCurDay, LCurYear : Word; //Now + LPMonth, LPYear : Word; + LMMonth, LMDay, LMYear : Word;//AMDate +begin + DecodeDate(Now, LCurYear, LCurMonth, LCurDay); + DecodeDate(AMDate, LMYear, LMMonth, LMDay); + if (LCurMonth - 6) < 1 then begin + LPMonth := 12 + (LCurMonth - 6); + LPYear := LCurYear - 1; + end else begin + LPMonth := LCurMonth - 6; + LPYear := LCurYear; + end; + if LMYear < LPYear then begin + Result := False; + Exit; + end; + if LMYear = LPYear then begin + Result := (LMMonth >= LPMonth); + if Result and (LMMonth = LPMonth) then begin + Result := (LMDay >= LCurDay); + Exit; + end; + end else begin + Result := True; + end; +end; + +function AddMissingYear(const ADay, AMonth : UInt32): UInt32; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LDay, LMonth, LYear : Word; + DT: TDateTime; +begin + DecodeDate(Now, LYear, LMonth, LDay); + Result := LYear; + if TryEncodeDate(LYear, AMonth, ADay, DT) and (DT > Trunc(Now + 1)) then begin + Result := LYear - 1; + end; +end; + +function IsHHMMSS(const AData : String; const ADelim : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//This assumes hours in the form 0-23 instead of the 12 AM/PM hour system used in the US. +var + LBuf, LPt : String; +begin + Result := False; + LBuf := AData; + if PatternsInStr(ADelim, AData) > 0 then begin + LPt := Fetch(LBuf, ADelim); + if (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, -1) < 24) then begin + LPt := Fetch(LBuf, ADelim); + if (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, 0) < 60) then begin + LPt := Fetch(LBuf, ADelim); + if LPt = '' then begin + Result := True; + end else begin + //seconds are also given - check those + Result := (IndyStrToInt(LPt, -1) > -1) and (IndyStrToInt(LPt, 0) < 60); + end; + end; + end; + end; +end; + +function MVSDate(const AData: String): TDateTime; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LYear, LMonth, LDay : Integer; + LCYear, LCMonth, LCDay : Word; + LBuffer : String; +begin + DecodeDate(Now, LCYear, LCMonth, LCDay); + LBuffer := AData; + if IndyPos('/', LBuffer) = 3 then begin + //two digit things could be in order of yy/mm/dd or mm/dd/yy in a partitionned dtaset + LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear); + if (LYear < 13) and (LYear > 0) then begin + LMonth := LYear; + LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay); + LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear); + end else begin + LMonth := IndyStrToInt(Fetch(LBuffer, '/'), LCMonth); + LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay); + end; + end else begin + LYear := IndyStrToInt(Fetch(LBuffer, '/'), LCYear); + LMonth := IndyStrToInt(Fetch(LBuffer, '/'), LCMonth); + LDay := IndyStrToInt(Fetch(LBuffer, '/'), LCDay); + end; + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +function AS400Date(const AData: String): TDateTime; +var + LDelim : String; + LBuffer : String; + LDay, LMonth, LYear : Integer; + + procedure SwapNos(var An1, An2 : Integer); + var + LN : Integer; + begin + LN := An2; + An2 := An1; + An1 := LN; + end; + +begin + Result := 0; + LDelim := FindDelimInNumbers(AData); + if LDelim = '' then begin + Exit; + end; + LBuffer := AData; + LDay := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + LMonth := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + LYear := IndyStrToInt(Fetch(LBuffer, LDelim), 0); + if LMonth > 12 then begin + SwapNos(LDay, LMonth); + end; + if LDay > 31 then begin + SwapNos(LYear, LDay); + end; + LYear := Y2Year(LYear); + Result := EncodeDate(LYear, LMonth, LDay); +end; + +//=== platform stuff +//===== Unix + +function IsValidUnixPerms(AData : String; const AStrict : Boolean = False) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//Stict mode is for things such as Novell Netware Unix Print Services FTP Deamon +//which are not quite like Unix. +//Non-strict mode is for Unix servers or servers that emulate Unix because some are broken. +var + SData : String; +begin + if not AStrict then begin + SData := UpperCase(AData); + Result := (Length(SData) > 9) and + {LynxOS may report "f" or "r" for a regular file, "+" for a contiguous file, + "i" for a non-persistent ipc special file, and "I" for a persistent ipc + special file. The Linux manpage for stat also reports "m" for XENIX shared + data subtype of IFNAM, and "w" for a BSD whiteout} + CharIsInSet(SData, 1, 'LD-BCPS+IMW') and {Do not Localize} + CharIsInSet(SData, 2, 'TSRWX-') and {Do not Localize} + {Distinct TCP/IP FTP Server-32 3.0 errs by reporting an 'A" here } + CharIsInSet(SData, 3, 'TSRWX-A') and {Do not Localize} + CharIsInSet(SData, 4, 'TSRWX-L') and {Do not Localize} + {Distinct TCP/IP FTP Server-32 3.0 errs by reporting an 'H" here for hidden files} + CharIsInSet(SData, 5, 'TSRWX-H') and {Do not Localize} + CharIsInSet(SData, 6, 'TSRWX-') and {Do not Localize} + {Distinct's FTP Server Active X may report a "Y" by mistake, saw in manual + FTP Server, ActiveX Control, File Transfer Protocol (RFC 959), ActiveX Control, + for Microsoft Windows, Version 4.01 + Copyright 1996 - 1998 by Distinct Corporation + All rights reserved + } + {Solaris returns "L" instead of "S" for setgid without group execute (mandatory locking)} + CharIsInSet(SData, 7, 'TSRWX-YL') and {Do not Localize} + CharIsInSet(SData, 8, 'TSRWX-A') and {Do not Localize} + {VxWorks 5.3.1 FTP Server has a quirk where a "A" is in the permissions + See: + http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=slrn73rfie. + 1g2.chc%40nasa2.ksc.nasa.gov&rnum=1&prev=/groups%3Fq%3DVxWorks%2BFTP%2BLIST%2 + Bformat%2Bdate%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D + slrn73rfie.1g2.chc%2540nasa2.ksc.nasa.gov%26rnum%3D1 + } + CharIsInSet(SData, 9, 'TSRWX-') and {Do not Localize} + CharIsInSet(SData, 10, 'TSRWX-'); {Do not Localize} + end else begin + Result := (Length(SData) > 9) and + CharIsInSet(AData, 1, 'd-') and {Do not Localize} + CharIsInSet(AData, 2, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 3, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 4, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 5, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 6, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 7, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 8, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 9, 'tsrwx-') and {Do not Localize} + CharIsInSet(AData, 10, 'tsrwx- '); {Do not Localize} + end; +end; + +function IsUnixLsErr(const AData: String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := TextStartsWith(AData, '/bin/ls:'); {do not localize} +end; + +function IsUnixHiddenFile(const AFileName : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LName : String; +begin + LName := IndyGetFileName(StripInitPathDelim(AFileName)); + Result := (not IsNavPath(AFileName)) and TextStartsWith(LName, '.'); +end; + +function IsUnixExec(const LUPer, LGPer, LOPer : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + if (Length(LUPer) > 2) and (Length(LGPer) > 2) and (Length(LOPer) > 2) then begin + Result := CharIsInSet(LUPer, 3, 'xSs') or {do not localize} + CharIsInSet(LGPer, 3, 'xSs') or {do not localize} + CharIsInSet(LOPer, 3, 'xSs'); {do not localize} + end else begin + Result := False; + end; +end; + +function PermStringToModeBits(const APerms : String): UInt32; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := 0; + //owner bits + if (Length(APerms) > 0) and (APerms[1] = 'r') then begin + Result := Result or IdS_IRUSR; + end; + if (Length(APerms) > 1) and (APerms[2] = 'w') then begin + Result := Result or IdS_IWUSR; + end; + if Length(APerms) > 2 then begin + case APerms[3] of + 'x' : //exec + begin + Result := Result or IdS_IXUSR; + end; + 's' : //SUID and exec + begin + Result := Result or IdS_IXUSR; + Result := Result or IdS_ISUID; + end; + 'S' : //SUID bit without owner exec + begin + Result := Result or IdS_ISUID; + end; + end; + end; + //group bits + if (Length(APerms) > 3) and (APerms[4] = 'r') then begin + Result := Result or IdS_IRGRP; + end; + if (Length(APerms) > 4) and (APerms[5] = 'w') then begin + Result := Result or IdS_IWGRP; + end; + if Length(APerms) > 5 then begin + case APerms[6] of + 'x' : //exec + begin + Result := Result or IdS_IXGRP; + end; + 's' : //SUID and exec + begin + Result := Result or IdS_IXGRP; + Result := Result or IdS_ISGID; + end; + 'S' : //SGID bit without group exec + begin + Result := Result or IdS_ISGID; + end; + end; + end; + //Other permissions + if (Length(APerms) > 6) and (APerms[7] = 'r') then begin + Result := Result or IdS_IROTH; + end; + if (Length(APerms) > 7) and (APerms[8] = 'w') then begin + Result := Result or IdS_IWOTH; + end; + if Length(APerms) > 8 then begin + case APerms[9] of + 'x' : + begin + Result := Result or IdS_IXOTH; + end; + 't' : + begin + Result := Result or IdS_IXOTH; + Result := Result or IdS_ISVTX; + end; + 'T' : + begin + Result := Result or IdS_ISVTX; + end; + end; + end; +end; + +function ModeBitsToPermString(const AMode : UInt32) : String; + + function GetPerm1Bit(ABit: UInt32; AIfSet: Char): Char; + begin + if (AMode and ABit) = ABit then begin + Result := AIfSet; + end else begin + Result := '-'; + end; + end; + + function GetPerm2Bits(ABit1, ABit2: UInt32; AIfBit1Set, AIfBit2Set: Char): Char; + begin + Result := GetPerm1Bit(ABit1, AIfBit1Set); + if Result = '-' then begin + Result := GetPerm1Bit(ABit2, AIfBit2Set); + end; + end; + +var + LPerm: Char; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(9); + {$ELSE} + SetLength(Result, 9); + {$ENDIF} + + //owner Permissions + //read by owner + LPerm := GetPerm1Bit(IdS_IRUSR, 'r'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[1] := LPerm; + {$ENDIF} + + //write by owner + LPerm := GetPerm1Bit(IdS_IWUSR, 'w'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[2] := LPerm; + {$ENDIF} + + //execute by owner + LPerm := GetPerm2Bits(IdS_ISUID, IdS_IXUSR, 's', 'x'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[3] := LPerm; + {$ENDIF} + + //group permissions + //read by group + LPerm := GetPerm1Bit(IdS_IRGRP, 'r'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[4] := LPerm; + {$ENDIF} + + //write by group + LPerm := GetPerm1Bit(IdS_IWGRP, 'w'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[5] := LPerm; + {$ENDIF} + + //execute by group + LPerm := GetPerm2Bits(IdS_ISGID, IdS_IXGRP, 's', 'x'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[6] := LPerm; + {$ENDIF} + + //other's permissions + //read by others + LPerm := GetPerm1Bit(IdS_IROTH, 'r'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[7] := LPerm; + {$ENDIF} + + //write by others + LPerm := GetPerm1Bit(IdS_IWOTH, 'w'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[8] := LPerm; + {$ENDIF} + + //execute by others + //Sticky bit - only owner can delete files in dir. + //on older systems, it means to keep the file in memory as a "cache" + LPerm := GetPerm2Bits(IdS_ISVTX, IdS_IXOTH, 't', 'x'); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(LPerm); + {$ELSE} + Result[9] := LPerm; + {$ENDIF} + + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +function ModeBitsToChmodNo(const AMode : UInt32): Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := 0; + if (AMode and IdS_ISUID) = IdS_ISUID then begin + Result := Result + 4000; + end; + if (AMode and IdS_ISGID) = IdS_ISGID then begin + Result := Result + 2000; + end; + if (AMode and IdS_ISVTX) = IdS_ISVTX then begin + Result := Result + 1000; + end; + if (AMode and IdS_IRUSR) = IdS_IRUSR then begin + Result := Result + 400; + end; + if (AMode and IdS_IWUSR) = IdS_IWUSR then begin + Result := Result + 200; + end; + if (AMode and IdS_IXUSR) = IdS_IXUSR then begin + Result := Result + 100; + end; + if (AMode and IdS_IRGRP) = IdS_IRGRP then begin + Result := Result + 40; + end; + if (AMode and IdS_IWGRP) = IdS_IWGRP then begin + Result := Result + 20; + end; + if (AMode and IdS_IXGRP) = IdS_IXGRP then begin + Result := Result + 10; + end; + if (AMode and IdS_IROTH) = IdS_IROTH then begin + Result := Result + 4; + end; + if (AMode and IdS_IWOTH) = IdS_IWOTH then begin + Result := Result + 2; + end; + if (AMode and IdS_IXOTH) = IdS_IXOTH then begin + Result := Result + 1; + end; +end; + +function ChmodNoToModeBits(const AModVal : UInt32): UInt32; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LSpecBits, LUBits, LGBits, LOBits : UInt32; + LTmp : UInt32; +begin + Result := 0; + LSpecBits := AModVal div 1000; + LSpecBits := LSpecBits and 7; + LTmp := AModVal; + LTmp := LTmp mod 1000; + LUBits := LTmp div 100; + LUBits := LUBits and 7; + LTmp := LTmp mod 100; + LGBits := LTmp div 10; + LGBits := LGBits and 7; + LTmp := LTmp mod 10; + LOBits := LTmp and 7; + if (LSpecBits and 4) = 4 then begin + Result := Result + IdS_ISUID; + end; + if (LSpecBits and 2) = 2 then begin + Result := Result + IdS_ISGID; + end; + if (LSpecBits and 1) = 1 then begin + Result := Result + IdS_ISVTX; + end; + //user bits + if (LUBits and 4) = 4 then begin + Result := Result + IdS_IRUSR; + end; + if (LUBits and 2) = 2 then begin + Result := Result + IdS_IWUSR; + end; + if (LUBits and 1) = 1 then begin + Result := Result + IdS_IXUSR; + end; + //group bits + if (LGBits and 4) = 4 then begin + Result := Result + IdS_IRGRP; + end; + if (LGBits and 2) = 2 then begin + Result := Result + IdS_IWGRP; + end; + if (LGBits and 1) = 1 then begin + Result := Result + IdS_IXGRP; + end; + //other bits + if (LOBits and 4) = 4 then begin + Result := Result + IdS_IROTH; + end; + if (LOBits and 2) = 2 then begin + Result := Result + IdS_IWOTH; + end; + if (LOBits and 1) = 1 then begin + Result := Result + IdS_IXOTH; + end; +end; + +procedure ChmodNoToPerms(const AChmodNo : Integer; var VPermissions : String); overload; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + VPermissions := ModeBitsToPermString(ChmodNoToModeBits(AChmodNo)); +end; + +procedure ChmodNoToPerms(const AChmodNo : Integer; var VUser, VGroup, VOther : String); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LPerms : String; +begin + ChmodNoToPerms(AChmodNo,LPerms); + VUser := Copy(LPerms, 1, 3); + VGroup := Copy(LPerms, 4, 3); + VOther := Copy(LPerms, 7, 3); +end; + +function PermsToChmodNo(const AUser, AGroup, AOther : String): Integer; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := ModeBitsToChmodNo(PermStringToModeBits(AUser+AGroup+AOther)); +end; + +//===== Novell Netware +//ftp.sips.state.nc.us +function IsNovelPSPattern(const AStr : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + s : TStringList; + LModStr : String; +begin + LModStr := AStr; + if (Length(LModStr) > 1) and (LModStr[2] = '[') then begin + IdInsert(' ', LModStr, 2); + end; + s := TStringList.Create; + try + SplitDelimitedString(LModStr, s, True); + //0-type + //1-permissions + //2-owner + //3-size + //4-month + //5-day of month + //6-year + //7-time + //8-am/pm + //9- start of filename + Result := (s.Count > 8) and IsNumeric(s[6]) and IsHHMMSS(s[7], ':') and + (TextIsSame(s[8], 'AM') or TextIsSame(s[8], 'PM')); {do not localize} + finally + FreeAndNil(s); + end; +end; + +function IsValidNovellPermissionStr(const AStr : String): Boolean; +const + PermSet = '-RWCEAFMS'; {do not localize} +var + i : Integer; +begin + Result := False; + if AStr = '' then begin + Exit; + end; + for i := 1 to Length(AStr) do begin + if not CharIsInSet(AStr, i, PermSet) then begin + Exit; + end; + end; + Result := True; +end; + +function ExtractNovellPerms(const AData : String) : String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//extract the Novell Netware permissions from the enclosing brackets +var + LOpen, LClose : Integer; +begin + Result := ''; + LOpen := IndyPos('[', AData); {Do not translate} + LClose := IndyPos(']', AData); {Do not translate} + if (LOpen <> 0) and (LClose <> 0) and (LOpen < LClose) then begin + Result := Copy(AData, LOpen+1, LClose-LOpen-1); + end; + Result := Trim(Result); +end; + +//===== QVT/NET + +function ExcludeQVNET(const AData : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//A few tests will return a false positive with WinQVTNet +//This function prevents this. +begin + Result := (not IsMMDDYY(Copy(AData, 36, 10), '-')) or + (Copy(AData, 46, 1) <> ' ') or (not IsHHMMSS(Copy(AData, 47, 5), ':')); +end; + +function ExtractQVNETFileName(const AData : String): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +//This is for WinQVT/Net v3.9 - note filenames are in a 8.3 format +//but unlike the standard MS-DOS form, spaces will appear if running +//on Win32 Operating systems and filenames have spaces. Note that +//long file names will not appear at all. I found this out with a rigged test case. +var + LBuf : String; +begin + LBuf := Copy(AData, 1, 12); + Result := Fetch(LBuf, '.'); + LBuf := Trim(LBuf); + if LBuf <> '' then begin + Result := Result + '.' + Fetch(LBuf); + end; + Result := Fetch(Result, '/'); +end; + +//===== Mainframe support +function ExtractRecFormat(const ARecFM : String): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := ARecFM; + if TextStartsWith(Result, '<') then begin + IdDelete(Result, 1, 1); + end; + if TextEndsWith(Result, '>') then begin + Result := Fetch(Result, '>'); + end; +end; +//===== IBM VSE Power Queue +function DispositionCodeToTIdVSEPQDisposition(const ADisp : Char) : TIdVSEPQDisposition; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + case ADisp of + 'A' : Result := IdPQAppendable; + 'D' : Result := IdPQProcessAndDelete; + 'H' : Result := IdPQHoldUntilReleased; + 'K' : Result := IdPQProcessAndKeep; + 'L' : Result := IdPQLeaveUntilReleased; + 'X' : Result := IdPQErrorHoldUntilDK;//(Local only) Hold until disposition is changed to D or K. Temporarily assigned by VSE/POWER when processing fails. + 'Y' : Result := IdPQGetOrErrorHoldUntilDK; + '*' : Result := IdPQJobProcessing; + //only valid for some local jobs being created + 'I' : Result := IdPQSpoolOutputToInputD; + 'N' : Result := IdPQSurpressOutputSpooling; + 'T' : Result := IdPQSpoolOutputToTape; + else + Result := IdPQProcessAndDelete; + end; +end; + +function TIdVSEPQDispositionDispositionCode(const ADisp : TIdVSEPQDisposition) : Char; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + case ADisp of + IdPQAppendable : Result := 'A'; + IdPQProcessAndDelete : Result := 'D'; + IdPQHoldUntilReleased : Result := 'H'; + IdPQProcessAndKeep : Result := 'K'; + IdPQLeaveUntilReleased : Result := 'L'; + IdPQErrorHoldUntilDK : Result := 'X'; + IdPQGetOrErrorHoldUntilDK : Result := 'Y'; + IdPQJobProcessing : Result := '*'; + //only valid for some local jobs being created + IdPQSpoolOutputToInputD : Result := 'I'; + IdPQSurpressOutputSpooling : Result := 'N'; + IdPQSpoolOutputToTape : Result := 'T' ; + else + Result := 'D'; + end; +end; + +function IsVMBFS(AData : String) : Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + s : TStringList; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count > 4 then begin + Result := (s[2] = 'F') or (s[2] = 'D'); + if Result then begin + Result := IsNumeric(s[4]) or (s[4] = '-'); + end; + end; + finally + FreeAndNil(s); + end; +end; + + +//===== EPLF formats +function ParseFacts(AData : String; AResults : TStrings; + const AFactDelim : String = ';'; const ANameDelim : String = ' '): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : String; +begin + LBuf := Fetch(AData, ANameDelim); + Result := AData; + AResults.Clear; + repeat + AResults.Add(Fetch(LBuf, AFactDelim)); + until LBuf = ''; +end; + +//===== MLSD Parse facts, this has to be different because of different charsets +function ParseFactsMLS(AData : String; AResults : TStrings; + const AFactDelim : String = ';'; const ANameDelim : String = ' '): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LBuf : TIdBytes; + LCharSet : String; + LEncoding: IIdTextEncoding; +begin + LEncoding := IndyTextEncoding_8Bit; + LBuf := ToBytes(ParseFacts(AData, AResults, AFactDelim, ANameDelim), LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); + LCharSet := AResults.Values['charset']; + if LCharSet = '' then begin + LCharSet := 'UTF-8'; + end; + try + Result := BytesToString(LBuf, CharsetToEncoding(LCharSet)); + except + Result := BytesToString(LBuf, LEncoding); + end; +end; + + +{Sterling Commerce support routines} + +{ +based on information found in: +"Connect:Enterprise UNIX Remote Users Guide Version 2.1 " Copyright +1999, 2002, 2003 Sterling Commerce, Inc. + +} +const + CValidFlags = 'ACDEGIMNPRTUXS'; //not sure about the S {Do not translate} + CWhiteSpace = ' -'; + + CSterThreeCharProt : array [0..7] of string = + ('TCP','BSC','FTP','FTP','HTTP','ASY','AS2','FTS'); {Do not translate} + CSterOneCharProt : array [0..6] of string = + ( 'A', 'B', 'F', 'G', 'H', 'Q', 'W'); {Do not translate} + CSterThreeCharDataFlag : array [0..2] of string = + ('BIN','ASC','EBC'); {Do not translate} + CSterOneCharDataFlag : array [0..2] of string = + ( 'Y', 'Z', 'K'); {Do not translate} + +function RawIsValidSterPattern(const AString : String; AOneChar, AThreeChar : array of String) : Boolean; +begin + Result := False; + if AString = '' then begin + Exit; + end; + if Length(AString) = 3 then begin + if AString = '---' then begin + Result := True; + end; + if PosInStrArray(AString, AThreeChar) > -1 then begin + Result := True; + end; + end; + if Length(AString) = 1 then begin + if PosInStrArray(AString, AOneChar) > -1 then begin + Result := True; + end; + end; +end; + +function IsValidSterCommFlags(const AString : String) : Boolean; +var + i : Integer; +begin + Result := False; + if AString = '' then begin + Exit; + end; + for i := 1 to Length(AString) do begin + if (IndyPos(AString[i], CValidFlags) = 0) and + (IndyPos(AString[i], CWhiteSpace) = 0) then begin + Exit; + end; + end; + Result := True; +end; + +function IsValidSterCommProt(const AString : String) : Boolean; +begin + Result := RawIsValidSterPattern(AString, CSterOneCharProt, CSterThreeCharProt); +end; + +function IsValidSterCommData(const AString : String) : Boolean; +begin + Result := RawIsValidSterPattern(AString, CSterOneCharDataFlag, CSterThreeCharDataFlag); +end; + +end. diff --git a/indy/Protocols/IdFTPList.pas b/indy/Protocols/IdFTPList.pas new file mode 100644 index 0000000..1efde53 --- /dev/null +++ b/indy/Protocols/IdFTPList.pas @@ -0,0 +1,621 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.73 3/23/2005 5:17:36 AM JPMugaas + Removed some unused stuff. + + Rev 1.72 2/23/2005 6:34:30 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.71 10/26/2004 9:19:14 PM JPMugaas + Fixed references. + + Rev 1.70 6/14/2004 12:05:50 AM JPMugaas + Added support for the following Item types that appear in some Unix listings + (particularly a /dev or /tmp dir): + + FIFO, Socket, Character Device, Block Device. + + Rev 1.69 4/19/2004 5:04:58 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.68 2004.02.03 5:44:44 PM czhower + Name changes + + Rev 1.67 24/01/2004 19:16:42 CCostelloe + Cleaned up warnings + + Rev 1.66 1/22/2004 4:19:04 PM SPerry + fixed set problems + + Rev 1.65 11/26/2003 6:22:12 PM JPMugaas + IdFTPList can now support file creation time for MLSD servers which support + that feature. I also added support for a Unique identifier for an item so + facilitate some mirroring software if the server supports unique ID with EPLF + and MLSD. + + Rev 1.64 3/9/2003 12:01:14 PM JPMugaas + Now can report errors in recursive lists. + Permissions work better. + + Rev 1.63 2/21/2003 06:54:26 PM JPMugaas + The FTP list processing has been restructured so that Directory output is not + done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so + that the code is more scalable. + + Rev 1.62 2/17/2003 11:10:12 PM JPMugaas + Cisco IOS now supported. + + Rev 1.61 2/17/2003 04:43:44 PM JPMugaas + TOPS20 support + + Rev 1.60 2/15/2003 10:29:16 AM JPMugaas + Added support for some Unix specific facts with MLSD and MLST. + + Rev 1.59 2/14/2003 05:41:44 PM JPMugaas + Moved everything from IdFTPUtils to IdFTPCommon at Kudzu's suggestion. + + Rev 1.58 2/13/2003 10:04:28 PM JPMugaas + Moved some routines out of this unit and into a separate place so the code + could be reused better. + + Rev 1.57 2/13/2003 01:31:56 AM JPMugaas + Made sure MLSD output is valid. I also reordered the facts in the MLSD + output to match with the FEAT listing. + + Rev 1.56 2/12/2003 12:23:44 PM JPMugaas + Bug Fixes: + 1) Blank line starting a recursive list. + 2) The ADetails parameter was working in the opposit as it should have been. + + Rev 1.55 2/12/2003 07:06:06 AM JPMugaas + Now honors the following parameters: + + x - display entrie in columns accross + C - diplay entries in columns downward + l - long format - standard for many FTP Servers + 1 - one entry per line. + + Rev 1.54 2/11/2003 06:50:34 PM JPMugaas + Now supports the following: + + -X sort by extension + -S soft by file size. + + More consistant with NcFTP Deamon. + + Rev 1.53 2/11/2003 03:43:00 PM JPMugaas + Q - put filename in quotes + m - list files and dirs in a comma separated list + + are now supported. + + Rev 1.52 2/10/2003 03:05:14 PM JPMugaas + Now can read Unix lists with both the owner and group missing. This can if + the use sent the -g and -o switches. + + Rev 1.51 2/10/2003 02:09:28 PM JPMugaas + Now can properly process dir output using -s and or -i switches. Note that + Inote and Block count may not be parsed properly but it shouldn't cause an + error. + + Rev 1.50 2/8/2003 12:43:24 PM JPMugaas + BugFix: In Unix, a time is only reported if the modified date is within 6 + monthes of the current date. The code assumed that the time is always given + if it's in the same year as the date (not always so). + + http://www.opengroup.org/onlinepubs/007908799/xbd/utilconv.html#usg + + Rev 1.49 2/8/2003 11:43:48 AM JPMugaas + Made note about LineSet appearing to have two '-'. There are just two + charactors that are sometimes rendered the same in some programs. + + Rev 1.48 2/8/2003 10:41:30 AM JPMugaas + inode support for /bin/ls output. + + Rev 1.47 2/8/2003 04:37:16 AM JPMugaas + Rewrote the dir sorting for recurive stuff so that it is more flexible and + permits more sorting options. Added support for the "t" parameter and the + /bin/ls "f" parameter. + + Rev 1.46 2/5/2003 10:13:38 PM JPMugaas + Added -o, -g, -p parameter support and refined the -F parameter so executable + file names are followed by a '*'. + + Rev 1.44 2/5/2003 02:42:10 PM JPMugaas + Now supports the -r parameter for reverse-sorting lists even with recursive + dirs. + + Rev 1.43 2/5/2003 08:43:48 AM JPMugaas + Added a ExportLSSwitches property to control the output of Unix list data + export. I also added support for the following switches: + + s - print blocks in List + F - add a / to the end of dirs and symbolic links pointing to dirs + A - surpress . and .. entries. + + The switches are the same for the /bin/ls command but we may not support them + all. + + More will follow. + Fixed a bug where the file being pointed to was not exported. + + Rev 1.42 2/4/2003 05:32:52 PM JPMugaas + Added ability to export recursive lists in both Unix and MS-DOS styles. + + Rev 1.41 2/4/2003 09:35:40 AM JPMugaas + Now handles Microsoft IIS DIR style with recursive listing. + Fix for Recursion with NcFTP and MIcrosoft IIS in Unix DIRSTYLE mode. + Fixed Novell Unix Print Services detction and Unix detection. + + Rev 1.40 2/3/2003 11:03:36 AM JPMugaas + Now exports the directory listing into TStrings. Also made it possible to + export a "total x" line for Unix style dirs. + + Rev 1.39 1/27/2003 02:53:18 AM JPMugaas + Fixed bug that I mistakenly introduced. + + Rev 1.38 1/27/2003 02:09:08 AM JPMugaas + Now, all permission feilds are empty at creation. If the Unix permission + feilds are empty when emulating Unix, default permissions will be given. + + Rev 1.37 1/27/2003 01:07:44 AM JPMugaas + In Unix, a list item was exported with two spaces between a year and the + filename instead of one space separating both items. + + Rev 1.35 1/27/2003 12:16:02 AM JPMugaas + Minor adjustment to the MS-DOS DIR output. It turns out that part of + the listing and the file size were off by one because only a and p were + returned instead of AM/PM. In addition, the filenames were separated from + the file size by two spaces instead of one space. + + Rev 1.34 1/26/2003 04:58:36 PM JPMugaas + Expanded HP3000 parser slightly. + + Rev 1.33 1/26/2003 02:36:22 AM JPMugaas + Removed RecType type. It was getting too ownerous to manage for MUSIC, + VM/CMS, and MVS systems. + Added more MVS specific properties in case a developer wishes to use them in + a properties dialog-box. + + Rev 1.32 1/25/2003 07:31:34 PM JPMugaas + Added support for MUSIC SP FTP Server. + + Rev 1.31 1/20/2003 04:27:24 PM JPMugaas + Now handles where JES Interface 1 indicates no jobs are available. + + Rev 1.30 1/20/2003 03:13:06 PM JPMugaas + Unix parser now works with a Axis NPS 53X FTP Printer Server. + + Rev 1.29 1/20/2003 12:55:06 PM JPMugaas + Fixed sybmolic link detection problem I introduced when applying a patch for + other file types in Unix. + + Rev 1.28 1/19/2003 11:10:12 PM JPMugaas + Changed LoadList so it does not do anything if the FormatType is flfNextLine. + + Changed the Total function so it can will ignore a "Total:" so a recursive + Unix listing will not ignore a "total:" subdirectory indicator. + + Rev 1.27 1/14/2003 04:37:52 PM JPMugaas + Fixed misapplied patch for Unix parsing problem in /dev directories. + + Rev 1.26 1/13/2003 10:16:06 PM JPMugaas + Fixed Unix parser for a peculiarity in ls output with charactor devices in + the /dev hierarchy. Data submitted Jeff Eaton in + Unix-ftp.netscape.com-4.txt illustrates this. Unix format check function now + recognizes the following: + b - block device + c - character device + p - pipe + s - socket + + Rev 1.25 1/13/2003 12:21:28 AM JPMugaas + Expanded comment in MVS parser to explain why we do not support file size in + that format. + + Rev 1.24 1/12/2003 09:00:32 PM JPMugaas + Refined EPLF export. The export now tries to use ModifiedDateGMT and if + that's not available, it uses the ModifiedDate (assuming it's in the local + timezone). EPLF requires dates to be based on GMT time. + + Rev 1.22 1/6/2003 10:37:12 AM JPMugaas + Fixed bug with Novell Netware date interpretation. If the month was 1 and + the day was 16, the Netware parser would interpret it as 1-16-2003 (note that + 1-16-2003 has not yet arrived). + + Rev 1.21 1/4/2003 03:24:24 PM JPMugaas + MVS JES Interface 1 parser now does not use string positions at all. Spacing + is now used instead. More reliable that way. + + Rev 1.19 1/4/2003 01:38:40 PM JPMugaas + Reworked HellSoft FTP Server for Novell Support and detection. Now it is + detected separately from Novell Netware although it uses the same parser. + Expanded Novell Netware Print Services for UNIX so DOS namespace is + supported. It turned out that I mistakenly thaught that it was simply Novell + Netware with a NFS namespace. Renamed format types appropriately. + + Rev 1.18 1/4/2003 10:53:52 AM JPMugaas + Fixed parse bug in MVS. Sometimes, the first char in a filename was + mistakenly dropped. + Fixed MS-DOS parser. For a times such 12:15 AM, the date would be returned + as 12:15 PM. + Fixed a MVS Particianed Data bug. A 0 in the 3rd colum was interprettted as + current date with the year 2000 if there was only a "0" in that column. + + Rev 1.17 1/3/2003 6:55:36 PM JPMugaas + WinQVT/Net 3.98.15 support. + + Rev 1.16 12/30/2002 9:19:34 AM JPMugaas + Patch from Andrew P. Rybin for where the count column and the file size + column Unix are rammed together. + + Rev 1.15 12/30/2002 2:36:04 AM JPMugaas + Renamed the DIstinctPermissions to DistinctAttributes because those are just + the standard MS-DOS attributes (system, read-only, hidden, and archive) as + verified in my notes. + Refined the Distinct32 FTP server detection. + + Rev 1.14 12/29/2002 10:29:28 PM JPMugaas + Updated Distinct Parser for a sample dir list where no year was given. Added + ModifiedTimeGMT for cases where we can obtain a timestamp in GMT (Greenwhich + Mean Time). These cases currently are EPLF, Distinct32, and MLST/MLSD output. + + Rev 1.13 12/29/2002 7:23:56 AM JPMugaas + Modified VMS parser for UCX 3.3 support (VMS-Unknown-10.txt, + VMS-Unknown-11.txt). + + Added support for Distinct TCP/IP FTP Server-32 v. 3.0. + + Rev 1.12 12/19/2002 03:58:16 PM JPMugaas + Fixed an AV problem with a VM/CMS sample dir (case 10). It was due to a + blank line that should not have been passed to the parser. + + Rev 1.11 12/19/2002 01:29:58 PM JPMugaas + Microware OS/9 support. + + Rev 1.9 12/11/2002 05:52:18 PM JPMugaas + Fixed MS-DOS parser. A bug would be triggered with + "MS-DOS-MicrosoftFTP5.0-1.txt". The parser would locate the first 43 in a + seconds portion of the dir entry instead of the file size column which also + contained 43. Thanks, Jeff Easton for reporting this little gem. Also + removed some unneeded variables from the MS-DOS parser. + + Rev 1.8 12/11/2002 03:37:06 PM JPMugaas + Added LocalFileName property and the parsers now set this. This property is + a suggested filename for saving the file in the local system. Pathes are + removed from the FileName property and the version mark is stripped with VMS + FTP Servers. + + Rev 1.7 12/9/2002 09:34:38 PM JPMugaas + Novel Netware with NFS Volume namespace was not working as expected. A space + at position in Unknown 1 and 2 was throwing things off. I simplified the + logic and refined the detection further. + + Rev 1.6 12/9/2002 06:57:50 PM JPMugaas + Added a new symbolic type for cases where a Unix server would return a / at + the the end of the LinkedTo file name for a dir (clarifying if a link points + to a file or a dir). If using the DIR -F, some dir names will have a / at + the end and executable programs may have a * at the end. Updated the UNIX + parser for new -F param. support. Note that the -F parameter is from the ls + command. Most servers get dir lists simply by piping output from the /bin/ls + command. NcFTP server will also simulate the ls output. + + Rev 1.5 12/7/2002 03:20:10 PM JPMugaas + NCSA FTP server for MS-DOS - I hope. I think it is included in the Telnet + package. + + Rev 1.4 12/6/2002 08:46:34 PM JPMugaas + KA9Q Support. KA9Q is a set of Internet programs for MS-DOS including a FTP + server. This was popular in the late 1980's and early 1990's. It's not in + use much anymore but might be used by Ham radio operators. + + Rev 1.3 12/1/2002 04:20:56 PM JPMugaas + added flfNextLine to handle cases where we can't determine the format of a + dir with a particular line returned by the server. Expanded Unix Parser to + also handle Unitree FTP servers. We now handle Unitree servers and I have + verified that Unix ls -l * output now works (note that many Unix servers + simply pipe output from that program). + + Rev 1.0 11/13/2002 08:28:58 AM JPMugaas + Initial import from FTP VC. + + Apr.2002 + - Fixed bug with MSDos Listing format - space in front of file names. + + Sep.2001 & Jan.2002 + - Merged changes submitted by Andrew P.Rybin + + 2002 - Aug-23 - J. Peter Mugaas + - fixed a parsing bug in all parsers. A file name begging with a space will + throw off the parsers. Modified VMS parser to permit file names containing spaces + + 2002 - Aug-22 - J. Peter Mugaas + - VM/CMS - now returns OwnerName - I think. + - Added RecType for VM/CMS. + - Renamed BlockSize to NumberBlocks. Note: Block size in VMS is usually 512 anyway + (we hard-code that for a constant) and in VM/CMS, the block size is either + 800, 512, 1024, 2048, or 4096 at the whim of the user and we can't get the + block size from the DIR listing. In other words, any block size property is + useless. + - Changed VMS behvioar to be consistant with this. + - Insider Privillages property added to TIdFTPListItem. This is the + OwnerPermissions for Novell Netware. Note that Novell Privillages are far different + than Unix permissions so they belong in a different property. + - added VMS file owner and group. + See: http://seqaxp.bio.caltech.edu/www/vms_beginners_faq.html#FILE00 + - VMS file protections (permissions). + See: http://www.djesys.com/vms/freevms/mentor/vms_prot.html#prvs + + 2002 - Aug-20 - J. Peter Mugaas + - Added Novell Netware directory parsing. + - Rewrote IdFTPList Novell Netware parsing. File names with spaces are now + properly handled. The code also has a side effect of stripping out a zero + that occurred in a directory that was probably due to a quirk. + + 2002 - Aug-19 - J. Peter Mugaas + - Improved VMS Directory partsing. It NO LONGER is dependant upon specific + column widthes. + - Fixed bugs in VM file parsing and determination. + - Now handles multiline VMS file list entries. + + 2002 - Aug-18 - J. Peter Mugaas + - VM/CMS or VM/ESA Mainframe directory format parsing added + - VMS parsing added + + February 2001 + - TFTPListItem now descends from TCollectionItem + - TFTPList now descends from TCollection + + Jun 2001 + - Fixes in UNIX format parser + + Aug 2001 + - It is now used in the FTP server component +} + +unit IdFTPList; + +{ + NOTE: For this class, I recommend that you read some sections in the + + Operating Systems Handbook + + The book is out of print but is freely available at: + http://www.snee.com/bob/opsys.html + + - Fixes as per user request for parsing non-detailed lists (SP). + [Added flfNoDetails list format]. + + Initial version by + D. Siders + Integral Systems + October 2000 + + Additions and extensions + A Neillans + Doychin Bondzhev (doychin@dsoft-bg.com) + dSoft-Bulgaria +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, IdException, IdFTPCommon; + +{ Indy TIdFtp extensions to support automatic parsing of FTP directory listings } + +type + EIdInvalidFTPListingFormat = class(EIdException); + + TIdDirItemType = (ditDirectory, ditFile, ditSymbolicLink, ditSymbolicLinkDir, + ditBlockDev, ditCharDev, ditFIFO, ditSocket); + + TIdFTPFileName = TIdUnicodeString; + + TIdFTPListItems = class; + + // TIdFTPListItem stores an item in the FTP directory listing + TIdFTPListItem = class(TCollectionItem) + protected + FSize: Int64; + FData: string; + FFileName: TIdFTPFileName; + FLocalFileName : TIdFTPFileName; //suggested file name for local file + FSizeAvail : Boolean; + FModifiedAvail : Boolean; + FModifiedDate: TDateTime; + + //the item below is for cases such as MLST output, EPLF, and Distinct format + //which usually reports dates in GMT + FModifiedDateGMT : TDateTime; + //Creation time values are for MLSD data output and can be returned by the + //the MLSD parser in some cases + + FItemType: TIdDirItemType; + //an error has been reported in the DIR listing itself for an item + FDirError : Boolean; + //Permission Display + FPermissionDisplay : String; + //this will very amoung platforms, do not use for CHMOD + //The format veries amoung systems. This is only provided for people + //wanting to display a "permission column in their FTP listing + //property set methods + procedure SetFileName(const AValue : TIdFTPFileName); + //may be used by some descendent classes + property ModifiedDateGMT : TDateTime read FModifiedDateGMT write FModifiedDateGMT; + public + constructor Create(AOwner: TCollection); override; + procedure Assign(Source: TPersistent); override; + + property Data: string read FData write FData; + + property Size: Int64 read FSize write FSize; + property ModifiedDate: TDateTime read FModifiedDate write FModifiedDate; + + property FileName: TIdFTPFileName read FFileName write SetFileName; + property LocalFileName : TIdFTPFileName read FLocalFileName write FLocalFileName; + property ItemType: TIdDirItemType read FItemType write FItemType; + property SizeAvail : Boolean read FSizeAvail write FSizeAvail; + property ModifiedAvail : Boolean read FModifiedAvail write FModifiedAvail; + + //Permission Display + property PermissionDisplay : String read FPermissionDisplay write FPermissionDisplay; + end; + + TIdFTPListOnGetCustomListFormat = procedure(AItem: TIdFTPListItem; var VText: string) of object; + TIdOnParseCustomListFormat = procedure(AItem: TIdFTPListItem) of object; + + // TFTPList is the container and parser for items in the directory listing + TIdFTPListItems = class(TCollection) + protected + FDirectoryName: string; + // + procedure SetDirectoryName(const AValue: string); + function GetItems(AIndex: Integer): TIdFTPListItem; + procedure SetItems(AIndex: Integer; const Value: TIdFTPListItem); + public + function Add: TIdFTPListItem; + constructor Create; reintroduce; + function IndexOf(AItem: TIdFTPListItem): Integer; + property DirectoryName: string read FDirectoryName write SetDirectoryName; + property Items[AIndex: Integer]: TIdFTPListItem read GetItems write SetItems; default; + end; + +implementation + +uses + IdContainers, IdResourceStrings, IdStrings, SysUtils; + +{ TFTPListItem } + +constructor TIdFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + Data := ''; {Do not Localize} + FItemType := ditFile; + Size := 0; + ModifiedDate := 0.0; + FFileName := ''; {Do not Localize} + FLocalFileName := ''; + FSizeAvail := True; + FModifiedAvail := True; +end; + +procedure TIdFTPListItem.Assign(Source: TPersistent); +var + LSource: TIdFTPListItem; +begin + if Source is TIdFTPListItem then begin + LSource := TIdFTPListItem(Source); + Data := LSource.Data; + ItemType := LSource.ItemType; + Size := LSource.Size; + ModifiedDate := LSource.ModifiedDate; + ModifiedDateGMT := LSource.ModifiedDateGMT; + FFileName := LSource.FileName; + FLocalFileName := LSource.LocalFileName; + SizeAvail := LSource.SizeAvail; + ModifiedAvail := LSource.ModifiedAvail; + PermissionDisplay := LSource.PermissionDisplay; + FDirError := LSource.FDirError; + end else begin + inherited Assign(Source); + end; +end; + +{ TFTPList } + +constructor TIdFTPListItems.Create; +begin + inherited Create(TIdFTPListItem); +end; + +function TIdFTPListItems.Add: TIdFTPListItem; +begin + Result := TIdFTPListItem(inherited Add); +end; + +function TIdFTPListItems.GetItems(AIndex: Integer): TIdFTPListItem; +begin + Result := TIdFTPListItem(inherited Items[AIndex]); +end; + +function TIdFTPListItems.IndexOf(AItem: TIdFTPListItem): Integer; +Var + i: Integer; +begin + for i := 0 to Count - 1 do + begin + if AItem = Items[i] then + begin + Result := i; + Exit; + end; + end; + Result := -1; +end; + +procedure TIdFTPListItems.SetItems(AIndex: Integer; const Value: TIdFTPListItem); +begin + inherited Items[AIndex] := Value; +end; + +procedure TIdFTPListItems.SetDirectoryName(const AValue: string); +begin + if not TextIsSame(FDirectoryName, AValue) then begin + FDirectoryName := AValue; + Clear; + end; +end; + +procedure TIdFTPListItem.SetFileName(const AValue: TIdFTPFileName); +var + i : Integer; + LDoLowerCase : Boolean; +const + LLowCase = 'abcdefghijklmnpqrstuvwxyz'; {do not localize} +begin + if (FLocalFileName = '') or TextIsSame(FFileName, FLocalFileName) then begin + //we do things this way because some file systems use all capital letters or are + //case insensivite. The Unix file is case sensitive and Unix users tend to + //prefer lower case filenames. We do not want to force lowercase if a file + //has both uppercase and lowercase because the uppercase letters are probably intentional + LDoLowerCase := True; + // TODO: add IsLower() functions in IdGlobal/Protocol? + for i := 1 to Length(AValue) do begin + if CharIsInSet(AValue, i, LLowCase) then begin + LDoLowerCase := False; + Break; + end; + end; + if LDoLowerCase then begin + FLocalFileName := LowerCase(AValue); + end else begin + FLocalFileName := AValue; + end; + end; + FFileName := AValue; +end; + +end. diff --git a/indy/Protocols/IdFTPListOutput.pas b/indy/Protocols/IdFTPListOutput.pas new file mode 100644 index 0000000..5cdd3c3 --- /dev/null +++ b/indy/Protocols/IdFTPListOutput.pas @@ -0,0 +1,1656 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.18 12/10/04 1:13:34 PM RLebeau + FormatDateTime() fixes. Was using 'mm' instead of 'nn' for minutes. + + Rev 1.17 10/26/2004 9:36:26 PM JPMugaas + Updated ref. + + Rev 1.16 10/26/2004 9:19:14 PM JPMugaas + Fixed references. + + Rev 1.15 10/1/2004 6:17:12 AM JPMugaas + Removed some dead code. + + Rev 1.14 6/27/2004 1:45:36 AM JPMugaas + Can now optionally support LastAccessTime like Smartftp's FTP Server could. + I also made the MLST listing object and parser support this as well. + + Rev 1.13 6/11/2004 9:34:44 AM DSiders + Added "Do not Localize" comments. + + Rev 1.12 4/19/2004 5:06:02 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.11 2004.02.03 5:45:34 PM czhower + Name changes + + Rev 1.10 24/01/2004 19:18:48 CCostelloe + Cleaned up warnings + + Rev 1.9 1/4/2004 12:09:54 AM BGooijen + changed System.Delete to IdDelete + + Rev 1.8 11/26/2003 6:23:44 PM JPMugaas + Quite a number of fixes for recursive dirs and a few other things that + slipped my mind. + + Rev 1.7 10/19/2003 2:04:02 PM DSiders + Added localization comments. + + Rev 1.6 3/11/2003 07:36:00 PM JPMugaas + Now reports permission denied in subdirs when doing recursive listts in Unix + export. + + Rev 1.5 3/9/2003 12:01:26 PM JPMugaas + Now can report errors in recursive lists. + Permissions work better. + + Rev 1.4 3/3/2003 07:18:34 PM JPMugaas + Now honors the FreeBSD -T flag and parses list output from a program using + it. Minor changes to the File System component. + + Rev 1.3 2/26/2003 08:57:10 PM JPMugaas + Bug fix. The owner and group should be left-justified. + + Rev 1.2 2/24/2003 07:24:00 AM JPMugaas + Now honors more Unix switches just like the old code and now work with the + NLIST command when emulating Unix. -A switch support added. Switches are + now in constants. + + Rev 1.1 2/23/2003 06:19:42 AM JPMugaas + Now uses Classes instead of classes. + + Rev 1.0 2/21/2003 06:51:46 PM JPMugaas + FTP Directory list output object for the FTP server. +} + +unit IdFTPListOutput; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList; + +type + // we can't use the standard FTP MLSD option types in the FTP Server + // because we support some minimal things that the user can't set. + // We have the manditory items to make it harder for the user to mess up. + + TIdFTPFactOutput = (ItemType, Modify, Size, Perm, Unique, UnixMODE, UnixOwner, + UnixGroup, CreateTime, LastAccessTime, WinAttribs,WinDriveType,WinDriveLabel); + + TIdFTPFactOutputs = set of TIdFTPFactOutput; + + TIdDirOutputFormat = (doUnix, doWin32, doEPLF); + + TIdFTPListOutputItem = class(TIdFTPListItem) + protected + FLinkCount: Integer; + FGroupName: string; + FOwnerName : String; + FLinkedItemName : string; + FNumberBlocks : Integer; + FInode : Integer; + FLastAccessDate: TDateTime; + FLastAccessDateGMT: TDateTime; + FCreationDate: TDateTime; + FCreationDateGMT : TDateTime; + //Unique ID for an item to prevent yourself from downloading something twice + FUniqueID : String; + //MLIST things + FMLISTPermissions : String; + + FUnixGroupPermissions: string; + FUnixOwnerPermissions: string; + FUnixOtherPermissions: string; + FUnixinode : Integer; + + FWinAttribs : UInt32; + //an error has been reported in the DIR listing itself for an item + FDirError : Boolean; + + FWinDriveType : Integer; + FWinDriveLabel : String; + public + constructor Create(AOwner: TCollection); override; + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + property Inode : Integer read FInode write FInode; + //Last Access time values are for MLSD data output and can be returned by the MLST command + property LastAccessDate: TDateTime read FLastAccessDate write FLastAccessDate; + property LastAccessDateGMT : TDateTime read FLastAccessDateGMT write FLastAccessDateGMT; + //Creation time values are for MLSD data output and can be returned by the MLST command + property CreationDate: TDateTime read FCreationDate write FCreationDate; + property CreationDateGMT : TDateTime read FCreationDateGMT write FCreationDateGMT; + // If this is not blank, you can use this as a unique identifier for an item to prevent + // yourself from downloading the same item twice (which is not easy to see with some + // some FTP sites where symbolic links or similar things are used. + //Valid only with EPLF and MLST + property UniqueID : string read FUniqueID write FUniqueID; + //Creation time values are for MLSD data output and can be returned by the + //the MLSD parser in some cases + property ModifiedDateGMT; + //Windows NT File Attributes (just like what is reported by RaidenFTPD + //BlackMoon FTP Server, and Serv-U + //On the server side, you deal with it as a number right from the Win32 FindFirst, + //FindNext functions. Easy + property WinAttribs : UInt32 read FWinAttribs write FWinAttribs; + + property WinDriveType : Integer read FWinDriveType write FWinDriveType; + property WinDriveLabel : String read FWinDriveLabel write FWinDriveLabel; + //MLIST Permissions + property MLISTPermissions : string read FMLISTPermissions write FMLISTPermissions; + property UnixOwnerPermissions: string read FUnixOwnerPermissions write FUnixOwnerPermissions; + property UnixGroupPermissions: string read FUnixGroupPermissions write FUnixGroupPermissions; + property UnixOtherPermissions: string read FUnixOtherPermissions write FUnixOtherPermissions; + property LinkCount: Integer read FLinkCount write FLinkCount; + property OwnerName: string read FOwnerName write FOwnerName; + property GroupName: string read FGroupName write FGroupName; + property LinkedItemName : string read FLinkedItemName write FLinkedItemName; + property DirError : Boolean read FDirError write FDirError; + end; + + TIdFTPListOutput = class(TCollection) + protected + FSwitches : String; + FOutput : String; + FDirFormat : TIdDirOutputFormat; + FExportTotalLine : Boolean; + function GetLocalModTime(AItem : TIdFTPListOutputItem) : TDateTime; virtual; + function HasSwitch(const ASwitch: String): Boolean; + function UnixItem(AItem : TIdFTPListOutputItem) : String; virtual; + function Win32Item(AItem : TIdFTPListOutputItem) : String; virtual; + function EPLFItem(AItem : TIdFTPListOutputItem) : String; virtual; + function NListItem(AItem : TIdFTPListOutputItem) : String; virtual; + function MListItem(AItem : TIdFTPListOutputItem; AMLstOpts : TIdFTPFactOutputs) : String; virtual; + procedure InternelOutputDir(AOutput : TStrings; ADetails : Boolean = True); virtual; + function UnixINodeOutput(AItem : TIdFTPListOutputItem) : String; + function UnixBlocksOutput(AItem : TIdFTPListOutputItem) : String; + function UnixGetOutputOwner(AItem : TIdFTPListOutputItem) : String; + function UnixGetOutputGroup(AItem : TIdFTPListOutputItem) : String; + function UnixGetOutputOwnerPerms(AItem : TIdFTPListOutputItem) : String; + function UnixGetOutputGroupPerms(AItem : TIdFTPListOutputItem) : String; + function UnixGetOutputOtherPerms(AItem : TIdFTPListOutputItem) : String; + + function GetItems(AIndex: Integer): TIdFTPListOutputItem; + procedure SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem); + + public + function Add: TIdFTPListOutputItem; + constructor Create; reintroduce; + function IndexOf(AItem: TIdFTPListOutputItem): Integer; + property Items[AIndex: Integer]: TIdFTPListOutputItem read GetItems write SetItems; default; + + procedure LISTOutputDir(AOutput : TStrings); virtual; + procedure MLISTOutputDir(AOutput : TStrings; AMLstOpts : TIdFTPFactOutputs); virtual; + procedure NLISTOutputDir(AOutput : TStrings); virtual; + + property DirFormat : TIdDirOutputFormat read FDirFormat write FDirFormat; + property Switches : String read FSwitches write FSwitches; + property Output : String read FOutput write FOutput; + property ExportTotalLine : Boolean read FExportTotalLine write FExportTotalLine; + end; + +const + DEF_FILE_OWN_PERM = 'rw-'; {do not localize} + DEF_FILE_GRP_PERM = DEF_FILE_OWN_PERM; + DEF_FILE_OTHER_PERM = 'r--'; {do not localize} + DEF_DIR_OWN_PERM = 'rwx'; {do not localize} + DEF_DIR_GRP_PERM = DEF_DIR_OWN_PERM; + DEF_DIR_OTHER_PERM = 'r-x'; {do not localize} + DEF_OWNER = 'root'; {do not localize} + +{NLIST and LIST switches - based on /bin/ls } +{ + Note that the standard Unix form started simply by Unix + FTP deamons piping output from the /bin/ls program for both + the NLIST and LIST FTP commands. The standard /bin/ls + program has several standard switches that allow the output + to be customized. For our output, we wish to emulate this behavior. + + Microsoft IIS even honors a subset of these switches dealing sort order + and recursive listings. It does not honor some sort-by-switches although + we honor those in Win32 (hey, we did MS one better, not that it says much though. + +} +const + {format switches - used by Unix mode only} + SWITCH_COLS_ACCROSS = 'x'; + SWITCH_COLS_DOWN = 'C'; + SWITCH_ONE_COL = '1'; + SWITCH_ONE_DIR = 'f'; + SWITCH_COMMA_STREAM = 'm'; + SWITCH_LONG_FORM = 'l'; + {recursive for both Win32 and Unix forms} + SWITCH_RECURSIVE = 'R'; + {sort switches - used both by Win32 and Unix forms} + SWITCH_SORT_REVERSE = 'r'; + SWITCH_SORTBY_MTIME = 't'; + SWITCH_SORTBY_CTIME = 'u'; + SWITCH_SORTBY_EXT = 'X'; + SWITCH_SORTBY_SIZE = 'S'; + {Output switches for Unix mode only} + SWITCH_CLASSIFY = 'F'; + // +{ -F Put aslash (/) aftereach filename if the file is a directory, an + asterisk (*) if the file is executable, an equal sign(=) if the + file is an AF_UNIX address family socket, andan ampersand (@) if + the file is asymbolic link.Unless the -H option isalso used, + symbolic links are followed to see ifthey might be adirectory; see + above. + + From: + http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?ls+1 } + SWITCH_SLASHDIR = 'p'; + SWITCH_QUOTEDNAME = 'Q'; + SWITCH_PRINT_BLOCKS = 's'; + SWITCH_PRINT_INODE = 'i'; + SWITCH_SHOW_ALLPERIOD = 'a'; //show all entries even ones with a pariod starting the filename/hidden + //note that anything starting with a period is shown except for the .. and . entries for security reasons + SWITCH_HIDE_DIRPOINT = 'A'; //hide the "." and ".." entries + SWITCH_BOTH_TIME_YEAR = 'T'; //This is used by FTP Voyager with a Serv-U FTP Server to both + //a time and year in the FTP list. Note that this does conflict with a ls -T flag used to specify a column size + //on Linux but in FreeBSD, the -T flag is also honored. +implementation + +uses + //facilitate inlining only. + IdException, + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF VCL_XE3_OR_ABOVE} + {$IFNDEF NEXTGEN} + System.Contnrs, + {$ENDIF} + System.Types, + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysTime, + {$ENDIF} + IdContainers, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +type + {$IFDEF HAS_GENERICS_TObjectList} + TDirEntry = class; + TDirEntryList = TIdObjectList; + {$ELSE} + // TODO: flesh out to match TIdObjectList for non-Generics compilers + TDirEntryList = TIdObjectList; + {$ENDIF} + + TDirEntry = class(TObject) + protected + FPathName : String; + FDirListItem : TIdFTPListOutputItem; + FSubDirs : TDirEntryList; + FFileList : TIdBubbleSortStringList; + + public + constructor Create(const APathName : String; ADirListItem : TIdFTPListOutputItem); + destructor Destroy; override; +// procedure Sort(ACompare: TIdSortCompare;const Recurse : Boolean = True); + procedure SortAscendFName; + procedure SortDescendFName; + procedure SortAscendMTime; + procedure SortDescendMTime; + procedure SortAscendSize; + procedure SortDescendSize; + procedure SortAscendFNameExt; + procedure SortDescendFNameExt; + function AddSubDir(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean; + function AddFileName(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean; + property SubDirs : TDirEntryList read FSubDirs; + property FileList : TIdBubbleSortStringList read FFileList; + property PathName : String read FPathName; + property DirListItem : TIdFTPListOutputItem read FDirListItem; + end; + +function RawSortAscFName(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer; +var +{ +> 0 (positive) Item1 is less than Item2 += 0 Item1 is equal to Item2 +< 0 (negative) Item1 is greater than Item2 +} + LTmpPath1, LTmpPath2 : String; + LPath1Dot, LPath2Dot : Boolean; +begin + LTmpPath1 := IndyGetFileName(AItem1.FileName); + LTmpPath2 := IndyGetFileName(AItem2.FileName); + + //periods are always greater then letters in dir lists + LPath1Dot := TextStartsWith(LTmpPath1, '.'); + LPath2Dot := TextStartsWith(LTmpPath2, '.'); + + if LPath1Dot and LPath2Dot then begin + if (LTmpPath1 = CUR_DIR) and (LTmpPath2 = PARENT_DIR) then begin + Result := 1; + Exit; + end; + if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = PARENT_DIR) then begin + Result := -1; + Exit; + end; + if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = CUR_DIR) then begin + Result := 0; + Exit; + end; + if (LTmpPath2 = PARENT_DIR) and (LTmpPath1 = PARENT_DIR) then begin + Result := 0; + Exit; + end; + end; + if LPath2Dot and (not LPath1Dot) then begin + Result := -1; + Exit; + end; + if LPath1Dot and (not LPath2Dot) then begin + Result := 1; + Exit; + end; + Result := -IndyCompareStr(LTmpPath1, LTmpPath2); +end; + +function RawSortDescFName(AItem1, AItem2: TIdFTPListItem): Integer; +begin + Result := -RawSortAscFName(AItem1, AItem2); +end; + +function RawSortAscFNameExt(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer; +var +{ +> 0 (positive) Item1 is less than Item2 += 0 Item1 is equal to Item2 +< 0 (negative) Item1 is greater than Item2 +} + LTmpPath1, LTmpPath2 : String; +begin + LTmpPath1 := ExtractFileExt(AItem1.FileName); + LTmpPath2 := ExtractFileExt(AItem2.FileName); + Result := -IndyCompareStr(LTmpPath1, LTmpPath2); + if Result = 0 then begin + Result := RawSortAscFName(AItem1, AItem2); + end; +end; + +function RawSortDescFNameExt(AItem1, AItem2: TIdFTPListItem): Integer; +begin + Result := -RawSortAscFNameExt(AItem1, AItem2, False); +end; + +function RawSortAscMTime(AItem1, AItem2: TIdFTPListItem): Integer; +{ +> 0 (positive) Item1 is less than Item2 + 0 Item1 is equal to Item2 +< 0 (negative) Item1 is greater than Item2 +} + +begin + if AItem1.ModifiedDate < AItem2.ModifiedDate then begin + Result := -1; + end + else if AItem1.ModifiedDate > AItem2.ModifiedDate then begin + Result := 1; + end + else begin + Result := RawSortAscFName(AItem1, AItem2); + end; +end; + +function RawSortDescMTime(AItem1, AItem2: TIdFTPListItem): Integer; +begin + Result := -RawSortAscMTime(AItem1, AItem2); +end; + +function RawSortAscSize(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer; +var + LSize1, LSize2 : Int64; +{ +> 0 (positive) Item1 is less than Item2 += 0 Item1 is equal to Item2 +< 0 (negative) Item1 is greater than Item2 +} +begin + LSize1 := AItem1.Size; + LSize2 := AItem2.Size; + if TIdFTPListOutput(AItem1.Collection).DirFormat = doUnix then begin + if AItem1.ItemType = ditDirectory then begin + LSize1 := UNIX_DIR_SIZE; + end; + if AItem2.ItemType = ditDirectory then begin + LSize2 := UNIX_DIR_SIZE; + end; + end; + if LSize1 < LSize2 then begin + Result := -1; + end + else if LSize1 > LSize2 then begin + Result := 1; + end else begin + Result := RawSortAscFName (AItem1, AItem2); + end; +end; + +function RawSortDescSize(AItem1, AItem2: TIdFTPListItem): Integer; +begin + Result := -RawSortAscSize(AItem1, AItem2, False); +end; + +{DirEntry objects} +function DESortAscFName(AItem1, AItem2: TDirEntry): Integer; +begin + Result := -IndyCompareStr(AItem1.PathName, AItem2.PathName); +end; + +function DESortAscMTime(AItem1, AItem2: TDirEntry): Integer; +var + L1, L2 : TIdFTPListItem; +{ +> 0 (positive) Item1 is less than Item2 += 0 Item1 is equal to Item2 +< 0 (negative) Item1 is greater than Item2 +} +begin + L1 := AItem1.DirListItem; + L2 := AItem2.DirListItem; + if L1.ModifiedDate > L2.ModifiedDate then begin + Result := -1; + end + else if L1.ModifiedDate < L2.ModifiedDate then begin + Result := 1; + end else begin + Result := DESortAscFName(AItem1, AItem2); + end; +end; + +function DESortDescMTime(AItem1, AItem2: TDirEntry): Integer; +begin + Result := -DESortAscMTime(AItem1, AItem2); +end; + +function DESortDescFName(AItem1, AItem2: TDirEntry): Integer; +begin + Result := -DESortAscFName(AItem1, AItem2); +end; + +{stringlist objects} +function StrSortAscMTime(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortAscMTime( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortDescMTime(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortDescMTime( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortAscSize(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortAscSize( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortDescSize(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortDescSize( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortAscFName(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortAscFName( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortDescFName(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortDescFName( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortAscFNameExt(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortAscFNameExt( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +function StrSortDescFNameExt(List: TStringList; Index1, Index2: Integer): Integer; +begin + Result := RawSortDescFNameExt( + TIdFTPListItem(List.Objects[Index1]), + TIdFTPListItem(List.Objects[Index2])); +end; + +{ TIdFTPListOutput } + +function TIdFTPListOutput.Add: TIdFTPListOutputItem; +begin + Result := TIdFTPListOutputItem(inherited Add); +end; + +constructor TIdFTPListOutput.Create; +begin + inherited Create(TIdFTPListOutputItem); + FDirFormat := doUnix; +end; + +function TIdFTPListOutput.EPLFItem(AItem: TIdFTPListOutputItem): String; +var + LFileName : String; +begin + LFileName := IndyGetFileName(AItem.FileName); + if AItem.ModifiedDateGMT > EPLF_BASE_DATE then begin + Result := '+m' + GMTDateTimeToEPLFDate(AItem.ModifiedDateGMT); + end + else if AItem.ModifiedDate > EPLF_BASE_DATE then begin + Result := '+m'+LocalDateTimeToEPLFDate(AItem.ModifiedDate); + end else begin + Result := ''; + end; + if AItem.ItemType = ditFile then begin + Result := Result + ',r'; + end else begin + Result := Result + ',/'; + end; + Result := Result + ',s' + IntToStr(AItem.Size); + Result := Result + #9 + LFileName; +end; + +function TIdFTPListOutput.GetItems(AIndex: Integer): TIdFTPListOutputItem; +begin + Result := TIdFTPListOutputItem(inherited GetItem(AIndex)); +end; + +function TIdFTPListOutput.GetLocalModTime(AItem: TIdFTPListOutputItem): TDateTime; +begin + if AItem.ModifiedDateGMT <> 0 then begin + Result := AItem.ModifiedDateGMT - TimeZoneBias; + end else begin + Result := AItem.ModifiedDate; + end; +end; + +function TIdFTPListOutput.HasSwitch(const ASwitch: String): Boolean; +begin + Result := IndyPos(ASwitch, Switches) > 0; +end; + +function TIdFTPListOutput.IndexOf(AItem: TIdFTPListOutputItem): Integer; +var + i : Integer; +begin + Result := -1; + for i := 0 to Count - 1 do begin + if AItem = Items[i] then begin + Result := i; + Exit; + end; + end; +end; + +procedure TIdFTPListOutput.InternelOutputDir(AOutput: TStrings; ADetails: Boolean); +type + TIdDirOutputType = (doColsAccross, doColsDown, doOneCol, doOnlyDirs, doComma, doLong); +var + i : Integer; + //note we use this for sorting pathes with recursive dirs + LRootPath : TDirEntry; + LShowNavSym : BOolean; + + function DetermineOp : TIdDirOutputType; + //we do things this way because the last switch in a mutually exclusive set + //always takes precidence over the others. + var + LStopScan : Boolean; + li : Integer; + begin + if ADetails then begin + Result := doLong; + end else begin + Result := doOneCol; + end; + if DirFormat <> doUnix then begin + Exit; + end; + LStopScan := False; + for li := Length(Switches) downto 1 do begin + case Switches[li] of + SWITCH_COLS_ACCROSS : + begin + Result := doColsAccross; + LStopScan := True; + end; + SWITCH_COLS_DOWN : + begin + Result := doColsDown; + LStopScan := True; + end; + SWITCH_ONE_COL : + begin + Result := doOneCol; + LStopScan := True; + end; + SWITCH_ONE_DIR : + begin + Result := doOnlyDirs; + LStopScan := True; + end; + SWITCH_COMMA_STREAM : + begin + Result := doComma; + LStopScan := True; + end; + SWITCH_LONG_FORM : + begin + Result := doLong; + LStopScan := True; + end; + end; + if LStopScan then begin + Break; + end; + end; + end; + + procedure PrintSubDirHeader(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + LUnixPrependPath : Boolean; + begin + LUnixPrependPath := HasSwitch(SWITCH_SORT_REVERSE) or HasSwitch(SWITCH_SORTBY_MTIME) or (DetermineOp <> doLong); + + if (ACurDir <> ARoot) or LUnixPrependPath then begin + //we don't want an empty line to start the list + if ACurDir <> ARoot then begin + ALOutput.Add(''); + end; + if DirFormat = doWin32 then begin + ALOutput.Add(MS_DOS_CURDIR + UnixPathToDOSPath(ACurDir.PathName) + ':'); + end + else if LUnixPrependPath then begin + if ACurDir = ARoot then begin + ALOutput.Add(CUR_DIR + ':'); + end else begin + ALOutput.Add(UNIX_CURDIR + DOSPathToUnixPath(ACurDir.PathName) + ':'); + end; + end else begin + ALOutput.Add(DOSPathToUnixPath(ACurDir.PathName) + ':'); + end; + end; + end; + + procedure ProcessOnePathCol(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + li : Integer; + LCurItem : TIdFTPListOutputItem; + begin + if Recurse and Assigned(ACurDir.SubDirs) then begin + if Recurse then begin + PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse); + end; + end; + for li := 0 to ACurDir.FileList.Count-1 do begin + ALOutput.Add(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li]))); + end; + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count-1 do begin + LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem; + if LCurItem.DirError then begin + if li = 0 then begin + ALOutput.Add(''); + end; + ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize} + end else begin + ProcessOnePathCol(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + end; + end; + end; + end; + + function CalcMaxLen(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False) : Integer; + var + LEntryMaxLen : Integer; + li : Integer; + begin + Result := 0; + for li := 0 to ACurDir.FileList.Count-1 do begin + LEntryMaxLen := Length(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li]))); + if LEntryMaxLen > Result then begin + Result := LEntryMaxLen; + end; + end; + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count-1 do begin + LEntryMaxLen := CalcMaxLen(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + if LEntryMaxLen > Result then begin + Result := LEntryMaxLen; + end; + end; + end; + end; + + procedure ProcessPathAccross(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + li, j : Integer; + LTmp : String; + LMaxLen : Integer; + LCols : Integer; + LCurItem : TIdFTPListOutputItem; + begin + if ACurDir.FileList.Count = 0 then begin + Exit; + end; + //Note that we will assume a console width of 80 and we don't want something to wrap + //causing a blank line + LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse); + //if more than 39, we probably are going to exceed the width of the screen, + //just treat as one column + if LMaxLen > 39 then begin + ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse); + Exit; + end; + if Recurse and Assigned(ACurDir.SubDirs) then begin + if Recurse then begin + PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse); + end; + end; + LCols := 79 div (LMaxLen + 2);//2 spaces between columns + j := 0; + repeat + LTmp := ''; + for li := 0 to LCols -1 do begin + LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[j])), LMaxLen, ' ') + ' '; + Inc(j); + if j = ACurDir.FileList.Count then begin + Break; + end; + end; + ALOutput.Add(TrimRight(LTmp)); + until j = ACurDir.FileList.Count; + + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count-1 do begin + LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem; + if LCurItem.DirError then begin + if li = 0 then begin + ALOutput.Add(''); + end; + ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize} + end else begin + ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + end; + end; + end; + end; + + procedure ProcessPathDown(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + li, j : Integer; + LTmp : String; + LMaxLen : Integer; + LCols : Integer; + LLines : Integer; + // LFrm : String; + LCurItem : TIdFTPListOutputItem; + begin + + if ACurDir.FileList.Count = 0 then begin + Exit; + end; + //Note that we will assume a console width of 80 and we don't want something to wrap + //causing a blank line + LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse); + //if more than 39, we probably are going to exceed the width of the screen, + //just treat as one column + if LMaxLen > 39 then begin + ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse); + Exit; + end; + if Recurse and Assigned(ACurDir.SubDirs) then begin + if Recurse then begin + PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse); + end; + end; + LCols := 79 div (LMaxLen + 2);//2 spaces between columns + LLines := ACurDir.FileList.COunt div LCols; + //LFrm := '%' + IntToStr(LMaxLen+2) + 's'; + if (ACurDir.FileList.COunt mod LCols) > 0 then begin + Inc(LLines); + end; + for li := 1 to LLines do begin + j := 0; + LTmp := ''; + repeat + if ((li-1)+(LLInes*j)) < ACurDir.FileList.Count then begin + LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[(li-1)+(LLInes*j)])), LMaxLen, ' ') + ' '; + end; + Inc(j); + until (j > LCols); + ALOutput.Add(TrimRight(LTmp)); + end; + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count -1 do begin + LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem; + if LCurItem.DirError then begin + if li = 0 then begin + ALOutput.Add(''); + end; + ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize} + end else begin + ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + end; + end; + end; + end; + + procedure ProcessPathComma(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + li : Integer; + LTmp : String; + LCurItem : TIdFTPListOutputItem; + begin + if Recurse then begin + PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse); + end; + LTmp := ''; + for li := 0 to ACurDir.FileList.Count -1 do begin + LTmp := LTmp + NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])) + ', '; + end; + IdDelete(LTmp, Length(LTmp)-1, 2); + ALOutput.Text := ALOutput.Text + IndyWrapText(LTmp, EOL + ' ', LWS + ',' , 79); //79 good maxlen for text only terminals + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count -1 do begin + LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem; + if LCurItem.DirError then begin + if li = 0 then begin + ALOutput.Add(''); + end; + ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize} + end else begin + ProcessPathComma(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + end; + end; + end; + end; + + procedure ProcessPathLong(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False); + var + li : Integer; + LBlockCount : Integer; + LCurItem : TIdFTPListOutputItem; + begin + if Recurse then begin + PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse); + end; + + if (DirFormat = doUnix) and ExportTotalLine then begin + LBlockCount := 0; + for li := 0 to ACurDir.FileList.Count-1 do begin + LBlockCount := LBlockCount + TIdFTPListOutputItem(ACurDir.FileList.Objects[li]).NumberBlocks; + end; + ALOutput.Add(IndyFormat('total %d', [LBlockCount])); {Do not translate} + end; + + for li := 0 to ACurDir.FileList.Count-1 do begin + LCurItem := TIdFTPListOutputItem(ACurDir.FileList.Objects[li]); + case DirFormat of + doEPLF : ALOutput.Add(EPLFItem(LCurItem)); + doWin32 : ALOutput.Add(Win32Item(LCurItem)); + else + ALOutput.Add(UnixItem(LCurItem)); + end; + end; + + if Recurse and Assigned(ACurDir.SubDirs) then begin + for li := 0 to ACurDir.SubDirs.Count-1 do begin + LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem; + if LCurItem.DirError then begin + if DirFormat = doUnix then begin + if li = 0 then begin + ALOutput.Add(''); + end; + ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize} + end; + end; + ProcessPathLong(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse); + end; + end; + end; + + procedure DoUnixfParam(ARoot : TDirEntry; ALOutput : TStrings); + var + li : Integer; + LIt : TIdFTPListItem; + begin + for li := 0 to ARoot.FileList.Count -1 do begin + LIt := TIdFTPListItem(ARoot.FileList.Objects[li]); + if LIt.ItemType = ditDirectory then begin + ALOutput.Add(IndyGetFileName(LIt.FileName)); + end; + end; + end; + +begin + LShowNavSym := (DirFormat = doUnix) and HasSwitch(SWITCH_SHOW_ALLPERIOD); + if LShowNavSym then begin + LShowNavSym := not HasSwitch(SWITCH_HIDE_DIRPOINT); + end; + LRootPath := TDirEntry.Create('', nil); + try + for i := 0 to Count-1 do + begin + if Items[i].ItemType in [ditDirectory, ditSymbolicLinkDir] then begin + if not IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin + LRootPath.AddSubDir(StripInitPathDelim(Items[i].FileName), Items[i]); + end else begin + //if it's a "." or "..", we show it only in Unix mode and only with eht -a switch + if LShowNavSym then begin + LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]); + end; + end; + end; + end; + //add the file names + for i := 0 to Count-1 do begin + if Items[i].ItemType in [ditFile, ditSymbolicLink] then begin + if IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin + if LShowNavSym then begin + LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]); + end; + end else begin + LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]); + end; + end; + end; + //Note that Indy does not support a Last Access time in some file systems + //so we use the u parameter to mean the same as the t parameter + if HasSwitch(SWITCH_SORT_REVERSE) then begin + if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin + LRootPath.SortDescendMTime; + end + else if HasSwitch(SWITCH_SORTBY_EXT) then begin + LRootPath.SortDescendFNameExt; + end + else if HasSwitch(SWITCH_SORTBY_SIZE) then begin + LRootPath.SortDescendSize; + end + else begin + LRootPath.SortDescendFName; + end; + end + else if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin + LRootPath.SortAscendMTime; + end + else if HasSwitch(SWITCH_SORTBY_EXT) then begin + LRootPath.SortAscendFNameExt; + end + else if HasSwitch(SWITCH_SORTBY_SIZE) then begin + LRootPath.SortAscendSize; + end + else begin + LRootPath.SortAscendFName; + end; + //select the operation + // do the selected output operation + case DetermineOp of + doColsAccross : ProcessPathAccross(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE)); + doColsDown : ProcessPathDown(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE)); + doOneCol : ProcessOnePathCol(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE)); + doOnlyDirs : DoUnixfParam(LRootPath, AOutput); + doComma : ProcessPathComma(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE)); + else + ProcessPathLong(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE)); + end; + finally + FreeAndNil(LRootPath); + end; +end; + +procedure TIdFTPListOutput.LISTOutputDir(AOutput: TStrings); +begin + InternelOutputDir(AOutput, True); +end; + +function TIdFTPListOutput.MListItem(AItem: TIdFTPListOutputItem; AMLstOpts: TIdFTPFactOutputs): String; +begin + Result := ''; + if AMLstOpts = [] then begin + Result := AItem.FileName; + Exit; + end; + + if (Size in AMLstOpts) and AItem.SizeAvail then begin + Result := 'size=' + IntToStr(AItem.Size) + ';'; {do not localize} + end; + + if ItemType in AMLstOpts then begin + Result := Result + 'type='; {do not localize} + case AItem.ItemType of + ditFile : + begin + Result := Result + 'file;'; {do not localize} + end; + ditDirectory : + begin + if AItem.FileName = '..' then begin {do not localize} + Result := Result + 'pdir;'; {do not localize} + end + else if AItem.FileName = '.' then begin + Result := Result + 'cdir;'; {do not localize} + end + else begin + Result := Result + 'dir;'; {do not localize} + end; + end; + ditSymbolicLink : + begin + Result := Result + 'OS.unix=slink:' + AItem.FileName + ';'; {do not localize} + end; + end; + + end; + + if Perm in AMLstOpts then begin + Result := Result + 'perm=' + AItem.MLISTPermissions + ';'; {do not localize} + end; + if (winDriveType in AMLstOpts) and (AItem.WinDriveType<>-1) then begin + Result := Result + 'win32.dt='+IntToStr(AItem.WinDriveType )+';'; + end; + if CreateTime in AMLstOpts then begin + if AItem.CreationDateGMT <> 0 then begin + Result := Result + 'create='+ FTPGMTDateTimeToMLS(AItem.CreationDateGMT) + ';'; {do not localize} + end + else if AItem.CreationDate <> 0 then begin + Result := Result + 'create='+ FTPLocalDateTimeToMLS(AItem.CreationDate) + ';'; {do not localize} + end; + end; + + if (Modify in AMLstOpts) and AItem.ModifiedAvail then + begin + if AItem.ModifiedDateGMT <> 0 then begin + Result := Result + 'modify='+ FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize} + end + else if AItem.ModifiedDate <> 0 then begin + Result := Result + 'modify='+ FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize} + end; + end; + + if UnixMODE in AMLstOpts then begin + Result := Result + 'UNIX.mode='+ IndyFormat('%.4d', [PermsToChmodNo(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem) )] ) + ';'; {do not localize} + end; + + if UnixOwner in AMLstOpts then begin + Result := Result + 'UNIX.owner=' + UnixGetOutputOwner(AItem) + ';'; {do not localize} + end; + + if UnixGroup in AMLstOpts then begin + Result := Result + 'UNIX.group=' + UnixGetOutputGroup(AItem) + ';'; {do not localize} + end; + + if (Unique in AMLstOpts) and (AItem.UniqueID <> '') then begin + Result := Result + 'unique=' + AItem.UniqueID + ';'; {do not localize} + end; + + if LastAccessTime in AMLstOpts then begin + if AItem.ModifiedDateGMT <> 0 then begin + Result := Result + 'windows.lastaccesstime=' + FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize} + end + else if AItem.ModifiedDate <> 0 then begin + Result := Result + 'windows.lastaccesstime=' + FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize} + end; + end; + + if WinAttribs in AMLstOpts then begin + Result := Result + 'win32.ea=0x' + IntToHex(AItem.WinAttribs, 8) + ';'; {do not localize} + end; + if (AItem.WinDriveType > -1) and (WinDriveType in AMLstOpts) then begin + Result := Result + 'Win32.dt='+IntToStr( AItem.WinDriveType ) + ';'; + end; + if (AItem.WinDriveLabel <> '') and (WinDriveLabel in AMLstOpts) then begin + Result := Result + 'Win32.dl='+AItem.WinDriveLabel; + end; + + Result := Result + ' ' + AItem.FileName; +end; + +procedure TIdFTPListOutput.MLISTOutputDir(AOutput : TStrings; AMLstOpts: TIdFTPFactOutputs); +var + i : Integer; +begin + AOutput.Clear; + for i := 0 to Count-1 do begin + AOutput.Add(MListItem(Items[i], AMLstOpts)); + end; +end; + +function TIdFTPListOutput.NListItem(AItem: TIdFTPListOutputItem): String; +begin + Result := IndyGetFileName(AItem.FileName); + if DirFormat = doUnix then begin + if HasSwitch(SWITCH_QUOTEDNAME) then begin + Result := '"' + Result + '"'; + end; + if HasSwitch(SWITCH_CLASSIFY) or HasSwitch(SWITCH_SLASHDIR) then begin + case AItem.ItemType of + ditDirectory : + Result := Result + PATH_SUBDIR_SEP_UNIX; + ditSymbolicLink, ditSymbolicLinkDir : + Result := Result + '@'; + else + begin + if IsUnixExec(AItem.UnixOwnerPermissions, AItem.UnixGroupPermissions , AItem.UnixOtherPermissions) then begin + Result := Result + '*'; + end; + end; + end; + end; + Result := UnixinodeOutput(AItem)+ UnixBlocksOutput(AItem) + Result; + end; +end; + +procedure TIdFTPListOutput.NLISTOutputDir(AOutput: TStrings); +begin + InternelOutputDir(AOutput, False); +end; + +procedure TIdFTPListOutput.SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem); +begin + inherited Items[AIndex] := AValue; +end; + +function TIdFTPListOutput.UnixBlocksOutput(AItem: TIdFTPListOutputItem): String; +begin + if HasSwitch(SWITCH_PRINT_BLOCKS) then begin + Result := IndyFormat('%4d ', [AItem.NumberBlocks]); + end else begin + Result := ''; + end; +end; + +function TIdFTPListOutput.UnixGetOutputGroup(AItem: TIdFTPListOutputItem): String; +begin + if AItem.GroupName = '' then begin + Result := UnixGetOutputOwner(AItem); + end else begin + Result := AItem.GroupName; + end; +end; + +function TIdFTPListOutput.UnixGetOutputGroupPerms(AItem: TIdFTPListOutputItem): String; +begin + if AItem.UnixOtherPermissions = '' then begin + if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin + Result := DEF_DIR_GRP_PERM; + end else begin + Result := DEF_FILE_GRP_PERM; + end; + end else begin + Result := AItem.UnixOtherPermissions; + end; +end; + +function TIdFTPListOutput.UnixGetOutputOtherPerms(AItem: TIdFTPListOutputItem): String; +begin + if AItem.UnixOtherPermissions = '' then begin + if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin + Result := DEF_DIR_OTHER_PERM; + end else begin + Result := DEF_FILE_OTHER_PERM; + end; + end else begin + Result := AItem.UnixOtherPermissions; + end; +end; + +function TIdFTPListOutput.UnixGetOutputOwner(AItem: TIdFTPListOutputItem): String; +begin + if AItem.OwnerName = '' then begin + Result := DEF_OWNER; + end else begin + Result := AItem.OwnerName; + end; +end; + +function TIdFTPListOutput.UnixGetOutputOwnerPerms(AItem: TIdFTPListOutputItem): String; +begin + if AItem.UnixOwnerPermissions = '' then begin + if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin + Result := DEF_DIR_OWN_PERM; + end else begin + Result := DEF_FILE_OWN_PERM; + end; + end else begin + Result := AItem.UnixOwnerPermissions; + end; +end; + +function TIdFTPListOutput.UnixINodeOutput(AItem: TIdFTPListOutputItem): String; +var + LInode : String; +begin + Result := ''; + if HasSwitch(SWITCH_PRINT_INODE) then begin + LInode := IntToStr(Abs(AItem.Inode)); + //should be no more than 10 digits + LInode := Copy(LInode, 1, 10); + Result := Result + IndyFormat('%10s ', [LInode]); + end; +end; + +function TIdFTPListOutput.UnixItem(AItem: TIdFTPListOutputItem): String; +var + LSize, LTime: string; + l, month: Word; + LLinkNum : Integer; + LFileName : String; + LFormat : String; + LMTime : TDateTime; +begin + LFileName := IndyGetFileName(AItem.FileName); + Result := UnixINodeOutput(AItem) + UnixBlocksOutput(AItem); + case AItem.ItemType of + ditDirectory: + begin + AItem.Size := UNIX_DIR_SIZE; + LSize := 'd'; {Do not Localize} + end; + ditSymbolicLink: + begin + LSize := 'l'; {Do not Localize} + end; + else + begin + LSize := '-'; {Do not Localize} + end; + end; + if AItem.LinkCount = 0 then begin + LLinkNum := 1; + end else begin + LLinkNum := AItem.LinkCount; + end; + LFormat := '%3:3s%4:3s%5:3s %6:3d '; {Do not localize} + //g - surpress owner + //lrwxrwxrwx 1 other 7 Nov 16 2001 bin -> usr/bin + //where it would normally print + //lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin + if not HasSwitch('g') then begin + LFormat := LFormat + '%1:-8s '; {Do not localize} + end; + //o - surpress group + //lrwxrwxrwx 1 root 7 Nov 16 2001 bin -> usr/bin + //where it would normally print + //lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin + if not HasSwitch('o') then begin + LFormat := LFormat + '%2:-8s '; {Do not localize} + end; + LFormat := LFormat + '%0:8d'; {Do not localize} + LSize := LSize + IndyFormat(LFormat, [AItem.Size, UnixGetOutputOwner(AItem), + UnixGetOutputGroup(AItem), UnixGetOutputOwnerPerms(AItem), + UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem), LLinkNum]); + LMTime := GetLocalModTime(AItem); + DecodeDate(LMTime, l, month, l); + LTime := MonthNames[month] + FormatDateTime(' dd', LMTime); {Do not Localize} + if HasSwitch(SWITCH_BOTH_TIME_YEAR) then begin + LTime := LTime + FormatDateTime(' hh:nn:ss yyyy', LMTime); {Do not Localize} + end + else if IsIn6MonthWindow(LMTime) then begin {Do not Localize} + LTime := LTime + FormatDateTime(' hh:nn', LMTime); {Do not Localize} + end + else begin + LTime := LTime + FormatDateTime(' yyyy', LMTime); {Do not Localize} + end; + // A.Neillans, 20 Apr 2002, Fixed glitch, extra space in front of names. + // Result := LSize + ' ' + LTime + ' ' + FileName; {Do not Localize} + Result := Result + LSize + ' ' + LTime + ' '; + if HasSwitch(SWITCH_QUOTEDNAME) then begin + Result := Result + '"' + LFileName + '"'; {Do not Localize} + end else begin + Result := Result + LFileName; + end; + if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin + if HasSwitch(SWITCH_QUOTEDNAME) then begin + Result := Result + UNIX_LINKTO_SYM + '"' + AItem.LinkedItemName + '"'; {Do not Localize} + end else begin + Result := Result + UNIX_LINKTO_SYM + AItem.LinkedItemName; + end; + end; + if ((IndyPos(SWITCH_CLASSIFY,Switches)>0) or (IndyPos(SWITCH_SLASHDIR,Switches)>0)) and {Do not translate} + (AItem.ItemType in [ditDirectory, ditSymbolicLinkDir]) then + begin + Result := Result + PATH_SUBDIR_SEP_UNIX; + end; + if HasSwitch(SWITCH_CLASSIFY) and (AItem.ItemType = ditFile) and + IsUnixExec(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem)) then + begin + //star is placed at the end of a file name + //like this: + //-r-xr-xr-x 1 0 1 17440 Aug 8 2000 ls* + Result := Result + '*'; + end; +end; + +function TIdFTPListOutput.Win32Item(AItem: TIdFTPListOutputItem): String; +var + LSize, LFileName : String; +begin + LFileName := IndyGetFileName(AItem.FileName); + if AItem.ItemType = ditDirectory then begin + LSize := ' ' + StringOfChar(' ', 9); {Do not Localize} + end else begin + LSize := StringOfChar(' ', 20 - Length(IntToStr(AItem.Size))) + IntToStr(AItem.Size); {Do not Localize} + end; + Result := FormatDateTime('mm-dd-yy hh:nnAM/PM', GetLocalModTime(AItem)) + ' ' + LSize + ' ' + LFileName; {Do not Localize} +end; + +{ TDirEntry } + +function TDirEntry.AddFileName(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean; +var + i : Integer; + LParentPart : String; + LDirEnt : TDirEntry; +begin + Result := False; + LParentPart := StripInitPathDelim(IndyGetFilePath(APathName)); + if LParentPart = PathName then begin + if FFileList.IndexOf(APathName) = -1 then begin + FFileList.AddObject(APathName, ADirEnt); + end; + Result := True; + Exit; + end; + if Assigned(SubDirs) then begin + for i := 0 to SubDirs.Count-1 do begin + LDirEnt := TDirEntry(SubDirs[i]); + LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName)); + if TextStartsWith(APathName, LParentPart) then begin + if TDirEntry(SubDirs[i]).AddFileName(APathName, ADirEnt) then begin + Result := True; + Break; + end; + end; + end; + end; +end; + +function TDirEntry.AddSubDir(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean; +var + LDirEnt : TDirEntry; + i : Integer; + LParentPart : String; +begin + Result := False; + LParentPart := StripInitPathDelim(IndyGetFilePath(APathName)); + if LParentPart = PathName then begin + if not Assigned(FSubDirs) then begin + FSubDirs := TDirEntryList.Create; + end; + LParentPart := StripInitPathDelim(IndyGetFilePath(APathName)); + LParentPart := IndyGetFileName(LParentPart); + LDirEnt := TDirEntry.Create(APathName, ADirEnt); + try + FSubDirs.Add(LDirEnt); + except + LDirEnt.Free; + raise; + end; + AddFileName(APathName, ADirEnt); + Result := True; + Exit; + end; + if Assigned(SubDirs) then begin + for i := 0 to SubDirs.Count-1 do begin + LDirEnt := TDirEntry(SubDirs[i]); + LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName)); + if TextStartsWith(APathName, LParentPart) then begin + if LDirEnt.AddSubDir(APathName, ADirEnt) then begin + Result := True; + Break; + end; + end; + end; + end; +end; + +constructor TDirEntry.Create(const APathName : String; ADirListItem : TIdFTPListOutputItem); +begin + inherited Create; + FPathName := APathName; + FFileList := TIdBubbleSortStringList.Create; + FDirListItem := ADirListItem; + //create that only when necessary; + FSubDirs := TDirEntryList.Create; +end; + +destructor TDirEntry.Destroy; +begin + FreeAndNil(FFileList); + FreeAndNil(FSubDirs); + inherited Destroy; +end; + +procedure TDirEntry.SortAscendFName; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortAscFName); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortAscFName + {$ELSE} + TIdSortCompare(@DESortAscFName) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortAscendFName; + end; + end; +end; + +procedure TDirEntry.SortAscendMTime; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortAscMTime); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortAscMTime + {$ELSE} + TIdSortCompare(@DESortAscMTime) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortAscendMTime; + end; + end; +end; + +procedure TDirEntry.SortDescendMTime; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortDescMTime); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortDescMTime + {$ELSE} + TIdSortCompare(@DESortDescMTime) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count -1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortDescendMTime; + end; + end; +end; + +procedure TDirEntry.SortDescendFName; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortDescFName + {$ELSE} + TIdSortCompare(@DESortDescFName) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortDescendFName; + end; + end; + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortDescFName); + end; +end; + +procedure TDirEntry.SortAscendFNameExt; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortAscFNameExt); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortAscFName + {$ELSE} + TIdSortCompare(@DESortAscFName) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortAscendFNameExt; + end; + end; +end; + +procedure TDirEntry.SortDescendFNameExt; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortDescFNameExt); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortAscFName + {$ELSE} + TIdSortCompare(@DESortAscFName) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortDescendFNameExt; + end; + end; +end; + +procedure TDirEntry.SortAscendSize; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortAscSize); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortAscMTime + {$ELSE} + TIdSortCompare(@DESortAscMTime) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortAscendSize; + end; + end; +end; + +procedure TDirEntry.SortDescendSize; +var + i : Integer; + LSubDir: TDirEntry; +begin + if Assigned(FFileList) then begin + FFileList.BubbleSort(StrSortDescSize); + end; + if Assigned(FSubDirs) then begin + FSubDirs.BubbleSort( + {$IFDEF HAS_GENERICS_TObjectList} + DESortDescFName + {$ELSE} + TIdSortCompare(@DESortDescFName) + {$ENDIF} + ); + for i := 0 to FSubDirs.Count-1 do begin + LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF}; + LSubDir.SortDescendSize; + end; + end; +end; + +{ TIdFTPListOutputItem } + +constructor TIdFTPListOutputItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + //indicate that this fact is not applicable + FWinDriveType := -1; +end; + +end. diff --git a/indy/Protocols/IdFTPListParseAS400.pas b/indy/Protocols/IdFTPListParseAS400.pas new file mode 100644 index 0000000..7259060 --- /dev/null +++ b/indy/Protocols/IdFTPListParseAS400.pas @@ -0,0 +1,319 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 9:36:26 PM JPMugaas + Updated ref. + + Rev 1.4 4/19/2004 5:05:28 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:22 PM czhower + Name changes + + Rev 1.2 10/19/2003 2:08:48 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:02 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 04:18:10 AM JPMugaas + More things restructured for the new list framework. +} + +unit IdFTPListParseAS400; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdAS400FTPListItem = class(TIdOwnerFTPListItem); + TIdFTPLPAS400 = class(TIdFTPLineOwnedList) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String=''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseAS400"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +const + DIR_TYPES : array [0..3] of string = ('*DIR','*DDIR','*LIB','*FLR'); + +{ TIdFTPLPAS400 } + +class function TIdFTPLPAS400.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +var + s : TStrings; +begin + Result := False; + if AListing.Count > 0 then begin + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + if s.Count > 4 then begin + Result := CharEquals(s[4], 1, '*') or (s[4]='DIR'); {Do not localize} + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPAS400.GetIdent: String; +begin + Result := 'AS400'; {do not localize} +end; + +class function TIdFTPLPAS400.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdAS400FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPAS400.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; + LDate : String; + LTime : String; + LObjType : String; + LI : TIdOwnerFTPListItem; +begin + try +{ From: +http://groups.google.com/groups?q=AS400+LISTFMT+%3D+0&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=9onmpt%24dhe%2402%241%40news.t-online.com&rnum=1 + + ftp> dir qtemp/timestamp + 200 PORT subcommand request successful. + 125 List started. + drwx---rwx 1 QPGMR 0 20480 Sep 24 18:16 TIMESTAMP + -rwx---rwx 1 QPGMR 0 0 Sep 24 18:16 TIMESTAMP.TIMESTAMP + 250 List completed. + FTP: 140 Bytes empfangen in 0.06Sekunden 2.33KB/s + + or + + ftp> dir qtemp/timestamp + 200 PORT subcommand request successful. + 125 List started. + + 1 2 3 4 5 + 123456789012345678901234567890123456789012345678901234567890 + QPGMR 20480 24.09.01 18:16:20 *FILE QTEMP/TIMESTAMP + QPGMR *MEM QTEMP/TIMESTAMP.TIMESTAMP + 250 List completed. + FTP: 146 Bytes empfangen in 0.00Sekunden 146000.00KB/s + +It depends qether the SITE param LISTFMT is set to "1" (1st example, *nix- +like) or "0" (2nd example, OS/400-like). I have choosen the 2nd format (I +think it's easier to parse). To get it, submit "QUOTE SITE LISTFMT 0" just +before submitting the DIR command. + +From IBM Manual at: +http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm + +Here is the original iSeries style format for the LIST subcommand +(when LISTFMT=0): + +owner size date time type name +A blank space separates each field. + +This is a description of each field: + +owner +The 10 character string that represents the user profile which owns the subject. +This string is left justified, and includes blanks. This field is blank for +anonymous FTP sessions. + +size +The 10 character number that represents the size of the object. This number is +right justified, and it includes blanks. This field is blank when an object has +no size associated with it. + +date +The 8 character modification date in the format that is defined for the server +job. It uses date separators that are defined for the server job. This +modification date is left justified, and it includes blanks. + +time +The 8 character modification time that uses the time separator, which the +server job defines. + +type +The 10 character OS/400 object type. + +name +The variable length name of the object that follows a CRLF (carriage return, +line feed pair). This name may include blanks. + +Here is an example of the original iSeries style format: + + 1 2 3 4 5 +123456789012345678901234567890123456789012345678901234567890 +BAILEYSE 5263360 06/11/97 12:27:39 *FILE BPTFSAVF + +Note on name format from ( +http://groups.google.com/groups?q=AS400+FTP+LIST+format&hl= +en&lr=&ie=UTF-8&oe=utf-8&selm=3264740F.B52%40mother.com&rnum=4): + +Starting in v3r1 you can access the shared folders area or libraries with FTP by using the +"NAMEFMT 1" command. For example: + +SYST +215 OS/400 is the remote operating system. The TCP/IP version is "V3R1M0". +SITE NAMEFMT 1 +250 Now using naming format "1". +LIST /QDLS + +/QDLS/ARM 0 11/09/95 07:19:30 DIR +/QDLS/ARM-VOL1 0 06/23/95 16:39:43 DIR +/QDLS/ARMM 0 08/04/95 14:32:03 DIR + +or + +SYST +215 OS/400 is the remote operating system. The TCP/IP version is "V3R1M0". +SITE NAMEFMT 1 +250 Now using naming format "1". +LIST /QSYS.LIB + +QSYS 3584 11/15/95 16:15:33 *FILE /QSYS.LIB/QSYS.LIB/QPRTRPYL.PRTF +QSYS 18432 11/15/95 16:15:33 *FILE /QSYS.LIB/QSYS.LIB/QPRTSBSD.PRTF +QSYS 5632 11/15/95 16:15:33 *FILE /QSYS.LIB/QSYS.LIB/QPRTSPLF.PRTF +QSYS 8704 11/15/95 16:15:33 *FILE /QSYS.LIB/QSYS.LIB/QPRTSPLQ.PRTF + +} +{Notes from Angus Robertson, Magenta Systems Ltd, + +MORE TYPES OF SHIT ON THE AS/400 FILE SYSTEM + + Object types that are commonly used or that you are likely to see on + this display include the following: +AUTL Authorization list +BLKSF Block special file +CFGL Configuration list +CLS Class +CMD Command +CTLD Controller description +DDIR Distributed directory +DEVD Device description +DIR Directory +DOC Document +DSTMF Distributed stream file +FILE Database file or device file +FLR Folder +JOBD Job description +JOBQ Job queue +LIB Library +LIND Line description +MSGQ Message queue +OUTQ Output queue +PGM Program +SBSD Subsystem description +SOMOBJ System Object Model object +STMF Stream file +SYMLNK Symbolic link +USRPRF User profile +} + LI := AItem as TIdOwnerFTPListItem; + LI.ModifiedAvail := False; + LI.SizeAvail := False; + + LBuffer := AItem.Data; + LI.OwnerName := Fetch(LBuffer); + + LBuffer := TrimLeft(LBuffer); + //we have to make sure that the size feild really exists or the + //the parser is thrown off + if (LBuffer <> '') and (IsNumeric(LBuffer[1])) then begin + LI.Size := IndyStrToInt64(FetchLength(LBuffer,9),0); + LI.SizeAvail := True; + LBuffer := TrimLeft(LBuffer); + end; + //Sometimes the date and time feilds will not present + if (LBuffer <> '') and (IsNumeric(LBuffer[1])) then begin + LDate := Trim(StrPart(LBuffer, 8)); + if (LBuffer <> '') and (LBuffer[1] <> ' ') then begin + LDate := LDate + Fetch(LBuffer); + end; + if LDate <> '' then begin + LI.ModifiedDate := AS400Date(LDate); + LI.ModifiedAvail := True; + end; + LTime := Trim(StrPart(LBuffer, 8)); + if (LBuffer <> '') and (LBuffer[1] <> ' ') then begin + LTime := LTime + Fetch(LBuffer); + end; + if LTime <> '' then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LTime); + end; + end; + //most of this data is manditory so things are less sensitive to positions + LBuffer := Trim(LBuffer); + LObjType := FetchLength(LBuffer,11); + //A file object is something like a file but it can contain members - treat as dir. + // Odd, I know. + //There are also several types of file objects + //note that I'm not completely sure about this so it's commented out. JPM + +// if TextStartsWith(LObjType, '*FILE') then begin {do not localize} +// LI.ItemType := ditDirectory; +// end; + if IdGlobal.PosInStrArray(LObjType,DIR_TYPES)>-1 then begin {do not localize} + LI.ItemType := ditDirectory; + if TextEndsWith(LBuffer,'/') then begin + LBuffer := Fetch(LBuffer,'/'); + end; + end; + LI.FileName := TrimLeft(LBuffer); + if LI.FileName = '' then begin + LI.FileName := LI.OwnerName; + LI.OwnerName := ''; + end; + + LI.LocalFileName := LowerCase(StripPath(AItem.FileName, '/')); + Result := True; + except + Result := False; + end; +end; + +initialization + RegisterFTPListParser(TIdFTPLPAS400); +finalization + UnRegisterFTPListParser(TIdFTPLPAS400); +end. diff --git a/indy/Protocols/IdFTPListParseBase.pas b/indy/Protocols/IdFTPListParseBase.pas new file mode 100644 index 0000000..a32c6ba --- /dev/null +++ b/indy/Protocols/IdFTPListParseBase.pas @@ -0,0 +1,762 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.23 3/23/2005 4:52:28 AM JPMugaas + Expansion with MLSD and WIN32.ea fact in MLSD directories as described by: + + http://www.raidenftpd.com/kb/kb000000049.htm + + This returns Win32 file attributes including some that Borland does not + support. + + Rev 1.22 2/23/2005 6:34:30 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.21 2/4/2005 12:00:50 PM JPMugaas + Switched from TObjectList to TList for the parsing registration list. + + Rev 1.20 2/3/2005 11:05:14 PM JPMugaas + Fix for compiler warnings. + + Rev 1.19 12/8/2004 5:34:02 PM JPMugaas + Added method for getting all of the identifiers for the parsing classes for + ego purposes only :-). + + Rev 1.18 12/8/2004 8:35:18 AM JPMugaas + Minor class restructure to support Unisys ClearPath. + + Rev 1.17 11/29/2004 2:45:28 AM JPMugaas + Support for DOS attributes (Read-Only, Archive, System, and Hidden) for use + by the Distinct32, OS/2, and Chameleon FTP list parsers. + + Rev 1.16 11/5/2004 1:17:26 AM JPMugaas + Now also should support sizd fact in some dir listings on PureFTPD. + + Rev 1.15 10/26/2004 9:27:32 PM JPMugaas + Updated references. + + Rev 1.14 6/27/2004 1:45:36 AM JPMugaas + Can now optionally support LastAccessTime like Smartftp's FTP Server could. + I also made the MLST listing object and parser support this as well. + + Rev 1.13 6/11/2004 9:35:00 AM DSiders + Added "Do not Localize" comments. + + Rev 1.12 6/7/2004 7:38:42 PM JPMugaas + Fixed bug that appears in some descendant classes. TIdFTPListBaseHeader + would only call ADir.Add. If a descendant is using it's own descendant of + the TIdFTPListItem class for special properties, that would cause an invalid + typecast error. It now calls MakeNewItem so that a descendant can override + the item creation to make it's own TIdFTPListItem descendant. + + Rev 1.11 6/5/2004 3:04:12 PM JPMugaas + In MLST format, we now indicate if Size is available for item. One version + of NcFTP will omit that for directories. Confirmed at ftp.borland.com. I + also did the same for Modified Date if MLST doesn't provide it. + + Rev 1.10 4/19/2004 5:05:12 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.9 2004.02.03 5:45:14 PM czhower + Name changes + + Rev 1.8 11/26/2003 6:22:18 PM JPMugaas + IdFTPList can now support file creation time for MLSD servers which support + that feature. I also added support for a Unique identifier for an item so + facilitate some mirroring software if the server supports unique ID with EPLF + and MLSD. + + Rev 1.7 10/19/2003 2:27:02 PM DSiders + Added localization comments. + + Rev 1.6 10/6/2003 08:58:00 PM JPMugaas + Reworked the FTP list parsing framework so that the user can obtain the list + of capabilities from a parser class with TIdFTP. This should permit the user + to present a directory listing differently for each parser (some FTP list + parsers do have different capabilities). + + Rev 1.5 4/7/2003 04:03:06 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.4 3/3/2003 04:23:18 PM JPMugaas + Fix for a stack overflow. stupid mistake really. Procedure kept calling + itself. + + Rev 1.3 2/23/2003 06:08:16 AM JPMugaas + + Rev 1.2 2/21/2003 06:54:22 PM JPMugaas + The FTP list processing has been restructured so that Directory output is not + done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so + that the code is more scalable. + + Rev 1.1 2/19/2003 05:53:10 PM JPMugaas + Minor restructures to remove duplicate code and save some work with some + formats. The Unix parser had a bug that caused it to give a False positive + for Xercom MicroRTOS. + + Rev 1.0 2/18/2003 07:00:30 PM JPMugaas + Base class for new parsing framework. +} + +unit IdFTPListParseBase; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdException; + +type + //We don't want to create instances of these classes and + //these classes should not contain variables accross procedures + //because they may run in threads. + TIdFTPListBase = class(TObject) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; virtual; + //This is probably going to be a commonly used thing so it may be best to define it here. + //This is for parsing an individual line of data using AItem.Data + //AItem is the item that was already created + //APath should probably be a path passed to the parser for qualitying the filename and should + //used only for recursive lists + class function ParseLine(const AItem : TIdFTPListItem; const APath : String=''): Boolean; virtual; + public + //This should return a unique string indicating the type of format the parser supports + class function GetIdent : String; virtual; + //This determines if the parser is appropriate and returns True if it is or False if another parser + //should be used + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; virtual; + //This parses the AListing and fills in the ADir object + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : boolean; virtual; + end; + + TIdFTPListParseClass = class of TIdFTPListBase; + + //these are anscestors for some listings with a heading + TIdFTPListBaseHeader = class(TIdFTPListBase) + protected + class function IsHeader(const AData : String): Boolean; virtual; + class function IsFooter(const AData : String): Boolean; virtual; + public + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : boolean; override; + end; + //these are anscestors for some listings with an optional heading + TIdFTPListBaseHeaderOpt = class(TIdFTPListBaseHeader) + protected + class function CheckListingAlt(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; virtual; + public + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + end; + //base class for line-by-line items where there is a file owner along with mod date and file size + TIdFTPLineOwnedList = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + end; + + //These two parsers are manditory for the FTP Protocol + TIdFTPLPNList = class(TIdFTPListBase) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String=''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + end; + + TIdFTPLPMList = class(TIdFTPListBase) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String=''): Boolean; override; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + end; + + //these are for some MS-DOS, OS/2, and Windows servers that report Attributes + //in their listings + TIdFTPLPBaseDOS = class(TIdFTPListBase) + protected + class function IsValidAttr(const AAttr : String) : Boolean; virtual; + end; + + EIdFTPListParseError = class(EIdException); + +function ParseListing(AListing : TStrings; ADir : TIdFTPListItems; const AFormatID : String ) : boolean; +function CheckListParse(AListing : TStrings; ADir : TIdFTPListItems;var AFormat : String; const ASysDescript : String =''; const ADetails : Boolean = True) : boolean; +function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): String; +function CheckListParseCapa(AListing: TStrings; ADir: TIdFTPListItems; var VFormat: String; + var VClass: TIdFTPListParseClass; const ASysDescript: String; const ADetails: Boolean=True): Boolean; + +procedure RegisterFTPListParser(const AParser : TIdFTPListParseClass); +procedure UnregisterFTPListParser(const AParser : TIdFTPListParseClass); + +procedure EnumFTPListParsers(AData : TStrings); + +const + NLST = 'NLST'; {do not localize} + MLST = 'MLST'; {do not localize} + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.Types, + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdFTPCommon, IdFTPListTypes, IdGlobal, IdGlobalProtocols, + IdResourceStringsProtocols, IdStrings, SysUtils; + +type + TIdFTPRegParseList = class(TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}) + protected + function FindParserByDirData(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True) : TIdFTPListParseClass; + function FindParserByIdent(const AIdent : String) : TIdFTPListParseClass; + public + procedure EnumFTPListParsers(AData : TStrings); + constructor Create; overload; + function ParseListing(AListing : TStrings; ADir : TIdFTPListItems; const AFormatID : String ) : boolean; virtual; + function CheckListParse(AListing : TStrings; ADir : TIdFTPListItems;var VFormat : String; const ASysDescript : String =''; const ADetails : Boolean = True) : boolean; virtual; + function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): String; virtual; + { + This is for TIdFTP. This parses a list, returns the Parser ID, and the capabilities of the parser. + } + function CheckListParseCapa(AListing : TStrings; ADir : TIdFTPListItems; var VFormat : String; var VClass : TIdFTPListParseClass; const ASysDescript : String =''; const ADetails : Boolean = True) : boolean; virtual; + end; + +var + GParserList : TIdFTPRegParseList = nil; + +{ TIdFTPRegParseList } + +constructor TIdFTPRegParseList.Create; +begin + inherited Create; +end; + +function TIdFTPRegParseList.CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): String; +var + LCurParser : TIdFTPListParseClass; +begin + Result := ''; + LCurParser := Self.FindParserByDirData(AListing, ASysDescript, ADetails); + if LCurParser <> nil then begin + Result := LCurParser.GetIdent; + end; +end; + +function TIdFTPRegParseList.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems; const AFormatID: String): boolean; +var + LCurParser : TIdFTPListParseClass; +begin + //we do not want to fault a user or developer for an ambigious list + //such as something only containing a "total 0". + Result := True; + ADir.Clear; + LCurParser := FindParserByIdent(AFormatID); + if LCurParser <> nil then begin + Result := LCurParser.ParseListing(AListing, ADir); + end; +end; + +function TIdFTPRegParseList.CheckListParse(AListing : TStrings; + ADir : TIdFTPListItems;var VFormat : String; + const ASysDescript : String =''; const ADetails : Boolean = True) : boolean; +var + LCurParser : TIdFTPListParseClass; +begin + LCurParser := FindParserByDirData(AListing); + Result := Assigned(LCurParser); + if Result then begin + VFormat := LCurParser.GetIdent; + Result := ParseListing(AListing, ADir, VFormat); + end; +end; + +function TIdFTPRegParseList.FindParserByIdent(const AIdent: String): TIdFTPListParseClass; +var + i : Integer; + LCurParser : TIdFTPListParseClass; +begin + for i := 0 to Count - 1 do begin + LCurParser := {$IFDEF HAS_GENERICS_TList}Items[i]{$ELSE}TIdFTPListParseClass(Items[i]){$ENDIF}; + if LCurParser.GetIdent = AIdent then begin + Result := LCurParser; + Exit; + end; + end; + Result := nil; +end; + +function TIdFTPRegParseList.FindParserByDirData(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True) : TIdFTPListParseClass; +var + i : Integer; + LCurParser : TIdFTPListParseClass; +begin + for i := 0 to Count - 1 do begin + LCurParser := {$IFDEF HAS_GENERICS_TList}Items[i]{$ELSE}TIdFTPListParseClass(Items[i]){$ENDIF}; + if LCurParser.CheckListing(AListing, ASysDescript, ADetails) then begin + Result := LCurParser; + Exit; + end; + end; + Result := nil; +end; + +function TIdFTPRegParseList.CheckListParseCapa(AListing: TStrings; + ADir: TIdFTPListItems; var VFormat: String; var VClass: TIdFTPListParseClass; + const ASysDescript: String; const ADetails: Boolean): boolean; +var + HasExtraParsers: Boolean; + I: Integer; + LCurParser : TIdFTPListParseClass; +begin + VFormat := ''; + ADir.Clear; + + // RLebeau 9/17/07: if something other than NLST or MLST was used, check to + // see that the user has included any of the IdFTPListParse... units in the + // app's uses clause. If the user forgot to include any, warn them. + // Otherwise, just move on and assume they know what they are doing... + + if ADetails then begin + HasExtraParsers := False; + for I := 0 to Count-1 do + begin + // we need to exclude protocol specified parsers + LCurParser := {$IFDEF HAS_GENERICS_TList}Items[I]{$ELSE}TIdFTPListParseClass(Items[I]){$ENDIF}; + if PosInStrArray(LCurParser.GetIdent, [NLST, MLST]) = -1 then begin + HasExtraParsers := True; + Break; + end; + end; + if not HasExtraParsers then begin + raise EIdFTPListParseError.Create(RSFTPNoListParseUnitsRegistered); {do not localize} + end; + end; + + VClass := FindParserByDirData(AListing, ASysDescript, ADetails); + Result := Assigned(VClass); + if Result then begin + VFormat := VClass.GetIdent; + Result := VClass.ParseListing(AListing, ADir); + end; +end; + +{ TIdFTPListBase } + +{register and unreg procedures} +procedure RegisterFTPListParser(const AParser : TIdFTPListParseClass); +begin + GParserList.Add( + {$IFDEF HAS_GENERICS_TList}AParser{$ELSE}TObject(AParser){$ENDIF} + ); +end; + +procedure UnregisterFTPListParser(const AParser : TIdFTPListParseClass); +begin + if Assigned(GParserList) then begin + GParserList.Remove( + {$IFDEF HAS_GENERICS_TList}AParser{$ELSE}TObject(AParser){$ENDIF} + ); + end; +end; + +function ParseListing(AListing : TStrings; ADir : TIdFTPListItems; const AFormatID : String) : boolean; +begin + Result := GParserList.ParseListing(AListing, ADir, AFormatID); +end; + +function CheckListParse(AListing : TStrings; ADir : TIdFTPListItems;var AFormat : String; const ASysDescript : String =''; const ADetails : Boolean = True) : boolean; +begin + Result := GParserList.CheckListParse(AListing, ADir, AFormat, ASysDescript, ADetails); +end; + +function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): String; +begin + Result := GParserList.CheckListing(AListing, ASysDescript, ADetails); +end; + +function CheckListParseCapa(AListing: TStrings; ADir: TIdFTPListItems; + var VFormat: String; var VClass : TIdFTPListParseClass; + const ASysDescript: String; const ADetails: Boolean): Boolean; +begin + Result := GParserList.CheckListParseCapa(AListing, ADir, VFormat, VClass, ASysDescript, ADetails); +end; + +procedure TIdFTPRegParseList.EnumFTPListParsers(AData: TStrings); +var + i : Integer; + LDesc : String; + LCurParser: TIdFTPListParseClass; +begin + AData.Clear; + for i := 0 to GParserList.Count -1 do begin + //we need to exclude protocol specified parsers + LCurParser := {$IFDEF HAS_GENERICS_TList}Items[i]{$ELSE}TIdFTPListParseClass(Items[i]){$ENDIF}; + LDesc := LCurParser.GetIdent; + if PosInStrArray(LDesc, [NLST, MLST]) = -1 then begin + AData.Add(LDesc); + end; + end; +end; + +{ TIdFTPListBase } + +class function TIdFTPListBase.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +begin + //C++Builder can not use abstract virtual class methods. + Result := False; +end; + +class function TIdFTPListBase.GetIdent: String; +begin + Result := ''; +end; + +class function TIdFTPListBase.MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; +begin + Result := AOwner.Add; +end; + +class function TIdFTPListBase.ParseLine(const AItem: TIdFTPListItem; const APath: String): Boolean; +begin + //C++Builder can not use abstract virtual class methods. + Result := False; +end; + +class function TIdFTPListBase.ParseListing(AListing: TStrings; ADir: TIdFTPListItems): Boolean; +var + i : Integer; + AItem : TIdFTPListItem; +begin + Result := True; + for i := 0 to AListing.Count -1 do begin + if AListing[i] <> '' then begin + AItem := MakeNewItem(ADir); + AItem.Data := AListing[i]; + ParseLine(AItem, ''); + end; + end; +end; + +{ TIdFTPLPNList } + +class function TIdFTPLPNList.CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): boolean; +begin + Result := not ADetails; +end; + +class function TIdFTPLPNList.GetIdent: String; +begin + Result := NLST; +end; + +class function TIdFTPLPNList.ParseLine(const AItem: TIdFTPListItem; + const APath: String = ''): Boolean; +begin + AItem.FileName := AItem.Data; + Result := True; +end; + +{ TIdFTPLPMList } + +class function TIdFTPLPMList.CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): boolean; +begin + //user has to specifically ask for this parser + Result := False; +end; + +class function TIdFTPLPMList.GetIdent: String; +begin + Result := MLST; +end; + +class function TIdFTPLPMList.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMLSTFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMList.ParseLine(const AItem: TIdFTPListItem; + const APath: String = ''): Boolean; +var + LFacts : TStrings; + LBuffer : String; + LI : TIdMLSTFTPListItem; +//based on: +// +//http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-15.txt +begin + LI := AItem as TIdMLSTFTPListItem; + LFacts := TStringList.Create; + try + LI.FileName := ParseFactsMLS(AItem.Data, LFacts); + + LI.LocalFileName := AItem.FileName; + + LBuffer := LFacts.Values['type']; {do not localize} +// file -- a file entry +// cdir -- the listed directory +// pdir -- a parent directory +// dir -- a directory or sub-directory +// OS.name=type -- an OS or file system dependent file type + case PosInStrArray(LBuffer, ['cdir', 'pdir', 'dir', + 'OS.unix=slink', + 'OS.unix=socket', + 'OS.unix=blk', + 'OS.unix=chr', + 'OS.unix=fifo']) of + 0, 1, 2 : LI.ItemType := ditDirectory; + 3 : LI.ItemType := ditSymbolicLink; + 4 : LI.ItemType := ditSocket; + 5 : LI.ItemType := ditBlockDev; + 6 : LI.ItemType := ditCharDev; + 7 : LI.ItemType := ditFIFO; + else + //PureFTPD may do something like this to report where a symbolic link points to: + // + // type=OS.unix=slink:.;size=1;modify=20090304221247;UNIX.mode=0777;unique=13g1f1fb23; pub + if TextStartsWith(LBuffer,'OS.unix=slink:') then begin + LI.ItemType := ditSymbolicLink; + Fetch(LBuffer,':'); + LI.LinkedItemName := LBuffer; + end else begin + //tnftpd does something like this for block devices + //Type=OS.unix=blk-14/0;Modify=20100629203948;Perm=;Unique=dEcpCEoCAAAAAAAA; disk0 + if TextStartsWith(LBuffer, 'OS.unix=blk-' ) then begin + LI.ItemType := ditBlockDev; + end else begin + //tnftpd does something like this for block devices + //Type=OS.unix=chr-19/0;Modify=20100630134139;Perm=;Unique=dEcpCGECAAAAAAAA; nsmb0 + if TextStartsWith(LBuffer, 'OS.unix=chr-' ) then begin + LI.ItemType := ditCharDev; + end else begin + LI.ItemType := ditFile; + end; + end; + end; + end; + LBuffer := LFacts.Values['modify']; {do not localize} + if LBuffer <> '' then begin + LI.ModifiedDate := FTPMLSToLocalDateTime(LBuffer); + LI.ModifiedDateGMT := FTPMLSToGMTDateTime(LBuffer); + LI.ModifiedAvail := True; + end else begin + LI.ModifiedAvail := False; + end; + //create + LBuffer := LFacts.Values['create']; {do not localize} + if LBuffer <> '' then begin + LI.CreationDate := FTPMLSToLocalDateTime(LBuffer); + LI.CreationDateGMT := FTPMLSToGMTDateTime(LBuffer); + end; + //last access time + LBuffer := LFacts.Values['windows.lastaccesstime']; {do not localize} + if LBuffer <> '' then begin + LI.LastAccessDate := FTPMLSToLocalDateTime(LBuffer); + LI.LastAccessDateGMT := FTPMLSToGMTDateTime(LBuffer); + end; + LBuffer := LFacts.Values['size']; {do not localize} + if LBuffer <> '' then begin + LI.Size := IndyStrToInt64(LBuffer, 0); + LI.SizeAvail := True; + end else begin + LI.SizeAvail := False; + end; + if (not LI.SizeAvail) and (LI.ItemType = ditDirectory) then + begin + {PureFTPD uses a sizd fact for directories instead of the size fact} + LBuffer := LFacts.Values['sizd']; {Do not localize} + if LBuffer <> '' then + begin + LI.Size := IndyStrToInt64(LBuffer, 0); + LI.SizeAvail := True; + end; + end; + LBuffer := LFacts.Values['perm']; {do not localize} + if LBuffer <> '' then + begin + LI.MLISTPermissions := LBuffer; + LI.PermissionDisplay := LI.MLISTPermissions; + end else + begin + //maybe there is a UNIX.mode value + LBuffer := LFacts.Values['UNIX.mode']; {do not localize} + if LBuffer <> '' then + begin + //Surge FTP does something like this: + //type=dir;size=4096;modify=20040901012354;create=20040901012354;unique=833.32641;perm=cdeflmp;unix.mode=drwxr-xr-x;unix.owner=root;unix.group=root pub + //type=file;size=1376687;modify=20031212015717;create=20031212015717;unique=833.195842;perm=r;unix.mode=-rw-r--r--;unix.owner=root;unix.group=root v.zip + // + //while other servers simply give the chmod number + if IsNumeric(LBuffer) then begin + ChmodNoToPerms(IndyStrToInt(LBuffer, 0), LBuffer); + case LI.ItemType of + ditFile : LBuffer := '-' + LBuffer; {do not localize} + ditDirectory : LBuffer := 'd' + LBuffer; {do not localize} + ditSymbolicLink, + ditSymbolicLinkDir : LBuffer := 'l' + LBuffer; {do not localize} + ditBlockDev : LBuffer := 'b' + LBuffer; {do not localize} + ditCharDev : LBuffer := 'c' + LBuffer; {do not localize} + ditFIFO : LBuffer := 'p' + LBuffer; {do not localize} + ditSocket : LBuffer := 's' + LBuffer; {do not localize} + end; + end; + LI.PermissionDisplay := LBuffer; + end; + end; + LI.UniqueID := LFacts.Values['unique']; {do not localize} + //Win32.ea + // + //format like this: + // + //size=0;lang=utf8;modify=20050308020346;create=20041109093936;type=cdir;UNIX.mode=0666;UNIX.owner=a;UNIX.group=default;win32.ea=0x00000810 . + // + LBuffer := LFacts.Values['win32.ea']; {do not localize} + if LBuffer <> '' then begin + Fetch(LBuffer, 'x'); {do not localize} + LBuffer := '$'+LBuffer; {do not localize} + LI.AttributesAvail := True; + LI.Attributes.FileAttributes := IndyStrToInt(LBuffer, 0); + end; + Result := True; + finally + FreeAndNil(LFacts); + end; +end; + +{ TIdFTPListBaseHeader } + +class function TIdFTPListBaseHeader.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +var + i : Integer; +begin + Result := False; + for i := 0 to AListing.Count -1 do begin + if (not IsWhiteString(AListing[i])) and (not IsLineStr(AListing[i])) then begin + Result := IsHeader(AListing[i]); + Break; + end; + end; +end; + +class function TIdFTPListBaseHeader.IsFooter(const AData: String): Boolean; +begin + Result := False; +end; + +class function TIdFTPListBaseHeader.IsHeader(const AData: String): Boolean; +begin + Result := False; +end; + +class function TIdFTPListBaseHeader.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): boolean; +var + LStart : Integer; + i : Integer; + LItem : TIdFTPListItem; +begin + if AListing.Count > 0 then begin + //find the entries below the header + LStart := 0; + for i := 0 to AListing.Count-1 do begin + if IsHeader(AListing[i]) or IsWhiteString(AListing[i]) or IsLineStr(AListing[i]) then + begin + LStart := i+1; + end else begin + //we found where the header ends + Break; + end; + end; + for i := LStart to AListing.Count -1 do begin + if (not IsWhiteString(AListing[i])) and (not IsLineStr(AListing[i])) and (not IsFooter(AListing[i])) then begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + ParseLine(LItem); + end; + end; + end; + Result := True; +end; + +{ TIdFTPListBaseHeaderOpt } + +class function TIdFTPListBaseHeaderOpt.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +begin + Result := inherited CheckListing(AListing, ASysDescript,ADetails); + if not Result then begin + Result := CheckListingAlt(AListing, ASysDescript,ADetails); + end; +end; + +class function TIdFTPListBaseHeaderOpt.CheckListingAlt(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +begin + Result := False; +end; + +{ TIdFTPLineOwnedList } + +class function TIdFTPLineOwnedList.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdOwnerFTPListItem.Create(AOwner); +end; + +{ TIdFTPLPBaseDOS } + +class function TIdFTPLPBaseDOS.IsValidAttr(const AAttr: String): Boolean; +var + i : Integer; +begin + Result := False; + for i := 1 to Length(AAttr) do begin + Result := CharIsInSet(AAttr, i, 'RASH'); {do not localize} + if not Result then begin + Break; + end; + end; +end; + +procedure EnumFTPListParsers(AData : TStrings); +begin + GParserList.EnumFTPListParsers(AData); +end; + +initialization + GParserList := TIdFTPRegParseList.Create; + + //Register the manditory parsers + RegisterFTPListParser(TIdFTPLPNList); + RegisterFTPListParser(TIdFTPLPMList); + +finalization + FreeAndNil(GParserList); + +end. + diff --git a/indy/Protocols/IdFTPListParseBullGCOS7.pas b/indy/Protocols/IdFTPListParseBullGCOS7.pas new file mode 100644 index 0000000..18ae71d --- /dev/null +++ b/indy/Protocols/IdFTPListParseBullGCOS7.pas @@ -0,0 +1,161 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 10/26/2004 9:36:28 PM JPMugaas + Updated ref. + + Rev 1.5 4/19/2004 5:05:50 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:30 PM czhower + Name changes + + Rev 1.3 1/22/2004 4:39:48 PM SPerry + fixed set problems + + Rev 1.2 10/19/2003 2:27:04 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:28 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 10:13:16 PM JPMugaas + Moved parsers to their own classes. +} + +unit IdFTPListParseBullGCOS7; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdFTPLPGOS7 = class(TIdFTPLineOwnedList) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseBullGCOS7"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdFTPListTypes, IdGlobalProtocols, IdStrings, SysUtils; + +{ TIdFTPLPGOS7 } + +class function TIdFTPLPGOS7.CheckListing(AListing: TStrings; + const ASysDescript: String = ''; const ADetails: Boolean = True): Boolean; +var + LData : String; + + { + - - -----0 SEPT SYSADMIN AUG 26, 1997 SEQ1 + - - -----0 SEPT SYSADMIN AUG 26, 1997 SEQ2 + 123456789012345678901234567890123456789012345678901234567890 + 1 2 3 4 5 6 + } + function NumericOrSpace(const ALine : String): Boolean; + var + i : Integer; + begin + Result := True; + for i := 1 to Length(ALine) do + begin + if (not IsNumeric(ALine[i])) and (not CharEquals(ALine, i, ' ')) then + begin + Result := False; + Break; + end; + end; + end; + +begin + Result := False; + if AListing.Count > 0 then + begin + LData := AListing[0]; + Result := (Length(LData) > 54) and + (CharIsInSet(LData, 1, '-d')) and + (LData[2] = ' ') and + (CharIsInSet(LData, 3, '-dsm')) and + (LData[4] = ' ') and + (LData[24] = ' ') and + (LData[25] <> ' ') and + (NumericOrSpace(Copy(LData, 46, 2))) and + (CharIsInSet(LData, 48, ', ')) and + (LData[49] = ' ') and + (NumericOrSpace(Copy(LData, 50, 4))) and + (LData[54] = ' ') and + (LData[55] <> ' '); + end; +end; + +class function TIdFTPLPGOS7.GetIdent: String; +begin + Result := 'Bull GCOS7'; {do not localize} +end; + +class function TIdFTPLPGOS7.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +{ +# From: FTP 7 - File Transfer Protocol +# This was a presentation that was made available in PDF form +# http://www.bull.com/servers/gcos7/ce7/ftp7-en.pdf +# reconstructed from screen-shots displayed in the presentation +} +var + LBuf : String; + LI : TIdOwnerFTPListItem; +begin + LI := AItem as TIdOwnerFTPListItem; + if LI.Data[1] = 'd' then begin + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + LI.FileName := Copy(AItem.Data, 55, MaxInt); + LBuf := ReplaceAll(Copy(AItem.Data, 42, 12), ',', ''); + if not IsWhiteString(LBuf) then begin + LI.ModifiedDate := DateStrMonthDDYY(LBuf, ' '); + end; + LI.OwnerName := Trim(Copy(AItem.Data, 25, 17)); + //I don't think size is provided + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPGOS7); +finalization + UnRegisterFTPListParser(TIdFTPLPGOS7); +end. diff --git a/indy/Protocols/IdFTPListParseBullGCOS8.pas b/indy/Protocols/IdFTPListParseBullGCOS8.pas new file mode 100644 index 0000000..1dc17e2 --- /dev/null +++ b/indy/Protocols/IdFTPListParseBullGCOS8.pas @@ -0,0 +1,176 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 2/23/2005 6:34:26 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.6 10/26/2004 9:36:28 PM JPMugaas + Updated ref. + + Rev 1.5 4/19/2004 5:05:52 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:30 PM czhower + Name changes + + Rev 1.3 1/22/2004 4:42:38 PM SPerry + fixed set problems + + Rev 1.2 10/19/2003 2:27:04 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:42 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 10:13:20 PM JPMugaas + Moved parsers to their own classes. +} +unit IdFTPListParseBullGCOS8; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdFTPLPGOS8ListItem = class(TIdUnixPermFTPListItem); + TIdFTPLPGOS8 = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseBullGCOS8"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPGOS8 } + +class function TIdFTPLPGOS8.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LData : String; +begin + { + d rwx rwx --- 0 02/25/98 ftptest2 catalog1 + - rwx rwx --- 1280 05/06/98 10:12:10 uid testbcd + 12345678901234567890123456789012345678901234567890123456789012345678901234567890 + 1 2 3 4 5 6 7 8 + } + Result := False; + if AListing.Count > 0 then + begin + LData := AListing[0]; + Result := (Length(LData) > 59) and + (CharIsInSet(LData, 1, 'd-')) and {Do not Localize} + (LData[2] = ' ') and + (CharIsInSet(LData, 3, 'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 4, 'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 5, 'tsrwx-')) and {Do not Localize} + (LData[6] = ' ') and + (CharIsInSet(LData, 7, 'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 8, 'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 9, 'tsrwx-')) and {Do not Localize} + (LData[10] = ' ') and + (CharIsInSet(LData, 11,'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 12,'tsrwx-')) and {Do not Localize} + (CharIsInSet(LData, 13,'tsrwx-')) and {Do not Localize} + (LData[14] = ' ') and + IsNumeric(LData[25]) and + (LData[26] = ' '); + end; +end; + +class function TIdFTPLPGOS8.GetIdent: String; +begin + Result := 'Bull GCOS8'; {do not localize} +end; + +class function TIdFTPLPGOS8.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdFTPLPGOS8ListItem.Create(AOwner); +end; + +class function TIdFTPLPGOS8.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +//Based on FTP 8 Administrator's and User's Guide +//which is available at: http://www.bull.com/us/cd_doc/cd_doc_data/rj05a03.pdf + +// d rwx rwx --- 0 02/25/98 ftptest2 catalog1 +// - rwx rwx --- 1280 05/06/98 10:12:10 uid testbcd +// 12345678901 12345678901234 +// 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +// 1 2 3 4 5 6 7 8 +var + LBuf : String; + LI : TIdUnixPermFTPListItem; +begin + LI := AItem as TIdFTPLPGOS8ListItem; + if Length(AItem.Data) > 0 then + begin + if LI.Data[1] = 'd' then begin + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + + LI.FileName := Copy(AItem.Data, 60, Length(AItem.Data)); + //These may correspond roughly to Unix permissions + //The values are the same as reported with Unix emulation mode + LI.UnixOwnerPermissions := Copy(AItem.Data, 3, 3); + LI.UnixGroupPermissions := Copy(AItem.Data, 7, 3); + LI.UnixOtherPermissions := Copy(AItem.Data, 11, 3); + LI.PermissionDisplay := Copy(AItem.Data, 1, 13); + LI.Size := IndyStrToInt64(Copy(AItem.Data, 15, 11), 0); + LI.OwnerName := Trim(Copy(AItem.Data, 46, 14)); + + LI.ModifiedDate := DateMMDDYY(Copy(AItem.Data, 27, 8)); + LBuf := Copy(AItem.Data, 36, 8); + if Length(Trim(LBuf)) > 0 then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LBuf); + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPGOS8); +finalization + UnRegisterFTPListParser(TIdFTPLPGOS8); + +end. + diff --git a/indy/Protocols/IdFTPListParseChameleonNewt.pas b/indy/Protocols/IdFTPListParseChameleonNewt.pas new file mode 100644 index 0000000..41cbea7 --- /dev/null +++ b/indy/Protocols/IdFTPListParseChameleonNewt.pas @@ -0,0 +1,218 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/29/2004 2:44:16 AM JPMugaas + New FTP list parsers for some legacy FTP servers. +} + +unit IdFTPListParseChameleonNewt; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase,IdFTPListTypes; + +type + TIdChameleonNewtFTPListItem = class(TIdDOSBaseFTPListItem); + + TIdFTPLPChameleonNewt = class(TIdFTPLPBaseDOS) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseChameleonNewt"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobal, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPChameleonNewt } + +class function TIdFTPLPChameleonNewt.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +{Look for something like this: + +. Nov 16 1994 17:16 +.. Nov 16 1994 17:16 +INSTALL Nov 16 1994 17:17 +CMT Nov 21 1994 10:17 +DESIGN1.DOC 11264 May 11 1995 14:20 A +README.TXT 1045 May 10 1995 11:01 +WPKIT1.EXE 960338 Jun 21 1995 17:01 R +CMT.CSV 0 Jul 06 1995 14:56 RHA +} +var + i : Integer; + LBuf, LBuf2 : String; + LInt : Integer; +begin + Result := False; + for i := 0 to AListing.Count -1 do + begin + LBuf := AListing[i]; + //filename and extension - we assume an 8.3 filename type because + //Windows 3.1 only supports that. + Fetch(LBuf); + LBuf := TrimLeft(LBuf); + // or file size + LBuf2 := Fetch(LBuf); + Result := (LBuf2 = '') or IsNumeric(LBuf2); {Do not localize} + if not Result then begin + Exit; + end; + LBuf := TrimLeft(LBuf); + //month + LBuf2 := Fetch(LBuf); + Result := StrToMonth(LBuf2) > 0; + if not Result then begin + Exit; + end; + //day + LBuf := TrimLeft(LBuf); + LInt := IndyStrToInt64(Fetch(LBuf), 0); + Result := (LInt > 0) and (LInt < 32); + if not Result then begin + Exit; + end; + //year + LBuf := TrimLeft(LBuf); + Result := IsNumeric(Fetch(LBuf)); + if not Result then begin + Exit; + end; + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsHHMMSS(LBuf2, ':'); + if not Result then begin + Exit; + end; + //attributes + repeat + LBuf := TrimLeft(LBuf); + if LBuf = '' then begin + Break; + end; + LBuf2 := Fetch(LBuf); + Result := IsValidAttr(LBuf2); + until not Result; + end; +end; + +class function TIdFTPLPChameleonNewt.GetIdent: String; +begin + Result := 'NetManage Chameleon/Newt'; {Do not localize} +end; + +class function TIdFTPLPChameleonNewt.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdChameleonNewtFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPChameleonNewt.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LI : TIdChameleonNewtFTPListItem; + LBuf, LBuf2 : String; + LDay, LMonth, LYear : Integer; +begin + LI := AItem as TIdChameleonNewtFTPListItem; + LBuf := AItem.Data; + //filename and extension - we assume an 8.3 filename type because + //Windows 3.1 only supports that. + LI.FileName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + // or file size + LBuf2 := Fetch(LBuf); + if LBuf2 = '' then {Do not localize} + begin + LI.ItemType := ditDirectory; + LI.SizeAvail := False; + end else + begin + LI.ItemType := ditFile; + Result := IsNumeric(LBuf2); + if not Result then begin + Exit; + end; + LI.Size := IndyStrToInt64(LBuf2, 0); + end; + //month + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + LMonth := StrToMonth(LBuf2); + Result := LMonth > 0; + if not Result then begin + Exit; + end; + //day + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + LDay := IndyStrToInt64(LBuf2, 0); + Result := (LDay > 0) and (LDay < 32); + if not Result then begin + Exit; + end; + //year + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsNumeric(LBuf2); + if not Result then begin + Exit; + end; + LYear := Y2Year(IndyStrToInt(LBuf2, 0)); + LI.ModifiedDate := EncodeDate(LYear, LMonth, LDay); + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsHHMMSS(LBuf2, ':'); + if not Result then begin + Exit; + end; + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LBuf2); + //attributes + repeat + LBuf := TrimLeft(LBuf); + if LBuf = '' then begin + Break; + end; + LBuf2 := Fetch(LBuf); + Result := LI.FAttributes.AddAttribute(LBuf2); + until not Result; +end; + +initialization + RegisterFTPListParser(TIdFTPLPChameleonNewt); +finalization + UnRegisterFTPListParser(TIdFTPLPChameleonNewt); +end. diff --git a/indy/Protocols/IdFTPListParseCiscoIOS.pas b/indy/Protocols/IdFTPListParseCiscoIOS.pas new file mode 100644 index 0000000..00ad4cd --- /dev/null +++ b/indy/Protocols/IdFTPListParseCiscoIOS.pas @@ -0,0 +1,104 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 10/26/2004 9:36:28 PM JPMugaas + Updated ref. + + Rev 1.3 4/19/2004 5:05:54 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.2 2004.02.03 5:45:32 PM czhower + Name changes + + Rev 1.1 10/19/2003 2:27:06 PM DSiders + Added localization comments. + + Rev 1.0 2/19/2003 10:13:28 PM JPMugaas + Moved parsers to their own classes. +} + +unit IdFTPListParseCiscoIOS; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase,IdFTPListTypes; + +{ + I think this FTP Server is embedded in the Cisco routers. + + The Cisco IOS router FTP Server only returns filenames, not dirs. + You set up a root dir and then you can only access that. + You might be able to update something such as flash RAM by specifying + pathes with uploads. +} + +type + TIdCiscoIOSFTPListItem = class(TIdMinimalFTPListItem); + + TIdFTPLPCiscoIOS = class(TIdFTPLPNList) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseCiscoIOS"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPCiscoIOS } + +class function TIdFTPLPCiscoIOS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +begin + // Identifier obtained from + // http://www.cisco.com/univercd/cc/td/doc/product/access/acs_serv/as5800/sc_3640/features.htm#xtocid210805 + // 1234567890 + Result := TextStartsWith(ASysDescript, 'Cisco IOS '); {do not localize} +end; + +class function TIdFTPLPCiscoIOS.GetIdent: String; +begin + Result := 'Cisco IOS'; {do not localize} +end; + +class function TIdFTPLPCiscoIOS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdCiscoIOSFTPListItem.Create(AOwner); +end; + +initialization + RegisterFTPListParser(TIdFTPLPCiscoIOS); +finalization + UnRegisterFTPListParser(TIdFTPLPCiscoIOS); +end. diff --git a/indy/Protocols/IdFTPListParseDistinctTCPIP.pas b/indy/Protocols/IdFTPListParseDistinctTCPIP.pas new file mode 100644 index 0000000..5dc4d64 --- /dev/null +++ b/indy/Protocols/IdFTPListParseDistinctTCPIP.pas @@ -0,0 +1,208 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 11/29/2004 2:45:28 AM JPMugaas + Support for DOS attributes (Read-Only, Archive, System, and Hidden) for use + by the Distinct32, OS/2, and Chameleon FTP list parsers. + + Rev 1.8 10/26/2004 9:36:28 PM JPMugaas + Updated ref. + + Rev 1.7 4/19/2004 5:05:56 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.6 2004.02.03 5:45:32 PM czhower + Name changes + + Rev 1.5 24/01/2004 19:19:28 CCostelloe + Cleaned up warnings + + Rev 1.4 1/23/2004 12:52:58 PM SPerry + fixed set problems + + Rev 1.3 1/22/2004 5:54:02 PM SPerry + fixed set problems + + Rev 1.2 10/19/2003 2:27:08 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:46 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 10:13:32 PM JPMugaas + Moved parsers to their own classes. +} + +unit IdFTPListParseDistinctTCPIP; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdDistinctTCPIPFTPListItem = class(TIdDOSBaseFTPListItem) + protected + FDist32FileAttributes : String; + public + property ModifiedDateGMT; + //This is kept solely for compatability, do NOT remove this as you will probably + //break someone's code + property Dist32FileAttributes : string read FDist32FileAttributes write FDist32FileAttributes; + end; + + TIdFTPLPDistinctTCPIP = class(TIdFTPLPBaseDOS) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseDistinctTCPIP"'} + {$ENDIF} + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + Posix.SysTime, + Posix.Time, + {$ENDIF} + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPDistinctTCPIP } + +class function TIdFTPLPDistinctTCPIP.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +const + DistValidTypes = '-d'; + DistValidAttrs = 'wash-d'; + //w - can write - read attribute not set + //a - archive bit set + //s - system attribute bit set + //h - hidden system bit set +var + s : TStrings; +begin + Result := False; + if AListing.Count > 0 then + begin + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + if s.Count > 2 then + begin + Result := (Length(s[0]) = 5) and (CharIsInSet(s[0], 1, DistValidTypes)) + and IsNumeric(s[1]) and (StrToMonth(s[2]) > 0); + if Result then + begin + Result := (CharIsInSet(s[0], 1, DistValidAttrs)) and + (CharIsInSet(s[0], 2, DistValidAttrs)) and + (CharIsInSet(s[0], 3, DistValidAttrs)) and + (CharIsInSet(s[0], 4, DistValidAttrs)) and + (CharIsInSet(s[0], 5, DistValidAttrs)); + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPDistinctTCPIP.GetIdent: String; +begin + Result := 'Distinct TCP/IP'; {do not localize} +end; + +class function TIdFTPLPDistinctTCPIP.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdDistinctTCPIPFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPDistinctTCPIP.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LBuf2, LDate : String; + LI : TIdDistinctTCPIPFTPListItem; +begin + Result := False; + LI := AItem as TIdDistinctTCPIPFTPListItem; + LI.Attributes.Read_Only := True; + LBuf := TrimLeft(LI.Data); + //attributes and attributes + LBuf2 := Fetch(LBuf); + LI.Dist32FileAttributes := LBuf2; + LI.Attributes.AddAttribute(LBuf2); + LBuf := TrimLeft(LBuf); + if TextStartsWith(LI.Dist32FileAttributes, 'd') then begin + LI.ItemType := ditDirectory; + end; + //size + LI.Size := IndyStrToInt64(Fetch(LBuf), 0); + LBuf := TrimLeft(LBuf); + //date - month + LDate := Fetch(LBuf); + if StrToMonth(LDate) = 0 then begin + Exit; + end; + LBuf := TrimLeft(LBuf); + //date - day and year + LBuf2 := Fetch(LBuf); + //we do it this way because a year might sometimes be missing + //in which case, we just add the current year. + LDate := LDate + ',' + LBuf2; + LDate := ReplaceAll(LDate, ',', ' '); + LI.ModifiedDate := DateStrMonthDDYY(LDate, ' ', True); + //time + LBuf := TrimLeft(LBuf); + LDate := Fetch(LBuf); + if not IsHHMMSS(LDate, ':') then begin + Exit; + end; + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LDate); + // -wa-- 23 Dec 29,2002 18:42 createtest.txt + // #Timestamp test with createtest.txt. + // Corresponding local Dir entry: + // 12/29/2002 01:42p 23 CreateTest.txt + // I suspect that this server returns the timestamp as GMT + LI.ModifiedDateGMT := LI.ModifiedDate; + LI.ModifiedDate := LI.ModifiedDate - TimeZoneBias; + // file name + LBuf := StripSpaces(LBuf, 1); + LI.FileName := LBuf; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPDistinctTCPIP); +finalization + UnRegisterFTPListParser(TIdFTPLPDistinctTCPIP); + +end. diff --git a/indy/Protocols/IdFTPListParseEPLF.pas b/indy/Protocols/IdFTPListParseEPLF.pas new file mode 100644 index 0000000..e6ce1d9 --- /dev/null +++ b/indy/Protocols/IdFTPListParseEPLF.pas @@ -0,0 +1,177 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 10/26/2004 9:36:30 PM JPMugaas + Updated ref. + + Rev 1.5 4/19/2004 5:05:30 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:22 PM czhower + Name changes + + Rev 1.3 11/26/2003 6:22:24 PM JPMugaas + IdFTPList can now support file creation time for MLSD servers which support + that feature. I also added support for a Unique identifier for an item so + facilitate some mirroring software if the server supports unique ID with EPLF + and MLSD. + + Rev 1.2 10/19/2003 2:27:14 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:48 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 04:18:16 AM JPMugaas + More things restructured for the new list framework. +} + +unit IdFTPListParseEPLF; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdAEPLFFTPListItem = class(TIdFTPListItem) + protected + //Unique ID for an item to prevent yourself from downloading something twice + FUniqueID : String; + //UNIX permissions + FEPLFPermissions: String; + public + property ModifiedDateGMT; + //Valid only with EPLF and MLST + property UniqueID : string read FUniqueID write FUniqueID; + property EPLFPermissions: string read FEPLFPermissions write FEPLFPermissions; + end; + + TIdFTPLPEPLF = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseEPLF"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPEPLF } + +class function TIdFTPLPEPLF.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s: String; +begin + Result := (AListing.Count > 0); + if Result then + begin + s := AListing[0]; + Result := (Length(s) > 2) and (s[1] = '+') and (IndyPos(#9, s) > 0); + end; +end; + +class function TIdFTPLPEPLF.GetIdent: String; +begin + Result := 'EPLF'; {do not localize} +end; + +class function TIdFTPLPEPLF.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdAEPLFFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPEPLF.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LFacts : TStrings; + i : Integer; + LI : TIdAEPLFFTPListItem; + LBuf: String; +begin + LI := AItem as TIdAEPLFFTPListItem; + LFacts := TStringList.Create; + try + LI.FileName := ParseFacts(Copy(LI.Data, 2, MaxInt), LFacts, ',', #9); + for i := 0 to LFacts.Count-1 do + begin + LBuf := LFacts[i]; + if LBuf = '/' then begin {do not localize} + LI.ItemType := ditDirectory; + end + else if LBuf = 'r' then begin {do not localize} + LI.ItemType := ditFile; + end + else if Length(LBuf) > 0 then + begin + case LBuf[1] of + 's': {do not localize} + begin + AItem.Size := IndyStrToInt64(Copy(LBuf, 2, MaxInt), 0); + end; + 'm': {do not localize} + begin + LBuf := Copy(LBuf, 2, MaxInt); + LI.ModifiedDate := EPLFDateToLocalDateTime(LBuf); + LI.ModifiedDateGMT := EPLFDateToGMTDateTime(LBuf); + end; + 'i': {do not localize} + begin + LI.UniqueID := Copy(LBuf, 2, MaxInt); + end; + 'u': {do not localize} + begin + if Length(LBuf) > 1 then begin + if LBuf[2] = 'p' then begin {do not localize} + LI.EPLFPermissions := Copy(LBuf, 3, MaxInt); + LI.PermissionDisplay := LI.EPLFPermissions; + end; + end; + end; + end; + end; + end; + finally + FreeAndNil(LFacts); + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPEPLF); +finalization + UnRegisterFTPListParser(TIdFTPLPEPLF); + +end. diff --git a/indy/Protocols/IdFTPListParseHellSoft.pas b/indy/Protocols/IdFTPListParseHellSoft.pas new file mode 100644 index 0000000..cd551e1 --- /dev/null +++ b/indy/Protocols/IdFTPListParseHellSoft.pas @@ -0,0 +1,133 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 9:36:30 PM JPMugaas + Updated ref. + + Rev 1.4 4/19/2004 5:05:26 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:20 PM czhower + Name changes + + Rev 1.2 24/01/2004 19:20:06 CCostelloe + Cleaned up warnings + + Rev 1.1 10/19/2003 2:27:14 PM DSiders + Added localization comments. + + Rev 1.0 2/19/2003 02:02:12 AM JPMugaas + Individual parsing objects for the new framework. +} + +unit IdFTPListParseHellSoft; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListParseNovellNetware; + +{ + This parser works just like Novell Netware's except that the detection is + different. + + HellSoft made a freeware FTP Server for Novell Netware in the early 1990's for + Novell Netware 3 and 4. It is still somewhat in use in some Eastern parts of Europ. +} + +type + TIdHellSoftFTPListItem = class(TIdNovellNetwareFTPListItem); + + TIdFTPLPHellSoft = class(TIdFTPLPNovellNetware) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseHellSoft"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPHellSoft } + +class function TIdFTPLPHellSoft.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; + + function IsHellSoftLine(const AData : String) : Boolean; + var + LPerms : String; + begin + Result := AData <> ''; + if Result then + begin + Result := CharIsInSet(AData, 1, 'dD-'); {do not localize} + if Result then + begin + //we have to be careful to distinguish between Hellsoft and + //NetWare Print Services for UNIX, FTP File Transfer Service + LPerms := ExtractNovellPerms(Copy(AData, 1, 12)); + Result := (Length(LPerms) = 7) and IsValidNovellPermissionStr(LPerms); + end; + end; + end; + +begin + Result := False; + if AListing.Count > 0 then + begin + if IsTotalLine(AListing[0]) then begin + Result := (AListing.Count > 1) and IsHellSoftLine(AListing[1]); + end else + begin + Result := IsHellSoftLine(AListing[0]); + end; + end; +end; + +class function TIdFTPLPHellSoft.GetIdent: String; +begin + Result := 'Hellsoft'; {do not localize} +end; + +class function TIdFTPLPHellSoft.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdHellSoftFTPListItem.Create(AOwner); +end; + +initialization + RegisterFTPListParser(TIdFTPLPHellSoft); +finalization + UnRegisterFTPListParser(TIdFTPLPHellSoft); + +end. diff --git a/indy/Protocols/IdFTPListParseIEFTPGateway.pas b/indy/Protocols/IdFTPListParseIEFTPGateway.pas new file mode 100644 index 0000000..eceeef5 --- /dev/null +++ b/indy/Protocols/IdFTPListParseIEFTPGateway.pas @@ -0,0 +1,319 @@ +unit IdFTPListParseIEFTPGateway; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase,IdFTPListTypes; + +{This is based on: + +Information Exchange +via TCP/IP FTP Gateway Users +Guide +Version 1 Release 4 + + Copyright GXS, Inc. 1998, 2005. All rights reserved. + +and is available at: +https://www.gxsolc.com/public/EDI/us/support/Library/Publications/IEtcpipFTPGatewayUserGuide_c3423452.pdf + +} +type + + TIdIEFTPGatewayLsLongListItem = class(TIdFTPListItem) + protected + FSenderAcct : String; + FSenderUserID : String; + FMClass : String; + public + property SenderAcct : String read FSenderAcct write FSenderAcct; + property SenderUserID : String read FSenderUserID write FSenderUserID; + property MClass : String read FMClass write FMClass; + end; + TIdIEFTPGatewayLsShortListItem = class(TIdMinimalFTPListItem); + TIdIEFTPGatewayLsFileNameListItem = class(TIdMinimalFTPListItem) + protected + FOrigFileName : String; + public + property OrigFileName : String read FOrigFileName write FOrigFileName; + end; + TIdIEFTPGatewayLSLibraryListItem = class(TIdUnixPermFTPListItem) + protected + FAccount : String; + public + property Account : String read FAccount write FAccount; + end; + + TIdFTPLPIEFTPGatewayLSLong = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + TIdFTPLPIEFTPGatewayLSShort = class(TIdFTPLPNList) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdFTPLPIEFTPGatewayLSFileName = class(TIdFTPListBase) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String=''): Boolean; override; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): boolean; override; + end; + TIdFTPLPIEFTPGatewayLSLibrary = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseIEFTPGateway"'} + {$ENDIF} + +implementation +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + + +function IsIEFile(const AStr : String): Boolean; + {$IFDEF USE_INLINE} inline; {$ENDIF} +begin + Result := TextEndsWith(AStr,'._IE'); +end; + +{ TIdFTPLPIEFTPGatewayLSLong } + +class function TIdFTPLPIEFTPGatewayLSLong.GetIdent: String; +begin + Result := 'IE-FTPListStyleLong'; {Do not localize} +end; + +class function TIdFTPLPIEFTPGatewayLSLong.IsHeader( + const AData: String): Boolean; +var s : TStrings; +begin +//" Filename (MSGKEY) Sender Class Size Date Time" + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count >=6 then begin + Result := (s[0] = 'Filename') and (s[1]='(MSGKEY)') + and (s[2]='Sender') and (s[3]='Class') + and (s[4]='Size') and (s[5]='Date') + and (s[6]='Time'); + end else begin + Result := False; + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPIEFTPGatewayLSLong.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdIEFTPGatewayLsLongListItem.Create(AOwner); +end; + +class function TIdFTPLPIEFTPGatewayLSLong.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var li : TIdIEFTPGatewayLsLongListItem; + s : TStrings; + d, m, y : Word; + h, mn, sec : Word; +begin + Result := True; +//"FFAD59A3FB10054AC5F1._IE ACCT1 USER1 ORDERS 0000006501 960821 092357" + li := AItem as TIdIEFTPGatewayLsLongListItem; + li.ItemType := ditFile; + s := TStringList.Create; + try + SplitDelimitedString(li.Data, s, True); + li.FileName := s[0]; + li.SenderAcct := s[1]; + li.SenderUserID := s[2]; + li.MClass := s[3]; + li.Size := StrToIntDef(s[4],0); + li.SizeAvail := True; + y := Y2Year(StrToInt(Copy(s[5],1,2))); + m := StrToInt(Copy(s[5],3,2)); + d := StrToInt(Copy(s[5],5,2)); + li.ModifiedDate := EncodeDate(y,m,d); + + h := StrToInt(Copy(s[6],1,2)); + mn := StrToInt(Copy(s[6],3,2)); + sec := StrToInt(Copy(s[6],5,2)); + li.ModifiedDate := li.ModifiedDate + EncodeTime(h,mn,sec,0); + + li.ModifiedAvail := True; + finally + FreeAndNil(s); + end; + +end; + +{ TIdFTPLPIEFTPGatewayLSFileName } + +class function TIdFTPLPIEFTPGatewayLSFileName.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +var LData : String; + i : Integer; +begin + Result := AListing.Count > 0; + if Result then begin + for i := 0 to AListing.Count - 1 do begin + LData := AListing[i]; + Result := IsIEFile(Fetch(LData)); + if Result then begin + LData := TrimLeft(LData); + Result := LData <> ''; + end; + if not Result then begin + break; + end; + end; + end; +end; + +class function TIdFTPLPIEFTPGatewayLSFileName.GetIdent: String; +begin + Result := 'IE-FTPListStyleFileName'; {Do not localize} +end; + +class function TIdFTPLPIEFTPGatewayLSFileName.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdIEFTPGatewayLsFileNameListItem.Create(AOwner); +end; + +class function TIdFTPLPIEFTPGatewayLSFileName.ParseLine( + const AItem: TIdFTPListItem; const APath: String): Boolean; +var li : TIdIEFTPGatewayLsFileNameListItem; + LData : String; +begin + Result := True; + li := AItem as TIdIEFTPGatewayLsFileNameListItem; + li.ItemType := ditFile; + LData := li.Data; + li.FileName := Fetch(LData); + LData := TrimLeft(LData); + li.OrigFileName := UnquotedStr(Fetch(LData)); +end; + +{ TIdFTPLPIEFTPGatewayLSShort } + +class function TIdFTPLPIEFTPGatewayLSShort.CheckListing(AListing : TStrings; + const ASysDescript : String =''; const ADetails : Boolean = True): boolean; +var + i : Integer; +begin + Result := False; + for I := 0 to AListing.Count - 1 do begin + Result := IsIEFile(AListing[i]); + if not Result then begin + break; + end; + end; +end; + +class function TIdFTPLPIEFTPGatewayLSShort.GetIdent: String; +begin + Result := 'IE-FTPListStyleShort'; {Do not localize} +end; + +class function TIdFTPLPIEFTPGatewayLSShort.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdIEFTPGatewayLsShortListItem.Create(AOwner); +end; + +{ TIdFTPLPIEFTPGatewayLSLibrary } + +class function TIdFTPLPIEFTPGatewayLSLibrary.GetIdent: String; +begin + Result := 'IE-FTPListStyleLibrary'; {Do not localize} +end; + +class function TIdFTPLPIEFTPGatewayLSLibrary.IsHeader( + const AData: String): Boolean; +var s : TStrings; +begin +//"Access Owner Account Size Last updated Name" + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count >=6 then begin + Result := (s[0] = 'Access') and (s[1]='Owner') + and (s[2]='Account') and (s[3]='Size') + and (s[4]='Last') and (s[5]='updated') + and (s[6]='Name'); + end else begin + Result := False; + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPIEFTPGatewayLSLibrary.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdIEFTPGatewayLSLibraryListItem.Create(AOwner); +end; + +class function TIdFTPLPIEFTPGatewayLSLibrary.ParseLine( + const AItem: TIdFTPListItem; const APath: String): Boolean; +var LI : TIdIEFTPGatewayLSLibraryListItem; + LData : String; +begin + Result := True; + LI := AItem as TIdIEFTPGatewayLSLibraryListItem; + LData := LI.Data; + LI.ItemType := ditFile; + LI.FUnixOwnerPermissions := Copy(LI.Data,2,3); + LI.FUnixGroupPermissions := Copy(LI.Data,5,3); + LI.FUnixOtherPermissions := Copy(LI.Data,8,3); + IdDelete(LData,1,10); + LI.OwnerName := Fetch(LData); + LData := TrimLeft(LData); + LI.Account := Fetch(LData); + LData := TrimLeft(LData); + LI.Size := StrToIntDef(Fetch(LData),0); + LData := TrimLeft(LData); + LI.ModifiedDate := DateYYMMDD(Fetch(LData)); + LData := TrimLeft(LData); + LI.ModifiedDate := TimeHHMMSS(Fetch(LData)); + IdDelete(LData,1,1); + LI.FileName := LData; +end; + +initialization + RegisterFTPListParser(TIdFTPLPIEFTPGatewayLSLong); + RegisterFTPListParser(TIdFTPLPIEFTPGatewayLSShort); + RegisterFTPListParser(TIdFTPLPIEFTPGatewayLSFileName); + RegisterFTPListParser(TIdFTPLPIEFTPGatewayLSLibrary); +finalization + UnRegisterFTPListParser(TIdFTPLPIEFTPGatewayLSLong); + UnRegisterFTPListParser(TIdFTPLPIEFTPGatewayLSShort); + UnRegisterFTPListParser(TIdFTPLPIEFTPGatewayLSFileName); + UnRegisterFTPListParser(TIdFTPLPIEFTPGatewayLSLibrary); +end. diff --git a/indy/Protocols/IdFTPListParseKA9Q.pas b/indy/Protocols/IdFTPListParseKA9Q.pas new file mode 100644 index 0000000..19fcb3c --- /dev/null +++ b/indy/Protocols/IdFTPListParseKA9Q.pas @@ -0,0 +1,224 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 10/26/2004 9:46:32 PM JPMugaas + Updated refs. + + Rev 1.5 6/5/2004 3:09:28 PM JPMugaas + Now indicates SIze is not available for directories. Size is not given for a + directory in KA9Q. + + Rev 1.4 4/19/2004 5:06:06 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:38 PM czhower + Name changes + + Rev 1.2 1/9/2004 4:50:26 PM BGooijen + removed text after final end. + + Rev 1.1 10/19/2003 2:27:16 PM DSiders + Added localization comments. + + Rev 1.0 3/16/2003 02:39:06 PM JPMugaas + I must have forgot to check in this file as part of the FTP list restructure. + !!!OOPS!!! +} + +unit IdFTPListParseKA9Q; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdKA9QFTPListItem = class(TIdFTPListItem); + + TIdFTPLPKA9Q = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function CheckListing(AListing : TStrings; const ASysDescript : String =''; const ADetails : Boolean = True): Boolean; override; + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseKA9Q"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPKA9Q } + +class function TIdFTPLPKA9Q.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +var + s : TStrings; + + function IsKAQ9TS(const AData : String) : Boolean; + begin + Result := (PatternsInStr(':', AData) = 1) and IsHHMMSS(AData, ':'); + end; + + function IsKAQ9DS(const AData : String) : Boolean; + begin + Result := (PatternsInStr('/', AData) = 2) and IsMMDDYY(AData, '/'); + end; + +begin + Result := False; + if AListing.Count > 0 then + begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + if s.Count > 2 then + begin + if TextEndsWith(s[0], '/') then + begin + //could be a dir + Result := IsKAQ9TS(s[1]) and IsKAQ9DS(s[2]); + end else + begin + //could be a file + Result := (s.Count > 3) and + (ExtractNumber(s[1], False) > -1) and + IsKAQ9TS(s[2]) and + IsKAQ9DS(s[3]); + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPKA9Q.GetIdent: String; +begin + Result := 'KA9Q'; {do not localize} +end; + +class function TIdFTPLPKA9Q.IsFooter(const AData: String): Boolean; +var + LWords : TStrings; +begin + Result := False; + if AData = '#' then {do not localize} + begin + Result := True; + Exit; + end; + LWords := TStringList.Create; + try + SplitDelimitedString(ReplaceAll(AData, '-', ' '), LWords, True); + if LWords.Count > 1 then + begin + Result := (LWords[1] = 'files.') or (LWords[1] = 'file.') or {do not localize} + (LWords[1] = 'files') or (LWords[1] = 'file'); {do not localize} + end; + finally + FreeAndNil(LWords); + end; +end; + +class function TIdFTPLPKA9Q.IsHeader(const AData: String): Boolean; +begin + Result := False; +end; + +class function TIdFTPLPKA9Q.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdKA9QFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPKA9Q.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LPt : String; + LNewItem : TIdFTPListItem; + LDir : TIdFTPListItems; +begin + { + Note that this parser is odd because it will create a new TIdFTPListItem. + I know that is not according to the current conventional design. However, KA9Q + is unusual because a single line can have two items (maybe more) + } + LBuf := AItem.Data; + {filename - note that a space is illegal in MS-DOS so this should be safe} + LPt := Fetch(LBuf); + if LPt <> '' then + begin + if TextEndsWith(LPt, '/') then + begin + AItem.FileName := Fetch(LPt, '/'); + AItem.ItemType := ditDirectory; + AItem.SizeAvail := False; + end else + begin + AItem.FileName := LPt; + AItem.ItemType := ditFile; + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + AItem.Size := ExtractNumber(LPt); + end; + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + if LPt <> '' then + begin + AItem.ModifiedDate := TimeHHMMSS(LPt); + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + if LPt <> '' then + begin + AItem.ModifiedDate := AItem.ModifiedDate + DateMMDDYY(LPt); + LBuf := Trim(LBuf); + if LBuf <> '' then + begin + LDir := AItem.Collection as TIdFTPListItems; + LNewItem := LDir.Add; + LNewItem.Data := LBuf; + TIdFTPLPKA9Q.ParseLine(LNewItem, APath); + end; + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPKA9Q); +finalization + UnRegisterFTPListParser(TIdFTPLPKA9Q); + +end. diff --git a/indy/Protocols/IdFTPListParseMPEiX.pas b/indy/Protocols/IdFTPListParseMPEiX.pas new file mode 100644 index 0000000..61629b7 --- /dev/null +++ b/indy/Protocols/IdFTPListParseMPEiX.pas @@ -0,0 +1,354 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 9:46:34 PM JPMugaas + Updated refs. + + Rev 1.4 4/19/2004 5:05:48 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:28 PM czhower + Name changes + + Rev 1.2 10/19/2003 2:27:22 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:52 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 05:51:24 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseMPEiX; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdMPiXFTPListItem = class(TIdRecFTPListItem) + protected + FLimit : UInt32; + public + constructor Create(AOwner: TCollection); override; + property RecLength; + property RecFormat; + property NumberRecs; + property Limit : UInt32 read FLimit write FLimit; + end; + + //Anscestor for the MPE/iX Parsers + //This is necessary because they both have a second line a function parses + //Do not register this one + TIdFTPLPMPiXBase = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsSecondHeader(ACols: TStrings): Boolean; virtual; + public + class function GetIdent : String; override; + end; + + TIdFTPLPMPiX = class(TIdFTPLPMPiXBase) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + class function IsHeader(const AData: String): Boolean; override; + public + class function GetIdent : String; override; + end; + + TIdFTPLPMPiXWithPOSIX = class(TIdFTPLPMPiXBase) + protected + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + class function IsHeader(const AData: String): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseMPEiX"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +{ TIdFTPLPMPiXBase } + +class function TIdFTPLPMPiXBase.GetIdent: String; +begin + Result := 'MPE/iX: '; {do not localize} +end; + +class function TIdFTPLPMPiXBase.IsSecondHeader(ACols: TStrings): Boolean; +begin + Result := (ACols.Count > 3) and + (ACols[0] = 'SIZE') and {do not localize} + (ACols[1] = 'TYP') and {do not localize} + (ACols[2] = 'EOF') and {do not localize} + (ACols[3] = 'LIMIT'); {do not localize} + if Result and (ACols.Count = 8) then + begin + Result := (ACols[4] = 'R/B') and {do not localize} + (ACols[5] = 'SECTORS') and {do not localize} + (ACols[6] = '#X') and {do not localize} + (ACols[7] = 'MX') {do not localize} + end; + { +This is for a Not Found banner such as: + +"@ not found" +"./@ not found" + + } + if (not Result) and (ACols.Count = 3) then + begin + Result := (IndyPos('@', ACols[0]) > 0) and + (ACols[1] = 'not') and {do not localize} + (ACols[2] = 'found'); {do not localize} + end; +end; + +class function TIdFTPLPMPiXBase.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMPiXFTPListItem.Create(AOwner); +end; + +{ TIdFTPLPMPiX } + +class function TIdFTPLPMPiX.GetIdent: String; +begin + Result := inherited GetIdent + 'LISTF'; {do not localize} +end; + +class function TIdFTPLPMPiX.IsHeader(const AData: String): Boolean; +var + LCols : TStrings; + LAccP, LGrpP : Integer; +begin + LAccP := IndyPos('ACCOUNT=', AData); {do not localize} + if LAccP = 0 then begin + LAccP := IndyPos('ACCOUNT =', AData); {do not localize} + end; + LGrpP := IndyPos('GROUP=', AData); {do not localize} + if LGrpP = 0 then begin + LGrpP := IndyPos('GROUP =', AData); {do not localize} + end; + Result := (LAccP > 0) and (LGrpP > LAccP); + if not Result then + begin + LCols := TStringList.Create; + try + SplitDelimitedString(ReplaceAll(AData, '-', ' '), LCols, True); + Result := (LCols.Count > 3) and + (LCols[0] = 'FILENAME') and {do not localize} + (LCols[1] = 'CODE') and {do not localize} + (LCols[2] = 'LOGICAL') and {do not localize} + (LCols[3] = 'RECORD'); {do not localize} + if Result and (LCols.Count = 5) then begin + Result := (LCols[4] = 'SPACE'); {do not localize} + end; + if not Result then begin + Result := IsSecondHeader(LCols); + end; + finally + FreeAndNil(LCols); + end; + end; +end; + +class function TIdFTPLPMPiX.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; + LBuf : String; + LI : TIdMPiXFTPListItem; +begin + LI := AItem as TIdMPiXFTPListItem; + LCols := TStringList.Create; + try + //According to "HP ARPA File Transfer Protocol, Users Guide, HP 3000 MPE/iX Computer Systems,Edition 6" + //the filename here can be 8 chars long + LI.FileName := Trim(Copy(AItem.Data, 1, 8)); + LBuf := Copy(AItem.Data, 8, MaxInt); + if (Length(LBuf) > 0) and (LBuf[1] <> ' ') then begin + Fetch(LBuf); + end; + SplitDelimitedString(LBuf, LCols, True); + if LCols.Count > 1 then begin + LI.Size := ExtractNumber(LCols[1]); + end; + //Type + if LCols.Count > 2 then begin + LI.RecFormat := LCols[2]; + end; + //record COunt - EOF + if LCols.Count > 3 then begin + LI.NumberRecs := IndyStrToInt64(LCols[3], 0); + end; + //Limit + if LCols.Count > 4 then begin + LI.Limit := IndyStrToInt64(LCols[4], 0); + end; + { + HP3000 is a flat file system where there are no + subdirs. There is a file group created by the user + but that is mroe logical than anything. There might + be a command for obtaining file groups but I have not + seen one. Note that file groups can not obtain other groups. + } + LI.ItemType := ditFile; + { + Note that HP3000 does not give you the date at all. + } + LI.ModifiedAvail := False; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPMPiXWithPOSIX } + +class function TIdFTPLPMPiXWithPOSIX.GetIdent: String; +begin + Result := inherited GetIdent + 'With POSIX'; {do not localize} +end; + +class function TIdFTPLPMPiXWithPOSIX.IsHeader(const AData: String): Boolean; +var + LCols : TStrings; +begin + { + Often is something like this (spacing may very): + == + + PATH= /PH/SAPHP/ + + CODE ------------LOGICAL RECORD----------- ----SPACE---- FILENAME + SIZE TYP EOF LIMIT R/B SECTORS #X MX + + == + or maybe this: + === + ACCOUNT= SYS GROUP= WORK + FILENAME CODE ------------LOGICAL RECORD----------- ----SPACE---- + === + } + Result := IndyPos('PATH=', AData) > 0; {do not localize} + if not Result then + begin + LCols := TStringList.Create; + try + SplitDelimitedString(ReplaceAll(AData, '-', ' '), LCols, True); + Result := (LCols.Count = 5) and + (LCols[0] = 'CODE') and {do not localize} + (LCols[1] = 'LOGICAL') and {do not localize} + (LCols[2] = 'RECORD') and {do not localize} + (LCols[3] = 'SPACE') and {do not localize} + (LCols[4] = 'FILENAME'); {do not localize} + if not Result then begin + Result := IsSecondHeader(LCols); + end; + finally + FreeAndNil(LCols); + end; + end; +end; + +class function TIdFTPLPMPiXWithPOSIX.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; + LI : TIdMPiXFTPListItem; +begin + LI := AItem as TIdMPiXFTPListItem; + LCols := TStringList.Create; + try + SplitDelimitedString(AItem.Data, LCols, True); + if LCols.Count > 0 then begin + LI.Size := ExtractNumber(LCols[0]); + end; + if LCols.Count > 1 then begin + LI.RecFormat := LCols[1]; + end; + if LCols.Count > 2 then begin + LI.NumberRecs := IndyStrToInt64(LCols[2], 0); + end; + if LCols.Count > 3 then begin + LI.Limit := IndyStrToInt64(LCols[3], 0); + end; + if LCols.Count > 8 then begin + LI.FileName := LCols[8]; + end; + { + The original HP3000 is a flat file system where there are no + subdirs. There is a file group created by the user + but that is more logical than anything. There might + be a command for obtaining file groups but I have not + seen one. Note that file groups can not obtain other groups. + + More recent versions of HP3000 have Posix support including a + hierarchical file system. Verified with test at: + + jazz.external.hp.com + } + if TextEndsWith(LI.FileName, '/') then + begin + LI.ItemType := ditDirectory; + LI.FileName := Copy(AItem.FileName, 1, Length(LI.FileName) - 1); + end else begin + LI.ItemType := ditFile; + end; + { + Note that HP3000 does not give you the date at all. + } + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdMPiXFTPListItem } + +constructor TIdMPiXFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + //MP/iX or HP3000 will not give you a modified date at all + ModifiedAvail := False; +end; + +initialization + RegisterFTPListParser(TIdFTPLPMPiX); + RegisterFTPListParser(TIdFTPLPMPiXWithPOSIX); +finalization + UnRegisterFTPListParser(TIdFTPLPMPiX); + UnRegisterFTPListParser(TIdFTPLPMPiXWithPOSIX); + +end. diff --git a/indy/Protocols/IdFTPListParseMVS.pas b/indy/Protocols/IdFTPListParseMVS.pas new file mode 100644 index 0000000..2b09822 --- /dev/null +++ b/indy/Protocols/IdFTPListParseMVS.pas @@ -0,0 +1,739 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 10/26/2004 9:46:36 PM JPMugaas + Updated refs. + + Rev 1.6 6/8/2004 12:42:22 PM JPMugaas + Fixed an Invalid Type Cast problem. + + Rev 1.5 4/19/2004 5:05:36 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:24 PM czhower + Name changes + + Rev 1.3 10/19/2003 3:36:02 PM DSiders + Added localization comments. + + Rev 1.2 4/7/2003 04:03:58 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.1 2/19/2003 05:53:20 PM JPMugaas + Minor restructures to remove duplicate code and save some work with some + formats. The Unix parser had a bug that caused it to give a False positive + for Xercom MicroRTOS. + + Rev 1.0 2/19/2003 06:04:36 AM JPMugaas + IBM MVS parser has been ported to new design. +} + +unit IdFTPListParseMVS; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ +This should work with IBM MVS, OS/390, and z/OS. + +Note that in z/OS, there is no need for a parser for the HFS (hierarchical file +system) because the server would present a Unix-like list for that file system. +} + +type + TIdJESJobStatus = (IdJESNotApplicable, IdJESReceived, IdJESHold, IdJESRunning, IdJESOuptutAvailable); + + TIdMVSFTPListItem = class(TIdRecFTPListItem) + protected + FBlockSize : Integer; + FMigrated : Boolean; + FVolume : String; + FUnit : String; + FOrg : String; //data set organization + FMVSNumberExtents: Integer; + FMVSNumberTracks: Integer; + public + constructor Create(AOwner: TCollection); override; + property Migrated : Boolean read FMigrated write FMigrated; + property BlockSize : Integer read FBlockSize write FBlockSize; + property RecLength; + property RecFormat; + property NumberRecs; + property Volume : String read FVolume write FVolume; + //can't be unit because that's a reserved word + property Units : String read FUnit write FUnit; + property Org : String read FOrg write FOrg; //data set organization + property NumberExtents: Integer read FMVSNumberExtents write FMVSNumberExtents; + property NumberTracks: Integer read FMVSNumberTracks write FMVSNumberTracks; + end; + + TIdMVSJESFTPListItem = class(TIdOwnerFTPListItem) + protected + FMVSJobStatus : TIdJESJobStatus; + FMVSJobSpoolFiles : Integer; + public + constructor Create(AOwner: TCollection); override; + property JobStatus : TIdJESJobStatus read FMVSJobStatus write FMVSJobStatus; + property JobSpoolFiles : Integer read FMVSJobSpoolFiles write FMVSJobSpoolFiles; + end; + + TIdMVSJESIntF2FTPListItem = class(TIdOwnerFTPListItem) + protected + FJobStatus : TIdJESJobStatus; + FJobSpoolFiles : Integer; + FDetails : TStrings; + procedure SetDetails(AValue : TStrings); + public + constructor Create(AOwner: TCollection); override; + destructor Destroy; override; + property Details : TStrings read FDetails write SetDetails; + property JobStatus : TIdJESJobStatus read FJobStatus write FJobStatus; + property JobSpoolFiles : Integer read FJobSpoolFiles write FJobSpoolFiles; + end; + + TIdFTPLPMVS = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + TIdFTPLPMVSPartitionedDataSet = class(TIdFTPListBaseHeader) + protected + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + //Jes queues + TIdFTPLPMVSJESInterface1 = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsMVS_JESNoJobsMsg(const AData: String): Boolean; virtual; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + TIdFTPLPMVSJESInterface2 = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsMVS_JESIntF2Header(const AData: String): Boolean; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseMVS"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +{ TIdFTPLPMVS } + +class function TIdFTPLPMVS.GetIdent: String; +begin + Result := 'MVS'; {do not localize} +end; + +class function TIdFTPLPMVS.IsHeader(const AData: String): Boolean; +//Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname +//Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname +//Volume Unit Date Ext Used Recfm Lrecl BlkSz Dsorg Dsname +var + lvolp, lunp, lrefp, lextp, lusedp, + lrecp, lBlkSz, lDsorg, lDsnp : Integer; +begin + {Note that this one is a little more difficult because I could not find + a MVS machine that accepts anonymous FTP. So I have to do the best I can + with some old posts where people had posted dir structures they got from FTP.} + lvolp := IndyPos('Volume', AData); {Do not translate} + lunp := IndyPos('Unit', AData); {Do not translate} + lrefp := IndyPos('Referred', AData); {Do not translate} + if lrefp = 0 then begin + lrefp := IndyPos('Date', AData); {Do not translate} + end; + lextp := IndyPos('Ext', AData); {Do not translate} + lusedp := IndyPos('Used', AData); {Do not translate} + lrecp := IndyPos('Lrecl', AData); {Do not translate} + lBlkSz := IndyPos('BlkSz', AData); {Do not translate} + lDsorg := IndyPos('Dsorg', AData); {Do not translate} + lDsnp := IndyPos('Dsname', AData); {Do not translate} + Result := (lvolp <> 0) and (lunp > lvolp) and + (lrefp > lunp) and (lextp > lrefp) and + (lusedp > lextp) and (lrecp > lusedp) and + (lBlkSz > lrecp) and (lDsorg > lBlkSz) and + (lDsnp > lDsorg); +end; + +class function TIdFTPLPMVS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMVSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMVS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +{Much of this is based on a thread at: + +http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 + +and + +http://www.snee.com/bob/opsys/part6mvs.pdf + +Note: Thread concerning MVS Data Set Size +http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=jcmorris.767551300%40mwunix&rnum=15&prev=/groups%3Fq%3DMVS%2BRecfm%2BV%26start%3D10%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3Djcmorris.767551300%2540mwunix%26rnum%3D15 +http://groups.google.com/groups?q=MVS+Recfm+V&start=10&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=jcmorris.767551300%40mwunix&rnum=15 + +http://www.isc.ucsb.edu/tsg/ftp-to-mvs.html +http://www.lsu.edu/ocs/tsc/os390doc/mvsftp.html +} + + function IsMVSMigrated(const AData : String) : Boolean; + begin + Result := TextStartsWith(AData, 'Migrated') or TextStartsWith(AData, 'MIGRAT'); {do not localize} + end; + + function IsPseudoDir(const AData : String) : Boolean; + begin + //In newer implementations, a directory mode is available, see if + //item is a Pseudo-directory + Result := TextStartsWith(AData, 'Pseudo Directory'); {do not localize} + end; + + function CanGetAttributes(const AData : String) : Boolean; + begin + Result := (not IsMVSMigrated(AData)) and (not IsPseudoDir(AData)) and + (not TextStartsWith(AData, 'Error')); {do not localize} + end; + +var + i : Integer; + s : TStrings; + LI : TIdMVSFTPListItem; +begin + //NOTE: File Size is not supported at all + //because the file size is calculated with something like this: + // BlkSz * Blks/Trk * Trks + //but you can not get MVS DEVINFO macro so you do not have enough information + //to work with. + AItem.ModifiedAvail := False; + AItem.SizeAvail := False; + LI := AItem as TIdMVSFTPListItem; + if IsMVSMigrated(AItem.Data) then begin + LI.Migrated := True; + end; + if IsPseudoDir(AItem.Data) then begin + LI.ItemType := ditDirectory; + end; + if CanGetAttributes(AItem.Data) then + begin + s := TStringList.Create; + try + SplitDelimitedString(AItem.Data, s, True); + if s.Count > 0 then begin + LI.Volume := s[0]; + end; + if s.Count > 1 then begin + LI.Units := s[1]; + end; + if s.Count > 2 then + begin + //Sometimes, the Referred Column will contain a date. + //e.g. **NONE** + //Documented in: Communications Server for z/OS V1R2 TCP/IP Implementation Guide Volume 2: UNIX Applications + //URL: http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg245228.pdf + if IsNumeric(s[2], 1, 1) then + begin + // If the number of extents is greater than 99 it can run into the date column: + // SM6009 3380 2010/03/09123 2415 U 18432 18432 PO LOADLIB + if Length(s[2]) > 10 then begin + s.Insert(3, Copy(S[2], 11, MaxInt)); + s[2] := Copy(S[2], 1, 10); + end; + LI.ModifiedDate := MVSDate(s[2]); + LI.ModifiedAvail := True; + end; + end; + if s.Count > 3 then begin + LI.NumberExtents := IndyStrToInt(s[3], 0); + end; + if s.Count >4 then begin + LI.NumberTracks := IndyStrToInt(s[4], 0); + end; + if s.Count > 5 then begin + LI.RecFormat := s[5]; + end; + if s.Count >6 then begin + LI.RecLength := IndyStrToInt(s[6], 0); + end; + if s.Count > 7 then begin + LI.BlockSize := IndyStrToInt(s[7], 0); + end; + if s.Count > 8 then + begin + LI.Org := s[8]; + // TODO: use PosInStrArray() instead? + if (LI.Org = 'PO') or (LI.Org = 'PO-E') then begin {do not localize} + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + end; + finally + FreeAndNil(s); + end; + end; + //Note that spaces are illegal in MVS file names (Data set names) + //http://www.snee.com/bob/opsys/part6mvs.pdf + //but for filenames enclosed in '', we should tolerate spaces. + if (AItem.Data <> '') and (TextEndsWith(AItem.Data, '''')) then + begin + i := IndyPos('''', AItem.Data)+1; + AItem.FileName := Copy(AItem.Data, i, Length(AItem.Data)-i-1); + end else + begin + i := RPos(' ', AItem.Data)+1; + AItem.FileName := Copy(AItem.Data, i, MaxInt); + end; + Result := True; +end; + +{ TIdFTPLPMVSPartitionedDataSet } + +class function TIdFTPLPMVSPartitionedDataSet.GetIdent: String; +begin + Result := 'MVS: Partitioned Data Set'; {do not localize} +end; + +class function TIdFTPLPMVSPartitionedDataSet.IsHeader( const AData: String): Boolean; +var + LPName, LPSize : Integer; +begin + //Name VV.MM Created Changed Size Init Mod Id + // + //or + // + //Name Size TTR Alias-of AC --------- Attributes --------- Amode Rmode + //if there are loaded moduals + LPName := IndyPos('Name', AData); {do not localize} + LPSize := IndyPos('Size', AData); {do not localize} + Result := (LPName > 0) and (LPSize > LPName); +end; + +class function TIdFTPLPMVSPartitionedDataSet.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + s : TStrings; +//MVS Particianed data sets must be treated differently than +//the regular MVS catalog. + +//NOTE: File Size is not supported at all. Size is usually size in records, not bytes +// This is based on stuff at: +// http://publibz.boulder.ibm.com:80/cgi-bin/bookmgr_OS390/BOOKS/F1AA2032/1.5.15?SHELF=&DT=20001127174124 +// and +// http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 + +//From Google: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=4e7k0p%24t1v%40blackice.winternet.com&rnum=6&prev=/groups%3Fq%3DMVS%2BPartitioned%2Bdata%2Bset%2Bdirectory%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26selm%3D4e7k0p%2524t1v%2540blackice.winternet.com%26rnum%3D6 + +{ +From: Ralph Goers (rgoer@rgoer.candle.com) +Subject: Re: FTP -- VM, MVS, VMS Questions + + +View this article only +Newsgroups: comp.os.os2.networking.tcp-ip +Date: 1996/01/27 + + +In message <4e7k0p$t1v@blackice.winternet.com> - frickson@gibbon.com (John C. F +rickson) writes: +:> +:>MVS directory lines look like this: +:> +:>Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname +:>OVH025 3380 12/29/95 1 60 FB 80 23200 PO MICSFILE.CLIST +:>USS018 3380 12/18/95 1 15 VB 259 8000 PS NFS.DOC +:>Migrated OAQPS.INTERIM.CNTYIM.V1.DATA.D052093 +:>AIR030 3380 12/13/95 1 18 FB 80 3600 PS OAQPS.INTERIM.PLNTNAME.DATA +:> +:>1) Ext is number of extents? Yes. +:>2) Used is number of tracks? Yes +:>3) Is the volume name important or can I ignore it? +It is important only if it is MIGRAT or migrated. This indicates that the +dataset has not been accessed recently and has been migrated to an HSM +volume (DASD or tape). +:>4) Is there any way to calculate filesizes? I think I would have to know +:> the capacity of the device (i.e. BlkSz * Blks/Trk * Trks) +I'm not sure this would do you any good. Just because you know how many bytes +are in a track doesn't mean that each track is full. In the case of an FB +file you can figure out how many blocks will fit in a track. This may not be +true of V type files. +:>5) Dsorg -- PO seems to equate to partioned data set. Is that always true? +:> Are any values other than PO and PS possible? +Yes - you can also have VS files (VSAM) - however I have no idea why anyone +would try to FTP one. +:>6) What is "Migrated"? Moved to tape? Is the file still available for +:> download, or should I simply ignore these lines? +If you reference the file it will automatically be brought back to a DASD +volume. +:>7) After loggin in, the server reports the current directory as "PA2", +:> but in response to a "PWD" command, it says "PUBLIC.". ??? Not sure about this. Usually, it defaults to "USERID.". +:>8) Is the "CD" command useful for anything other than getting into +:> partitioned data sets? +Sure. If you have a bunch of sequential datasets starting with A.B then +cd'ing to that and then doing MGET * should download all the sequential files. +PDSes should not be downloaded but should be treated as a subdirectory. +:>9) After CD'ing into a PDS, a directory looks like this: +:> Name VV.MM Created Changed Size Init Mod Id +:> $README 01.10 89/04/19 94/12/15 18:55 90 1 0 EWZ +:> What are the Size, Init, Mod and ID fields? +:> Is VV.MM version information? +:> +Size is the number of records in the file. init is the number of lines that +were in the file when statistics were first set and mod is the number of +lines modified since then. VV.MM is a version and modification level. These +SPF statistics can be reset by going into edit on the member and typing STATS +OFF, SAVE, STATS ON, SAVE. They can also be reset via option 3.5 - Member +statistics panel. Some programs also modify them for their own use. + +You should count on these statistics even being present. They are created +only by SPF. Other programs use the same area in the directory entry for +their own purposes. When you link a load module, for example, information +about it is stored there. When you FTP files up to MVS FTP will not create +statistics for PDS members. + +Hope this is helpful. + +Ralph +} +begin + AItem.ModifiedAvail := False; + AItem.SizeAvail := False; + s := TStringList.Create; + try + SplitDelimitedString(AItem.Data, s, True); + if s.Count > 0 then + begin + AItem.FileName := s[0]; + //in some particianed data sets, dates are missing. + if (s.Count > 7) and (s[3] <> '') and IsNumeric(s[3], 1, 1) and (IndyPos('/', s[3]) > 0) then + begin + AItem.ModifiedDate := MVSDate(s[3]); + { Name VV.MM Created Changed Size Init Mod Id} + { $README 01.10 89/04/19 94/12/15 18:55 90 1 0 EWZ } + if s.Count > 4 then + begin + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(s[4]); + AItem.ModifiedAvail := True; + end; + end; + end; + finally + FreeAndNil(s); + end; + Result := True; +end; + +{ TIdFTPLPMVSJESInterface1 } + +class function TIdFTPLPMVSJESInterface1.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; +begin + Result := False; + if AListing.Count > 0 then + begin + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + Result := (s.Count > 2) and (PosInStrArray(Trim(s[2]), MVS_JES_Status) > -1); + if Result and (s.Count > 3) then begin + Result := IsNumeric(s[3]) or CharEquals(s[3], 1, '-'); + end; + if not Result then begin + Result := IsMVS_JESNoJobsMsg(AListing[0]); + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPMVSJESInterface1.GetIdent: String; +begin + Result := 'MVS: JES Queue Interface 1'; {do not localize} +end; + +class function TIdFTPLPMVSJESInterface1.IsMVS_JESNoJobsMsg(const AData: String): Boolean; +begin + Result := (AData = 'No jobs found on JES queue'); {do not localize} +end; + +class function TIdFTPLPMVSJESInterface1.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMVSJESFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMVSJESInterface1.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LI : TIdMVSJESFTPListItem; +begin + { + ALFREDCA JOB03192 OUTPUT 3 Spool Files + ALFREDCA JOB03193 OUTPUT 0 Spool Files + ALFREDCA JOB03194 INPUT + 12345678901234567890123456789012345678901234567890 + 1 2 3 4 5 + #From: IBM Communications Server for OS/390 V2R10 TCP/IP Implementation Guide Volume 2: UNIX Applications + #Obtained at: http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg245228.pdf + } + AItem.ModifiedAvail := False; + AItem.SizeAvail := False; + LI := AItem as TIdMVSJESFTPListItem; + //owner + LBuf := AItem.Data; + LI.OwnerName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //filename + LI.FileName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + case PosInStrArray(Fetch(LBuf), MVS_JES_Status) of + 0 : LI.JobStatus := IdJESReceived; // 'INPUT' job received but not run yet + 1 : LI.JobStatus := IdJESHold; // 'HELD' job is in hold status + 2 : LI.JobStatus := IdJESRunning; // 'ACTIVE' job is running + 3 : LI.JobStatus := IdJESOuptutAvailable; // 'OUTPUT' job has finished and has output available + end; + //spool file output if available + LBuf := TrimLeft(LBuf); + LI.JobSpoolFiles := IndyStrToInt(Fetch(LBuf), 0); + Result := True; +end; + +class function TIdFTPLPMVSJESInterface1.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + LItem : TIdFTPListItem; + i : Integer; +begin + Result := False; + if AListing.Count > 0 then + begin + if not IsMVS_JESNoJobsMsg(Alisting[0]) then + begin + for i := 0 to AListing.Count -1 do + begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + ParseLine(LItem); + end; + end else + begin + Result := True; + end; + end; +end; + +{ TIdFTPLPMVSJESInterface2 } + +class function TIdFTPLPMVSJESInterface2.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +begin + Result := False; + if AListing.Count > 0 then begin + Result := IsMVS_JESIntF2Header(AListing[0]); + end; +end; + +class function TIdFTPLPMVSJESInterface2.GetIdent: String; +begin + Result := 'MVS: JES Queue Interface 2'; {do not localize} +end; + +class function TIdFTPLPMVSJESInterface2.IsMVS_JESIntF2Header(const AData: String): Boolean; +begin + Result := (AData = 'JOBNAME JOBID OWNER STATUS CLASS'); {do not localize} +end; + +class function TIdFTPLPMVSJESInterface2.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMVSJESIntF2FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMVSJESInterface2.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LNo : String; + LPos, LPos2 : Integer; + LI : TIdMVSJESIntF2FTPListItem; +begin + { + JOBNAME JOBID OWNER STATUS CLASS + BPXAS STC02133 ++++++++ OUTPUT STC RC=000 2 spool + BPXAS STC00916 ++++++++ OUTPUT STC RC=000 2 spool + BPXAS STC02132 ++++++++ ACTIVE STC + BPXAS STC02131 ++++++++ ACTIVE STC + 123456789012345678901234567890123456789012345678901234567890 + 1 2 3 4 5 6 + } + LI := AItem as TIdMVSJESIntF2FTPListItem; + LI.ModifiedAvail := False; + LI.SizeAvail := False; + LI.FileName := Trim(Copy(AItem.Data, 10, 8)); + LI.OwnerName := Trim(Copy(AItem.Data, 19, 7)); + if IsLineStr(LI.OwnerName) then begin + LI.OwnerName := ''; + end; + case PosInStrArray(Trim(Copy(AItem.Data, 28, 7)), MVS_JES_Status) of + 0 : LI.JobStatus := IdJESReceived; // 'INPUT' job received but not run yet + 1 : LI.JobStatus := IdJESHold; // 'HELD' job is in hold status + 2 : LI.JobStatus := IdJESRunning; // 'ACTIVE' job is running + 3 : LI.JobStatus := IdJESOuptutAvailable; // 'OUTPUT' job has finished and has output available + end; + LBuf := Trim(Copy(AItem.Data, 35, MaxInt)); + LPos := IndyPos(' spool', LBuf); {do not localize} + if LPos = 0 then + begin + Result := False; + Exit; + end; + LNo := ''; + for LPos2 := LPos-1 downto 1 do + begin + if LBuf[LPos2] = ' ' then begin + Break; + end; + LNo := LBuf[LPos2] + LNo; + end; + LI.JobSpoolFiles := IndyStrToInt(LNo, 0); + Result := True; +end; + +class function TIdFTPLPMVSJESInterface2.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + LItem : TIdFTPListItem; + i : Integer; + LStartLine : Integer; + LDetailFlag : Boolean; + LI : TIdMVSJESIntF2FTPListItem; +begin + Result := False; + if AListing.Count > 0 then + begin + if IsMVS_JESIntf2Header(AListing[0]) then begin + LStartLine := 1; + end else begin + LStartLine := 0; + end; + LDetailFlag := False; + for i := LStartLine to AListing.Count-1 do + begin + if LDetailFlag then + begin + if ADir.Count > 0 then + begin + LI := ADir.Items[ADir.Count-1] as TIdMVSJESIntF2FTPListItem; + LI.Details.Add(AListing[i]); + end; + end + else if AListing[i] = '--------' then begin + LDetailFlag := True; + end else + begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + ParseLine(LItem); + end; + end; + Result := True; + end; +end; + +{ TIdMVSFTPListItem } + +constructor TIdMVSFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FSizeAvail := False; //we can't get the file size from a MVS system +end; + +{ TIdMVSJESIntFFTPListItem } + +constructor TIdMVSJESIntF2FTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FDetails := TStringList.Create; +end; + +destructor TIdMVSJESIntF2FTPListItem.Destroy; +begin + FreeAndNil(FDetails); + inherited Destroy; +end; + +procedure TIdMVSJESIntF2FTPListItem.SetDetails(AValue: TStrings); +begin + FDetails.Assign(AValue); +end; + +{ TIdMVSJESFTPListItem } + +constructor TIdMVSJESFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + JobStatus := IdJESNotApplicable; +end; + +initialization + RegisterFTPListParser(TIdFTPLPMVS); + RegisterFTPListParser(TIdFTPLPMVSPartitionedDataSet); + RegisterFTPListParser(TIdFTPLPMVSJESInterface1); + RegisterFTPListParser(TIdFTPLPMVSJESInterface2); +finalization + UnRegisterFTPListParser(TIdFTPLPMVS); + UnRegisterFTPListParser(TIdFTPLPMVSPartitionedDataSet); + UnRegisterFTPListParser(TIdFTPLPMVSJESInterface1); + UnRegisterFTPListParser(TIdFTPLPMVSJESInterface2); + +end. diff --git a/indy/Protocols/IdFTPListParseMicrowareOS9.pas b/indy/Protocols/IdFTPListParseMicrowareOS9.pas new file mode 100644 index 0000000..8d8ceef --- /dev/null +++ b/indy/Protocols/IdFTPListParseMicrowareOS9.pas @@ -0,0 +1,183 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 2/23/2005 6:34:28 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.4 10/26/2004 9:46:34 PM JPMugaas + Updated refs. + + Rev 1.3 4/19/2004 5:06:10 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.2 2004.02.03 5:45:40 PM czhower + Name changes + + Rev 1.1 10/19/2003 2:27:20 PM DSiders + Added localization comments. + + Rev 1.0 4/7/2003 04:11:38 PM JPMugaas + I mistakenly omitted the OS-9 parser when restructuring. Restored. +} + +unit IdFTPListParseMicrowareOS9; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList, IdFTPListParseBase,IdFTPListTypes; + +type + TIdMicrowareOS9FTPListItem = class(TIdOwnerFTPListItem) + protected + FOS9OwnerPermissions : String; + FOS9PublicPermissions : String; + FOS9MiscPermissions : String; + FOS9Sector: UInt32; + public + property OS9OwnerPermissions : String read FOS9OwnerPermissions write FOS9OwnerPermissions; + property OS9PublicPermissions : String read FOS9PublicPermissions write FOS9PublicPermissions; + property OS9MiscPermissions : String read FOS9MiscPermissions write FOS9MiscPermissions; + property OS9Sector : UInt32 read FOS9Sector write FOS9Sector; + end; + + TIdFTPLPMicrowareOS9 = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseMicrowareOS9"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +const + MICROWARE_OS9 = 'MicroWare OS-9'; {do not localize} + +{ TIdFTPLPMicrowareOS9 } + +class function TIdFTPLPMicrowareOS9.GetIdent: String; +begin + Result := MICROWARE_OS9; +end; + +class function TIdFTPLPMicrowareOS9.IsHeader(const AData: String): Boolean; +var + LWrds : TStrings; +begin + {The banner is usually something like this: + + Directory of . 11:44:44 + Owner Last modified Attributes Sector Bytecount Name + + } + LWrds := TStringList.Create; + try + Result := False; + SplitDelimitedString(AData, LWrds, True); + if LWrds.Count > 2 then + begin + Result := (LWrds[0] = 'Directory') and (LWrds[1] = 'of') and {do not localize} + (PatternsInStr(':', LWrds[LWrds.Count - 1]) = 2); + if not Result then + begin + Result := (LWrds.Count = 7) and + (LWrds[0] = 'Owner') and {do not localize} + (LWrds[1] = 'Last') and {do not localize} + (LWrds[2] = 'modified') and {do not localize} + (LWrds[3] = 'Attributes') and {do not localize} + (LWrds[4] = 'Sector') and {do not localize} + (LWrds[5] = 'Bytecount') and {do not localize} + (LWrds[6] = 'Name'); {do not localize} + end; + end; + finally + FreeAndNil(LWrds); + end; +end; + +class function TIdFTPLPMicrowareOS9.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMicrowareOS9FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMicrowareOS9.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LPerms : String; + LI : TIdMicrowareOS9FTPListItem; +begin + LI := AItem as TIdMicrowareOS9FTPListItem; + LBuf := TrimLeft(LI.Data); + //Owner + LI.OwnerName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //Modified date + LI.ModifiedDate := DateYYMMDD(Fetch(LBuf)); + LBuf := TrimLeft(LBuf); + //not sure what this number is + Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //permissions + LPerms := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + if TextStartsWith(LPerms, 'd') then begin + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + LI.PermissionDisplay := LPerms; + LI.OS9MiscPermissions := Copy(LPerms, 1, 2); + LI.OS9PublicPermissions := Copy(LPerms, 3, 3); + LI.OS9OwnerPermissions := Copy(LPerms, 5, 3); + //sector + LI.OS9Sector := IndyStrToInt64('$'+Fetch(LBuf), 0); + LBuf := TrimLeft(LBuf); + //size not sure if in decimal or hexidecimal + LI.Size := IndyStrToInt64(Fetch(LBuf), 0); + //name + LI.FileName := LBuf; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPMicrowareOS9); +finalization + UnRegisterFTPListParser(TIdFTPLPMicrowareOS9); + +end. diff --git a/indy/Protocols/IdFTPListParseMusic.pas b/indy/Protocols/IdFTPListParseMusic.pas new file mode 100644 index 0000000..eaa9f7d --- /dev/null +++ b/indy/Protocols/IdFTPListParseMusic.pas @@ -0,0 +1,179 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 9:46:36 PM JPMugaas + Updated refs. + + Rev 1.4 4/19/2004 5:05:48 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:28 PM czhower + Name changes + + Rev 1.2 10/19/2003 3:36:02 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:03:56 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 05:51:12 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseMusic; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdMusicFTPListItem = class(TIdRecFTPListItem) + protected + FOwnerName : String; + public + property OwnerName : String read FOwnerName write FOwnerName; + property RecLength; + property RecFormat; + property NumberRecs; + end; + TIdFTPLPMusic = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseMusic"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +{ TIdFTPLPMusic } + +class function TIdFTPLPMusic.GetIdent: String; +begin + Result := 'MUSIC/SP'; {do not localize} +end; + +class function TIdFTPLPMusic.IsHeader(const AData: String): Boolean; +var + LWords : TStrings; +begin + LWords := TStringList.Create; + try + SplitDelimitedString(AData, LWords, True); + Result := (LWords.Count > 7) and + ((LWords[0] = 'File') and {do not localize} + (LWords[1] = 'name') and {do not localize} + (LWords[2] = 'RlenRf') and {do not localize} + (LWords[3] = 'Size') and {do not localize} + (LWords[4] = 'Read') and {do not localize} + (LWords[5] = 'Write') and {do not localize} + (LWords[6] = 'By') and {do not localize} + ((LWords[7] = 'Attrbs') and {do not localize} + (LWords.Count > 8) and + (LWords[8] = '#Recs')) or {do not localize} + (LWords[7] = 'Attrbs#Recs')); {do not localize} + finally + FreeAndNil(LWords); + end; +end; + +class function TIdFTPLPMusic.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdMusicFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPMusic.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LTmp : String; + LDay, LMonth, LYear : Integer; + LI : TIdMusicFTPListItem; +begin + LI := AItem as TIdMusicFTPListItem; + LBuf := AItem.Data; + //file name + LI.FileName := Fetch(LBuf); + if TextEndsWith(AItem.FileName, '\') then + begin + LI.ItemType := ditDirectory; + LI.FileName := Copy(AItem.FileName, 1, Length(AItem.FileName)-1); + end; + //record length and type + LBuf := TrimLeft(LBuf); + LTmp := Fetch(LBuf); + LI.RecFormat := ExtractRecFormat(StripNo(LTmp)); + LI.RecLength := ExtractNumber(LTmp); + if LI.RecFormat = 'DIR' then begin {do not localize} + LI.ItemType := ditDirectory; + end; + //Size - estimate + LBuf := TrimLeft(LBuf); + LTmp := Fetch(LBuf); + LI.Size := ExtractNumber(LTmp) * 1024; //usually, K ends the number + //Read - not sure so lets skip it + LBuf := TrimLeft(LBuf); + Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //Write date - I think this is last modified + LTmp := Fetch(LBuf); + LDay := IndyStrToInt(Copy(LTmp, 1, 2), 1); + LMonth := StrToMonth(Copy(LTmp, 3, 3)); + LYear := Y2Year(IndyStrToInt(Copy(LTmp, 6, MaxInt), 0)); + LI.ModifiedDate := EncodeDate(LYear, LMonth, LDay); + LBuf := TrimLeft(LBuf); + //Write time + LI.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(Fetch(LBuf)); + LBuf := TrimLeft(LBuf); + //Owner + LI.OwnerName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //attribs and rec count + if IndyPos(' ', LBuf) > 0 then + begin + Fetch(LBuf); + LBuf := TrimLeft(LBuf); + end else begin + LI.NumberRecs := IndyStrToInt(LBuf, 0); + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPMusic); +finalization + RegisterFTPListParser(TIdFTPLPMusic) + +end. diff --git a/indy/Protocols/IdFTPListParseNCSAForDOS.pas b/indy/Protocols/IdFTPListParseNCSAForDOS.pas new file mode 100644 index 0000000..183c80f --- /dev/null +++ b/indy/Protocols/IdFTPListParseNCSAForDOS.pas @@ -0,0 +1,199 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 12/5/2004 5:06:30 PM JPMugaas + Tightened the detection for NCSA Telnet FTP Server for DOS. The parser was + causing problems with SuperTCP because the formats are similar. By + preventing SuperTCP from being detected, LongFilenames were not parsed in + SuperTCP running on Windows 2000. + + Rev 1.5 10/26/2004 9:51:14 PM JPMugaas + Updated refs. + + Rev 1.4 6/5/2004 4:45:22 PM JPMugaas + Reports SizeAvail=False for directories in a list. As per the dir format. + + Rev 1.3 4/19/2004 5:05:58 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.2 2004.02.03 5:45:32 PM czhower + Name changes + + Rev 1.1 10/19/2003 3:36:04 PM DSiders + Added localization comments. + + Rev 1.0 2/19/2003 10:13:38 PM JPMugaas + Moved parsers to their own classes. +} + +unit IdFTPListParseNCSAForDOS; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdNCSAforDOSFTPListItem = class(TIdFTPListItem); + + TIdFTPLPNCSAforDOS = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseNCSAForDOS"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + + +{ TIdFTPLPNCSAforDOS } + +class function TIdFTPLPNCSAforDOS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; + LData : String; + i : Integer; +begin + Result := False; + if AListing.Count > 0 then + begin + //we have to loop through because there is a similar format + //in SuperTCP but that one has an additional field with spaces + //for long filenames. + for i := 0 to AListing.Count-2 do + begin + LData := AListing[i]; + s := TStringList.Create; + try + SplitDelimitedString(LData, s, True); + if s.Count = 4 then + begin + Result := ((s[1] = '') or IsNumeric(s[1])) and {do not localize} + IsMMDDYY(s[2], '-') and IsHHMMSS(s[3], ':') and {do not localize} + ExcludeQVNET(LData); + end; + finally + FreeAndNil(s); + end; + if not Result then begin + Break; + end; + end; + Result := IsFooter(AListing[AListing.Count-1]); + end; +end; + +class function TIdFTPLPNCSAforDOS.GetIdent: String; +begin + Result := 'NCSA for MS-DOS (CU/TCP)'; {do not localize} +end; + +class function TIdFTPLPNCSAforDOS.IsFooter(const AData: String): Boolean; +var + LWords : TStrings; +begin + Result := False; + LWords := TStringList.Create; + try + SplitDelimitedString(ReplaceAll(AData, '-', ' '), LWords, True); + while LWords.Count > 2 do begin + LWords.Delete(0); + end; + if LWords.Count = 2 then begin + Result := (LWords[0] = 'Bytes') and (LWords[1] = 'Available'); {do not localize} + end; + finally + FreeAndNil(LWords); + end; +end; + +class function TIdFTPLPNCSAforDOS.IsHeader(const AData: String): Boolean; +begin + Result := False; +end; + +class function TIdFTPLPNCSAforDOS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdNCSAforDOSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPNCSAforDOS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LPt : String; +begin + LBuf := AItem.Data; + {filename - note that a space is illegal in MS-DOS so this should be safe} + AItem.FileName := Fetch(LBuf); + {type or size} + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + if LPt = '' then {do not localize} + begin + AItem.ItemType := ditDirectory; + AItem.SizeAvail := False; + end else + begin + AItem.ItemType := ditFile; + AItem.Size := IndyStrToInt64(LPt, 0); + end; + //time stamp + if LBuf <> '' then + begin + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + if LPt <> '' then + begin + //Date + AItem.ModifiedDate := DateMMDDYY(LPt); + LBuf := Trim(LBuf); + LPt := Fetch(LBuf); + if LPt <> '' then begin + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LPt); + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPNCSAforDOS); +finalization + UnRegisterFTPListParser(TIdFTPLPNCSAforDOS); + +end. diff --git a/indy/Protocols/IdFTPListParseNCSAForMACOS.pas b/indy/Protocols/IdFTPListParseNCSAForMACOS.pas new file mode 100644 index 0000000..9c23b80 --- /dev/null +++ b/indy/Protocols/IdFTPListParseNCSAForMACOS.pas @@ -0,0 +1,131 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/28/2004 9:20:22 PM JPMugaas + Preliminary support for NCSA Telnet's FTP Server for MacIntosh. +} + +unit IdFTPListParseNCSAForMACOS; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ +NCSA Telnet for MacIntosh's FTP Server only lists the filenames followed by a / +if they are directories instead of files. + +About the only way I can see to detect this server type is to use the greeting banner: + +"Macintosh Resident FTP server, ready" or maybe, the SYST reply "MACOS NCSA". + +Unlike many system types, spaces are permitted in filenames. + +This also may work on an old Intercon TCP/Connect for MacIntosh. I only found that +by looking at some source-code in LibWWW which has some contributions from Dartmouth College. + +The code is at: + +http://dev.w3.org/cvsweb/libwww/Library/src/HTFTP.c?rev=1.109&content-type=text/x-cvsweb-markup + +http://dev.w3.org/cvsweb/libwww/Library/src/HTFTPDir.c?rev=2.18&content-type=text/x-cvsweb-markup + +and + +http://web.mit.edu/afs/dev.mit.edu/project/andydev/src/andrew-8.0/WWW/Library/Implementation/HTFTP.c +} +type + TIdNCSAforMACOSFTPListItem = class(TIdMinimalFTPListItem); + + TIdFTPLPNCSAforMACOS = class(TIdFTPLPNList) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseNCSAForMACOS"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal; + +{ TIdFTPLPNCSAforMACOS } + +class function TIdFTPLPNCSAforMACOS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +begin + // From Google: http://groups.google.com/groups?q=MAC-OS+TCP/Connect&hl=en&lr=&selm=881%40lts.UUCP&rnum=4 + //Intercon's MAC-OS reports "MAC-OS TCP/Connect for the Macintosh Version x.x" + //but LibWWW + //http://dev.w3.org/cvsweb/libwww/Library/src/HTFTP.c?rev=1.109&content-type=text/x-cvsweb-markup + //"MAC-OS TCP/ConnectII" + Result := (ASysDescript = 'MAC NCSA') or + TextStartsWith(ASysDescript, ' MAC-OS TCP/Connect'); +end; + +class function TIdFTPLPNCSAforMACOS.GetIdent: String; +begin + Result := 'NCSA for MACOS'; {do not localize} +end; + +class function TIdFTPLPNCSAforMACOS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdNCSAforMACOSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPNCSAforMACOS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +begin + Result := True; + try + if CharIsInSet(AItem.Data, Length(AItem.Data), '/\') then + begin + AItem.ItemType := ditDirectory; + AItem.FileName := Copy(AItem.Data, 1, Length(AItem.Data)-1); + end else + begin + AItem.ItemType := ditFile; + AItem.FileName := AItem.Data; + end; + except + Result := False; + end; +end; + +initialization + RegisterFTPListParser(TIdFTPLPNCSAforMACOS); +finalization + UnRegisterFTPListParser(TIdFTPLPNCSAforMACOS); + +end. diff --git a/indy/Protocols/IdFTPListParseNovellNetware.pas b/indy/Protocols/IdFTPListParseNovellNetware.pas new file mode 100644 index 0000000..629da6b --- /dev/null +++ b/indy/Protocols/IdFTPListParseNovellNetware.pas @@ -0,0 +1,292 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 2/23/2005 6:34:26 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.8 10/26/2004 9:51:14 PM JPMugaas + Updated refs. + + Rev 1.7 4/20/2004 4:01:30 PM JPMugaas + Fix for nasty typecasting error. The wrong create was being called. + + Rev 1.6 4/19/2004 5:05:24 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.5 2004.02.03 5:45:20 PM czhower + Name changes + + Rev 1.4 1/22/2004 4:59:16 PM SPerry + fixed set problems + + Rev 1.3 1/22/2004 7:20:44 AM JPMugaas + System.Delete changed to IdDelete so the code can work in NET. + + Rev 1.2 10/19/2003 3:36:06 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:04:08 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 02:02:08 AM JPMugaas + Individual parsing objects for the new framework. +} + +unit IdFTPListParseNovellNetware; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ +This parser should work with Netware 3 and 4. It will probably work on later +versions of Novell Netware as well. + +} +type + TIdNovellNetwareFTPListItem = class(TIdNovellBaseFTPListItem); + + TIdFTPLPNovellNetware = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseNovellNetware"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPNovellNetware } + +class function TIdFTPLPNovellNetware.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; + + function IsNetwareLine(const AData : String) : Boolean; + var + LPerms : String; + begin + Result := AData <> ''; + if Result then + begin + //The space in the set might be necessary for a test case I had captured + //from a website. I don't know if that is an real FTP list or not but it's + //better to be safe then sorry. At least I did tighten up the Novell Permissions + //check. + Result := CharIsInSet(AData, 1, 'dD- '); {Do not localize} + if Result then + begin + // we need to test HellSoft separately from this even though they will be handled by + //the same parser. + if Result then + begin + LPerms := ExtractNovellPerms(AData); + Result := IsValidNovellPermissionStr(LPerms) and + (Length(LPerms) = 8) and + (not IsNovelPSPattern(AData)); + end; + end; + end; + end; + +begin + Result := False; + if AListing.Count > 0 then + begin + if IsTotalLine(AListing[0]) then begin + Result := (AListing.Count > 1) and IsNetwareLine(AListing[1]); + end else begin + Result := IsNetwareLine(AListing[0]); + end; + end; +end; + +class function TIdFTPLPNovellNetware.GetIdent: String; +begin + Result := 'Novell Netware'; {do not localize} +end; + +class function TIdFTPLPNovellNetware.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdNovellNetwareFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPNovellNetware.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + strs : TStrings; + wYear, LCurrentMonth, wMonth, wDay: Word; + wHour, wMin, wSec, wMSec: Word; + ADate: TDateTime; + LBuf : String; + NameStartPos : Integer; + NameStartIdx : Integer; + LName : String; + LI : TIdNovellNetwareFTPListItem; +begin + { + - [RWCEAFMS] 0 1576 Feb 08 2000 00README + - [RWCEAFMS] 0 742 Jan 03 2001 00INDEX + - [RWCEAFMS] tjhamalainen 1020928 Sep 25 2001 winntnw.exe + d [RWCEAFMS] Okkola 512 Aug 17 03:42 WINDOWS + + -[R----F-] 1 raj 857 Feb 23 2000 !info! + d[R----F-] 1 raj 512 Nov 30 2001 incoming + + d [R----F--] supervisor 512 Jan 16 18:53 login + - [R----F--] rhesus 214059 Oct 20 15:27 cx.exe + } + LI := AItem as TIdNovellNetwareFTPListItem; + NameStartIdx := 5; + // Get defaults for modified date/time + ADate := Now; + DecodeDate(ADate, wYear, wMonth, wDay); + DecodeTime(ADate, wHour, wMin, wSec, wMSec); + LCurrentMonth := wMonth; + + if TextStartsWith(LI.Data, 'D') then begin {do not localize} + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + //Most FTP Clients don't support permissions on a Novel Netware server. + LBuf := AItem.Data; + LI.NovellPermissions := ExtractNovellPerms(LBuf); + LI.PermissionDisplay := '[' + LI.NovellPermissions + ']'; {do not localize} + Fetch(LBuf, '] '); {do not localize} + if LBuf <> '' then + begin + //One Novell Server I found at nf.wsp.krakow.pl + //uses an old version of Novell Netware (3.12 or so). That differs slightly + if LBuf[1] = ' ' then {do not localize} + begin + IdDelete(LBuf, 1, 1); + // LBuf := TrimLeft(LBuf); + Fetch(LBuf); + end; + strs := TStringList.Create; + try + SplitDelimitedString(LBuf, strs, True); + { + 0 - owner + 1 - size + 2 - month + 3 - day of month + 4 - time or year + 5 - start of file name or time + 6 - start of file name if 5 was time + } + if strs.Count > 4 then + begin + LI.OwnerName := strs[0]; + LI.Size := IndyStrToInt64(strs[1], 0); + wMonth := StrToMonth(strs[2]); + if wMonth < 1 then begin + wMonth := LCurrentMonth; + end; + wDay := IndyStrToInt(strs[3], wDay); + if IndyPos(':', Strs[4]) = 0 then {do not localize} + begin + wYear := IndyStrToInt(strs[4], wYear); + wYear := Y2Year(wYear); + wHour := 0; + wMin := 0; + if (Strs.Count > 5) and (IndyPos(':', Strs[5]) > 0) then {do not localize} + begin + LBuf := Strs[5]; + wHour := IndyStrToInt(Fetch(LBuf, ':'), wHour); {do not localize} + wMin := IndyStrToInt(Fetch(LBuf, ':'), wMin); {do not localize} + NameStartIdx := 6; + end; + end else + begin + wYear := AddMissingYear(wDay, wMonth); + LBuf := Strs[4]; + wHour := IndyStrToInt(Fetch(LBuf, ':'), wHour); {do not localize} + wMin := IndyStrToInt(Fetch(LBuf, ':'), wMin); {do not localize} + end; + LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay); + LI.ModifiedDate := LI.ModifiedDate + EncodeTime(wHour, wMin, 0, 0); + //Note that I doubt a file name can start with a space in Novel/ + //Netware. Some code I've seen strips those off. + for NameStartPos := NameStartIdx to Strs.Count -1 do begin + LName := LName + ' ' + Strs[NameStartPos]; {do not localize} + end; + IdDelete(LName, 1, 1); + LI.FileName := LName; + //Novell Netware is case sensitive I think. + LI.LocalFileName := LName; + end; + finally + FreeAndNil(strs); + end; + end; + Result := True; +end; + +class function TIdFTPLPNovellNetware.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + LStartLine, i : Integer; + LItem : TIdFTPListItem; +begin + if AListing.Count = 0 then + begin + Result := True; + Exit; + end; + If IsTotalLine(AListing[0]) then begin + LStartLine := 1; + end else begin + LStartLine := 0; + end; + for i := LStartLine to AListing.Count -1 do + begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + ParseLine(LItem); + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPNovellNetware); +finalization + UnRegisterFTPListParser(TIdFTPLPNovellNetware); + +end. diff --git a/indy/Protocols/IdFTPListParseNovellNetwarePSU.pas b/indy/Protocols/IdFTPListParseNovellNetwarePSU.pas new file mode 100644 index 0000000..b282d44 --- /dev/null +++ b/indy/Protocols/IdFTPListParseNovellNetwarePSU.pas @@ -0,0 +1,363 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 2/23/2005 6:34:28 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.8 10/26/2004 9:51:14 PM JPMugaas + Updated refs. + + Rev 1.7 4/19/2004 5:05:58 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.6 2004.02.03 5:45:34 PM czhower + Name changes + + Rev 1.5 1/22/2004 4:58:24 PM SPerry + fixed set problems + + Rev 1.4 1/22/2004 7:20:46 AM JPMugaas + System.Delete changed to IdDelete so the code can work in NET. + + Rev 1.3 10/19/2003 3:36:10 PM DSiders + Added localization comments. + + Rev 1.2 6/27/2003 02:07:40 PM JPMugaas + Should now compile now that IsNumeric was moved to IdCoreGlobal. + + Rev 1.1 4/7/2003 04:04:12 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 10:13:42 PM JPMugaas + Moved parsers to their own classes. +} + +unit IdFTPListParseNovellNetwarePSU; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdNovellPSU_DOSFTPListItem = class(TIdNovellBaseFTPListItem); + + TIdFTPLPNetwarePSUDos = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdNovellPSU_NFSFTPListItem = class(TIdUnixBaseFTPListItem); + + TIdFTPLPNetwarePSUNFS = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + +const + NOVELLNETWAREPSU = 'Novell Netware Print Services for Unix: '; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseNovellNetwarePSU"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, + SysUtils; + +{ TIdFTPLPNetwarePSUDos } + +class function TIdFTPLPNetwarePSUDos.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LPerms : String; + LData : String; +begin + Result := True; + if AListing.Count >0 then + begin + LData := AListing[0]; + Result := LData <> ''; + if Result then + begin + Result := CharIsInSet(LData, 1, 'dD-'); {do not localize} + if Result then + begin + //we have to be careful to distinguish between Hellsoft and + //NetWare Print Services for UNIX, FTP File Transfer Service + LPerms := ExtractNovellPerms(Copy(LData, 1, 12)); + Result := (Length(LPerms) = 8) and + IsValidNovellPermissionStr(LPerms) and + IsNovelPSPattern(LData); + end; + end; + end; +end; + +class function TIdFTPLPNetwarePSUDos.GetIdent: String; +begin + Result := NOVELLNETWAREPSU + 'DOS Namespace'; {do not localize} +end; + +class function TIdFTPLPNetwarePSUDos.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdNovellPSU_DOSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPNetwarePSUDos.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LModifiedDate : String; + LModifiedTime : String; + LI : TIdNovellPSU_DOSFTPListItem; +begin + //-[RWCEAFMS] server1 3166 Sept 14, 99 2:30 pm vol$log.err + LI := AItem as TIdNovellPSU_DOSFTPListItem; + LBuf := LI.Data; + //item type + if TextStartsWith(LBuf, 'D') then begin {do not localize} + LI.ItemType := ditDirectory; + end else begin + LI.ItemType := ditFile; + end; + IdDelete(LBuf, 1, 1); + LBuf := TrimLeft(LBuf); + //Permissions + LI.NovellPermissions := ExtractNovellPerms(Fetch(LBuf)); + LI.PermissionDisplay := '[' + LI.NovellPermissions + ']'; {do not localize} + LBuf := TrimLeft(LBuf); + //Owner + LI.OwnerName := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //size + LI.Size := IndyStrToInt64(Fetch(LBuf), 0); + LBuf := TrimLeft(LBuf); + //month + //we have to make sure that the month is 3 chars. The list might return Sept instead of Sep + LModifiedDate := Copy(Fetch(LBuf), 1, 3); + LBuf := TrimLeft(LBuf); + //day + LModifiedDate := LModifiedDate + ' ' + Fetch(LBuf); {do not localize} + LModifiedDate := Fetch(LModifiedDate, ','); {do not localize} + LBuf := TrimLeft(LBuf); + //year + LModifiedDate := LModifiedDate + ' ' + Fetch(LBuf); {do not localize} + LBuf := TrimLeft(LBuf); + LI.ModifiedDate := DateStrMonthDDYY(LModifiedDate, ' '); {do not localize} + //time + LModifiedTime := Fetch(LBuf); + LBuf := TrimLeft(LBuf); + //am/pm + LModifiedTime := LModifiedTime + Fetch(LBuf); + LI.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LModifiedTime); + //name + LI.FileName := LBuf; + Result := True; +end; + +{ TIdFTPLPNetwarePSUNFS } + +class function TIdFTPLPNetwarePSUNFS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; + LBuf : string; +begin + Result := False; + { + 1234567890 + -rw------ - 2 root wheel 512 Oct 14, 99 8:45 pm deleted.sav + -rw------ - 1 bill support 1285 Oct 14, 99 9:55 pm 123456789.123456 + + or + + -rw------ 1 root wheel 512 Oct 14 99 8:45 pm deleted.sav + -rw------ 1 bill support 1285 Oct 14 99 9:55 pm 123456789.123456 + + } + if AListing.Count > 0 then + begin + LBuf := AListing[0]; + //remove the extra space that sometimes appears in older versions to "flatten" + //out the listing + if (Length(LBuf) > 9) and (LBuf[10] = ' ') then {do not localize} + begin + IdDelete(LBuf, 10, 1); + end; + Result := IsValidUnixPerms(LBuf, True); + if Result then + begin + s := TStringList.Create; + try + SplitDelimitedString(LBuf, s, True); + Result := (s.Count > 9) and (PosInStrArray(s[9], ['AM', 'PM'], False) <> -1); {do not localize} + if Result then + begin + LBuf := s[6]; + LBuf := Fetch(LBuf, ','); {do not localize} + Result := IsNumeric(LBuf) and IsNumeric(s[7]) and CharEquals(s[8], 3, ':'); + if Result then begin + Result := StrToMonth(s[5]) > 0; + end; + end; + finally + FreeAndNil(s); + end; + end; + end; +end; + +class function TIdFTPLPNetwarePSUNFS.GetIdent: String; +begin + Result := NOVELLNETWAREPSU + 'NFS Namespace'; {do not localize} +end; + +class function TIdFTPLPNetwarePSUNFS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdNovellPSU_NFSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPNetwarePSUNFS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LBuf2 : String; + LI : TIdNovellPSU_NFSFTPListItem; +begin + { + -rw------ - 2 root wheel 512 Oct 14, 99 8:45 pm deleted.sav + -rw------ - 1 bill support 1285 Oct 14, 99 9:55 pm 123456789.123456 + + or + + -rw------- 1 root wheel 512 Oct 14, 99 8:45 pm deleted.sav + -rw------- 1 root wheel 3166 Oct 14, 99 8:45 pm vol$log.err + -rw------- 1 bill support 1285 Oct 14, 99 9:55 pm 123456789.123456 + drw------- 2 mary support 512 Oct 14, 99 7:33 pm locktst + drwxr-xr-x 1 root wheel 512 Oct 14, 99 4:33 pm brief + + Based on: + http://www.novell.com/documentation/lg/nw42/unixpenu/data/tut0i3h5.html#cnovdocsdocenglishnw42unixpenudatahpyvrshhhtml + http://www.novell.com/documentation/lg/nw5/usprint/unixpenu/data/tut0i3h5.html#dnovdocsdocenglishnw5usprintunixpenudatahpyvrshhhtm + http://www.novell.com/documentation/lg/nfs24/docui/index.html#../nfs24enu/data/hpyvrshh.html + } + { + 0 - type of item and Permissions + 1 - # of links + 2 - Owner + 3 - Group + 4 - size + 5 - month + 6 - day + 7 - year + 8 - time + 9 - am/pm + 10 - name + } + LI := AItem as TIdNovellPSU_NFSFTPListItem; + LBuf := LI.Data; + LBuf2 := Fetch(LBuf); + if not IsNumeric(Fetch(LBuf, ' ', False)) then begin {do not localize} + LBuf2 := LBuf2 + Fetch(LBuf); + end; + if TextStartsWith(LBuf2, '-') then begin {do not localize} + LI.ItemType := ditFile; + end else begin + LI.ItemType := ditDirectory; + end; + LI.UnixOwnerPermissions := Copy(LBuf2, 2, 3); + LI.UnixGroupPermissions := Copy(LBuf2, 5, 3); + LI.UnixOtherPermissions := Copy(LBuf2, 8, 3); + LI.PermissionDisplay := LBuf2; + //number of links + LI.LinkCount := IndyStrToInt(Fetch(LBuf), 0); + //Owner + LBuf := TrimLeft(LBuf); + LI.OwnerName := Fetch(LBuf); + //Group + LBuf := TrimLeft(LBuf); + LI.GroupName := Fetch(LBuf); + //Size + LBuf := TrimLeft(LBuf); + AItem.Size := IndyStrToInt64(Fetch(LBuf), 0); + //Date - month + LBuf := TrimLeft(LBuf); + LBuf2 := UpperCase(Fetch(LBuf)) + ' '; {do not localize} + //Date - day + LBuf := TrimLeft(LBuf); + LBuf2 := TrimRight(LBuf2) + ' ' + Fetch(LBuf); {do not localize} + LBuf2 := Fetch(LBuf2, ','); {do not localize} + //Year - year + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf) + ' ' + LBuf2; {do not localize} + //Process Day + LI.ModifiedDate := DateYYStrMonthDD(LBuf2, ' '); {do not localize} + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + //make sure AM/pm are processed + LBuf2 := LBuf2 + Fetch(LBuf); + LI.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LBuf2); + // File name + if IndyPos(UNIX_LINKTO_SYM,LBuf) > 0 then + begin + LI.FileName := Fetch(LBuf, UNIX_LINKTO_SYM); + LI.LinkedItemName := LBuf; + end + else if IndyPos(UNIX_LINKTO_SYM, LBuf) > 0 then + begin + LI.FileName := Fetch(LBuf, UNIX_LINKTO_SYM); + LI.LinkedItemName := LBuf; + end else + begin + LI.FileName := LBuf; + end; + //Novell Netware is case sensitive I think. + LI.LocalFileName := AItem.FileName; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPNetwarePSUDos); + RegisterFTPListParser(TIdFTPLPNetwarePSUNFS); +finalization + UnRegisterFTPListParser(TIdFTPLPNetwarePSUDos); + UnRegisterFTPListParser(TIdFTPLPNetwarePSUNFS); + +end. diff --git a/indy/Protocols/IdFTPListParseOS2.pas b/indy/Protocols/IdFTPListParseOS2.pas new file mode 100644 index 0000000..c959f0a --- /dev/null +++ b/indy/Protocols/IdFTPListParseOS2.pas @@ -0,0 +1,217 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 11/29/2004 2:45:28 AM JPMugaas + Support for DOS attributes (Read-Only, Archive, System, and Hidden) for use + by the Distinct32, OS/2, and Chameleon FTP list parsers. + + Rev 1.5 10/26/2004 9:51:16 PM JPMugaas + Updated refs. + + Rev 1.4 4/19/2004 5:05:46 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:28 PM czhower + Name changes + + Rev 1.2 10/19/2003 3:36:14 PM DSiders + Added localization comments. + + Rev 1.1 10/1/2003 05:27:36 PM JPMugaas + Reworked OS/2 FTP List parser for Indy. The aprser wasn't detecting OS/2 in + some more samples I was able to get ahold of. + + Rev 1.0 2/19/2003 05:50:28 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseOS2; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ + This parser is based on some data that I had managed to obtain second hand + from what people posted on the newsgroups. +} + +type + TIdOS2FTPListItem = class(TIdDOSBaseFTPListItem); + + TIdFTPLPOS2 = class(TIdFTPLPBaseDOS) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + +const + OS2PARSER = 'OS/2'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseOS2"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + + +{ TIdFTPLPOS2 } + +class function TIdFTPLPOS2.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuf, LBuf2 : String; + LNum : String; +begin + { + " 73098 A 04-06-97 15:15 ds0.internic.net1996052434624.txt" + " 0 DIR 12-11-95 13:55 z" + or maybe this: + taken from the FileZilla source-code comments + " 0 DIR 05-12-97 16:44 PSFONTS" + "36611 A 04-23-103 10:57 OS2 test1.file" + " 1123 A 07-14-99 12:37 OS2 test2.file" + " 0 DIR 02-11-103 16:15 OS2 test1.dir" + " 1123 DIR A 10-05-100 23:38 OS2 test2.dir" + } + Result := False; + if AListing.Count > 0 then + begin + LBuf := TrimLeft(AListing[0]); + LNum := Fetch(LBuf); + if not IsNumeric(LNum) then begin + Exit; + end; + repeat + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + if LBuf2 = 'DIR' then {do not localize} + begin + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + end; + if IsMMDDYY(LBuf2, '-') then begin + //we found a date + Break; + end; + if not IsValidAttr(LBuf2) then begin + Exit; + end; + until False; + //there must be two spaces between the date and time + if not TextStartsWith(LBuf, ' ') then begin {do not localize} + Exit; + end; + if (Length(LBuf) >= 3) and (LBuf[3] = ' ') then begin {do not localize} + Exit; + end; + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsHHMMSS(LBuf2, ':'); + end; +end; + +class function TIdFTPLPOS2.GetIdent: String; +begin + Result := OS2PARSER; +end; + +class function TIdFTPLPOS2.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdOS2FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPOS2.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, lBuf2, LNum : String; + LOSItem : TIdOS2FTPListItem; +begin + { + Assume layout such as: + + 0 DIR 02-18-94 19:47 BC + 79836 A 11-19-96 19:08 w.txt + 12345678901234567890123456789012345678901234567890123456789012345678 + 1 2 3 4 5 6 + } + Result := False; + LBuf := AItem.Data; + LBuf := TrimLeft(LBuf); + LNum := Fetch(LBuf); + AItem.Size := IndyStrToInt64(LNum, 0); + LOSItem := AItem as TIdOS2FTPListItem; + repeat + //keep going until we find a date + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + if LNum = '0' then + begin + if LBuf2 = 'DIR' then {do not localize} + begin + LOSItem.ItemType := ditDirectory; + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + end; + end; + if IsMMDDYY(LBuf2, '-') then + begin + //we found a date + LOSItem.ModifiedDate := DateMMDDYY(LBuf2); + Break; + end; + LOSItem.Attributes.AddAttribute(LBuf2); + if LBuf = '' then begin + Exit; + end; + until False; + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + if IsHHMMSS(LBuf2, ':') then begin {do not localize} + LOSItem.ModifiedDate := LOSItem.ModifiedDate + TimeHHMMSS(LBuf2); + end; + //fetch removes one space. We ned to remove an additional one + //before the filename as a filename might start with a space + Delete(LBuf, 1, 1); + LOSItem.FileName := LBuf; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPOS2); +finalization + UnRegisterFTPListParser(TIdFTPLPOS2); + +end. diff --git a/indy/Protocols/IdFTPListParsePCNFSD.pas b/indy/Protocols/IdFTPListParsePCNFSD.pas new file mode 100644 index 0000000..19c6cb1 --- /dev/null +++ b/indy/Protocols/IdFTPListParsePCNFSD.pas @@ -0,0 +1,202 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 12/8/2004 10:58:34 AM JPMugaas + PC-NFSD FTP List parser. + + Rev 1.0 12/8/2004 10:37:42 AM JPMugaas + Parser for PC-NFS for DOS. +} + +unit IdFTPListParsePCNFSD; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ + This parser is a little more tolarant of stuff than others because of scanty samples. + I only found one second hand and it might not have included a header or footer. + Here's all I had: + + prog1 exe 2,563,136 06-10-99 10:00a + temp 01-27-97 3:41p + + That was from the TotalCommander helpfile. + It was part of a PC-NFSD package for MS-DOS which included a FTP Deamon. +} + +type + TIdPCNFSDFTPListItem = class(TIdFTPListItem); + + TIdFTPLPPCNFSD = class(TIdFTPListBase) + protected + class function CheckLine(const AData : String): Boolean; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParsePCNFSD"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +const + DIR = ''; {Do not translate} + +{ TIdFTPLPPC-NFSD } + +class function TIdFTPLPPCNFSD.CheckLine(const AData: String): Boolean; +var + s : TStrings; + i : Integer; + LBuf : String; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count > 3 then + begin + //last col -time + i := s.Count - 1; + LBuf := s[i]; + if CharIsInSet(LBuf, Length(LBuf), 'ap') then {do not localize} + begin + LBuf := Fetch(LBuf, 'a'); {Do not localize} + LBuf := Fetch(LBuf, 'p'); {Do not localize} + if IsHHMMSS(LBuf, ':') then + begin + Dec(i); + //date + if IsMMDDYY(s[i], '-') then + begin + Dec(i); + // size or dir + if IsNumeric(s[i]) or (s[i] = DIR) then begin + Result := (i = 0) or (i = 1); + end; + end; + end; + end; + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPPCNFSD.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + i : Integer; +begin + Result := False; + for i := 0 to AListing.Count -1 do + begin + Result := CheckLine(AListing[i]); + if Result then begin + Break; + end; + end; +end; + +class function TIdFTPLPPCNFSD.GetIdent: String; +begin + Result := 'PC-NFSD'; {Do not localize} +end; + +class function TIdFTPLPPCNFSD.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdPCNFSDFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPPCNFSD.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LI : TIdPCNFSDFTPListItem; + s : TStrings; + i : Integer; +begin + Result := False; + LI := AItem as TIdPCNFSDFTPListItem; + s := TStringList.Create; + try + SplitDelimitedString(LI.Data, s, True); + if s.Count > 3 then + begin + LI.FileName := s[0]; + //assume filename 8.3 requirements in MS-DOS + if Length(s[1]) < 4 then + begin + LI.FFileName := LI.FFileName + '.' + s[1]; + i := 2; + end else begin + i := 1; + end; + // or size + LI.Size := ExtractNumber(s[i], False); + if (LI.Size <> -1) or (s[i] = DIR) then + begin + if s[i] = DIR then + begin + LI.ItemType := ditDirectory; + LI.SizeAvail := False; + end; + Inc(i); + //date + if IsMMDDYY(s[i], '-') then + begin + LI.ModifiedDate := DateMMDDYY(s[i]); + Inc(i); + //time + if CharIsInSet(s[i], Length(s[i]), 'ap') then {Do not localize} + begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(s[i]); + Result := True; + end; + end; + end; + end; + finally + FreeAndNil(s); + end; +end; + +initialization + RegisterFTPListParser(TIdFTPLPPCNFSD); +finalization + UnRegisterFTPListParser(TIdFTPLPPCNFSD); + +end. diff --git a/indy/Protocols/IdFTPListParsePCTCP.pas b/indy/Protocols/IdFTPListParsePCTCP.pas new file mode 100644 index 0000000..6e35632 --- /dev/null +++ b/indy/Protocols/IdFTPListParsePCTCP.pas @@ -0,0 +1,188 @@ +unit IdFTPListParsePCTCP; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdPCTCPFTPListItem = class(TIdFTPListItem); + + TIdFTPLPPCTCPNet = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseWinQVTNET"'} + {$ENDIF} + +{ +THis is a parser for "PC/TCP v 2.11 ftpsrv.exe". This was a part of the PC/TCP +suite from FTP Software Inc. + +based on + +http://www.farmanager.com/viewvc/plugins/ftp/trunk/lib/DirList/pr_pctcp.cpp?revision=275&view=markup&pathrev=788 + +Note that no source-code was used, just the listing data. + + PC/TCP ftpsrv.exe + looks like + 1 2 3 4 5 6 +0123456789012345678901234567890123456789012345678901234567890 +------------------------------------------------------------- + 1 2 3 4 5 6 +123456789012345678901234567890123456789012345678901234567890 +------------------------------------------------------------- + 40774 IO.SYS Tue May 31 06:22:00 1994 + 38138 MSDOS.SYS Tue May 31 06:22:00 1994 + 54645 COMMAND.COM Tue May 31 06:22:00 1994 + UTIL Thu Feb 20 09:55:02 2003 +} +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ TIdFTPLPPCTCPNet } + +class function TIdFTPLPPCTCPNet.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LData : String; +begin + Result := False; + + if AListing.Count > 0 then begin + LData := AListing[0]; + //size or dir + Result := (LData ='') or IsNumeric(Trim(Copy(LData,1,10))); + //file name + if Result then begin + Result := Trim(Copy(LData,11,19))<> ''; + end; + //day of week + if Result then begin + Result := StrToDay(Trim(Copy(LData,31,7))) > 0; + end; + //month + if Result then begin + Result := StrToMonth(Copy(LData,38,3)) > 0; + end; + //day + if Result then begin + Result := StrToIntDef(Copy(LData,42,2),0) > 0; + end; + //time + if Result then begin + Result := IsHHMMSS(Copy(LData,45,8),':'); + end; + //year + if Result then begin + Result := IsNumeric(Trim(Copy(LData,54,4))); + end; + end; +end; + +class function TIdFTPLPPCTCPNet.GetIdent: String; +begin + Result := 'PC/TCP ftpsrv.exe'; +end; + +class function TIdFTPLPPCTCPNet.MakeNewItem( + AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdPCTCPFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPPCTCPNet.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var LData : String; + LPt : String; + LMonth : Word; + LDay : Word; + LYear : Word; +begin + Result := False; + LData := TrimLeft(AItem.Data); + LPt := Fetch(LData); + //dir or file size + if LPt = '' then begin + AItem.ItemType := ditDirectory; + AItem.SizeAvail := False; + end else begin + if IsNumeric(LPt) then begin + AItem.Size := StrToIntDef(LPt,0); + AItem.SizeAvail := True; + end else begin + exit; + end; + end; + //file name + LData := TrimLeft(LData); + LPt := Fetch(LData); + if LPt = '' then begin + Exit; + end else begin + AItem.FileName := LPt; + end; + //Day of week + LData := TrimLeft(LData); + LPt := Fetch(LData); + if StrToDay(LPt) < 1 then begin + exit; + end; + //month + LData := TrimLeft(LData); + LPt := Fetch(LData); + LMOnth := StrToMonth(LPt); + if LMonth < 1 then begin + exit; + end; + //day + LData := TrimLeft(LData); + LPt := Fetch(LData); + LDay := StrToIntDef(LPt,0); + if LDay = 0 then begin + exit; + end; + + //time + LData := TrimLeft(LData); + LPt := Fetch(LData); + if not IsHHMMSS(LPt,':') then begin + exit; + end; + AItem.ModifiedDate := TimeHHMMSS(LPt); + //year + LData := TrimLeft(LData); + LPt := Fetch(LData); + LYear := StrToIntDef(LPt,$FFFF); + if LYear = $FFFF then begin + Exit; + end; + LYear := Y2Year(LYear); + AItem.ModifiedDate := AItem.ModifiedDate + EncodeDate(LYear,LMonth,LDay); + AItem.ModifiedAvail := True; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPPCTCPNet); +finalization + UnRegisterFTPListParser(TIdFTPLPPCTCPNet); +end. diff --git a/indy/Protocols/IdFTPListParseStercomOS390Exp.pas b/indy/Protocols/IdFTPListParseStercomOS390Exp.pas new file mode 100644 index 0000000..d813784 --- /dev/null +++ b/indy/Protocols/IdFTPListParseStercomOS390Exp.pas @@ -0,0 +1,182 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 10/26/2004 9:55:58 PM JPMugaas + Updated refs. + + Rev 1.2 4/19/2004 5:06:10 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.1 10/19/2003 3:36:18 PM DSiders + Added localization comments. + + Rev 1.0 10/1/2003 12:55:20 AM JPMugaas + New FTP list parsers. +} + +unit IdFTPListParseStercomOS390Exp; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdSterCommExpOS390FTPListItem = class(TIdFTPListItem) + protected + FRecFormat : String; + FRecLength : Integer; + FBlockSize : Integer; + public + property RecFormat : String read FRecFormat write FRecFormat; + property RecLength : Integer read FRecLength write FRecLength; + property BlockSize : Integer read FBlockSize write FBlockSize; + end; + + TIdFTPLPSterCommExpOS390 = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + +const + STIRCOMEXPOS390 = 'Connect:Express for OS/390'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseStercomOS390Exp"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ + "Connect:Express OS/390 FTP Guide Version 4.1" Copyright + 2002, 2003 Sterling Commerce, Inc. + + 125 LIST Command accepted. + -D 2 T VB 00244 18000 FTPGDG!PSR$TST.GDG.TSTGDG0(+01) + -D 2 * VB 00244 27800 FTPV!PSR$TST.A.VVV.&REQNUMB + -F 1 R - - - FTPVAL1!PSR$TST.A.VVV + 250 list completed successfully. + + The LIST of symbolic files from Connect:Express Files directory available for + User FTP1 is sent. A number of File attributes are showed. Default profile FTPV + is part of the list. The Following attributes are sent: + - Dynamic or Fixed Allocation + - Allocation rule: 2 = to be created, 1 = pre-allocated, 0=to be created or replaced + - Direction Transmission, Reception, * = both + - File record format (Variable, Fixed, Blocked..) + - Record length + - Block size +} + +{ TIdFTPLPSterCommExpOS390 } + +class function TIdFTPLPSterCommExpOS390.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuf : String; +begin + Result := False; + if AListing.Count > 0 then + begin + if not IdFTPCommon.IsTotalLine(AListing[0]) then + begin + LBuf := AListing[0]; + if Length(LBuf) >= 3 then + begin + if CharIsInSet(LBuf, 2, 'DF') and (LBuf[3] = ' ') then {do not localize} + begin + Result := True; + Exit; + end; + end; + if Length(LBuf) >= 5 then + begin + if CharIsInSet(LBuf, 4, '012') and (LBuf[5] = ' ') then {do not localize} + begin + Result := True; + Exit; + end; + end; + end; + end; +end; + +class function TIdFTPLPSterCommExpOS390.GetIdent: String; +begin + Result := STIRCOMEXPOS390; +end; + +class function TIdFTPLPSterCommExpOS390.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdSterCommExpOS390FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPSterCommExpOS390.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + s : TStrings; + LI : TIdSterCommExpOS390FTPListItem; +begin + LI := AItem as TIdSterCommExpOS390FTPListItem; + s := TStringList.Create; + try + SplitDelimitedString(AItem.Data, s, True); + if s.Count > 3 then + begin + if s[3] <> '-' then begin {do not localize} + LI.RecFormat := s[3]; + end; + end; + if s.Count > 4 then begin + LI.RecLength := IndyStrToInt64(s[4], 0); + end; + if s.Count > 5 then begin + LI.BlockSize := IndyStrToInt64(s[5], 0); + end; + if s.Count > 6 then begin + LI.FileName := s[6]; + end; + finally + FreeAndNil(s); + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPSterCommExpOS390); +finalization + UnRegisterFTPListParser(TIdFTPLPSterCommExpOS390); + +end. diff --git a/indy/Protocols/IdFTPListParseStercomUnixEnt.pas b/indy/Protocols/IdFTPListParseStercomUnixEnt.pas new file mode 100644 index 0000000..fa73d2b --- /dev/null +++ b/indy/Protocols/IdFTPListParseStercomUnixEnt.pas @@ -0,0 +1,486 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 10/26/2004 9:55:58 PM JPMugaas + Updated refs. + + Rev 1.3 7/31/2004 6:55:06 AM JPMugaas + New properties. + + Rev 1.2 4/19/2004 5:06:12 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.1 10/19/2003 3:36:20 PM DSiders + Added localization comments. + + Rev 1.0 10/1/2003 12:55:22 AM JPMugaas + New FTP list parsers. +} + +unit IdFTPListParseStercomUnixEnt; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdSterCommEntUxFTPListItem = class(TIdOwnerFTPListItem) + protected + FFlagsProt : String; + FProtIndicator : String; + public + property FlagsProt : String read FFlagsProt write FFlagsProt; + property ProtIndicator : String read FProtIndicator write FProtIndicator; + end; + + TIdFTPLPSterComEntBase = class(TIdFTPListBaseHeader) + protected + class function IsFooter(const AData : String): Boolean; override; + end; + + TIdFTPLPSterCommEntUx = class(TIdFTPLPSterComEntBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdSterCommEntUxNSFTPListItem = class(TIdOwnerFTPListItem); + + TIdFTPLPSterCommEntUxNS = class(TIdFTPLPSterComEntBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class procedure StripPlus(var VString : String); + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdSterCommEntUxRootFTPListItem = class(TIdMinimalFTPListItem); + + TIdFTPLPSterCommEntUxRoot = class(TIdFTPLPSterComEntBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + +const + STIRCOMUNIX = 'CONNECT:Enterprise for UNIX'; {do not localize} + STIRCOMUNIXNS = STIRCOMUNIX + '$$'; {do not localize} //dir with $$ parameter + STIRCOMUNIXROOT = STIRCOMUNIX + ' ROOT'; {do not localize} //root dir for mailboxes + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseStercomUnixEnt"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ TIdFTPLPSterCommEntUx } + +class function TIdFTPLPSterCommEntUx.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuf : String; + + function IsSterPrefix(const AStr : String) : Boolean; + begin + Result := False; + if Length(AStr) = 13 then + begin + if IsValidSterCommFlags(Copy(AStr, 1, 10)) then begin + Result := IsValidSterCommProt(Copy(AStr, 11, 3)); + end; + end; + end; + +begin +{ +Expect a pattern such as this: + +These are from the The Jakarta Project test cases CVS code. Only +the string constants from a test case are used. +-C--E-----FTP B QUA1I1 18128 41 Aug 12 13:56 QUADTEST +-C--E-----FTP A QUA1I1 18128 41 Aug 12 13:56 QUADTEST2 + +From: + +http://www.mail-archive.com/commons-user@jakarta.apache.org/msg03809.html +Person noted this was from a "CONNECT:Enterprise for UNIX 1.3.01 Secure FTP" + +The first few letters (ARTE, AR) are flags associated with each file. +The two sets of numbers represent batch IDs and file sizes. + +-ARTE-----TCP A cbeodm 22159 629629 Aug 06 05:47 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 4915 1031030 Aug 06 09:12 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 16941 321321 Aug 06 12:41 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 7872 3010007 Aug 07 02:31 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 2737 564564 Aug 07 05:54 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 14879 991991 Aug 07 08:57 PSEUDOFILENAME +-ARTE-----TCP A cbeodm 5183 332332 Aug 07 12:37 PSEUDOFILENAME +-AR-------TCP A cbeodm 5252 2767765 Aug 08 01:49 PSEUDOFILENAME +-AR-------TCP A cbeodm 15502 537537 Aug 08 05:44 PSEUDOFILENAME +-AR-------TCP A cbeodm 13444 1428427 Aug 08 09:01 PSEUDOFILENAME + +-SR--M------- A steve 1 369 Sep 02 13:47 <> +} + Result := False; + if AListing.Count > 0 then + begin + LBuf := AListing[0]; + Result := IsSterPrefix(Fetch(LBuf)); + if Result then begin + Result := IsValidSterCommProt(Fetch(LBuf)); + end; + end; +end; + +class function TIdFTPLPSterCommEntUx.GetIdent: String; +begin + Result := STIRCOMUNIX; +end; + +class function TIdFTPLPSterCommEntUx.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdSterCommEntUxFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPSterCommEntUx.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LTmp : String; + wYear, wMonth, wDay, wHour, wMin, wSec : Word; + LI : TIdSterCommEntUxFTPListItem; +begin + LI := AItem as TIdSterCommEntUxFTPListItem; + DecodeDate(Now, wYear, wMonth, wDay); + wHour := 0; + wMin := 0; + wSec := 0; + LBuf := AItem.Data; + //flags and protocol + LBuf := TrimLeft(LBuf); + LI.FlagsProt := Fetch(LBuf); + //protocol indicator + LBuf := TrimLeft(LBuf); + LI.ProtIndicator := Fetch(LBuf); + // owner + LI.OwnerName := Fetch(LBuf); + //file size + repeat + LBuf := TrimLeft(LBuf); + LTmp := Fetch(LBuf); + if LBuf <> '' then + begin + if IsNumeric(LBuf[1]) then begin + //we found the month + Break; + end; + LI.Size := IndyStrToInt(LTmp, 0); + end; + until False; + //month + wMonth := StrToMonth(LTmp); + //day + LBuf := TrimLeft(LBuf); + LTmp := Fetch(LBuf); + wDay := IndyStrToInt(LTmp, wDay); + //year or time + LBuf := TrimLeft(LBuf); + LTmp := Fetch(LBuf); + if IndyPos(':', LTmp) > 0 then {do not localize} + begin + //year is missing - just get the time + wYear := AddMissingYear(wDay, wMonth); + wHour := IndyStrToInt(Fetch(LTmp, ':'), 0); {do not localize} + wMin := IndyStrToInt(Fetch(LTmp, ':'), 0); {do not localize} + end else + begin + wYear := IndyStrToInt(LTmp, wYear); + end; + LI.FileName := LBuf; + LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay); + LI.ModifiedDate := LI.ModifiedDate + EncodeTime(wHour, wMin, wSec, 0); + Result := True; +end; + +{ TIdFTPLPSterCommEntUxNS } + +class function TIdFTPLPSterCommEntUxNS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuf, LBuf2 : String; +{ +The list format is this: + +solution 00003444 00910368 030924-1636 A R TCP BIN +solution 00000439 02688275 030925-0940 A RT TCP BIN +solution 00003603 00124000 + 030925-1548 A RT TCP BIN +solution 00003604 00265440 + 030925-1548 A RT TCP BIN +solution 00003605 00030960 + 030925-1548 A RT TCP BIN +solution 00003606 00007341 + 030925-1548 A RT TCP ASC +solution 00002755 06669222 <3963338.fix.000> 030926-0811 A R TCP BIN +solution 00003048 00007341 + 030926-0832 A RT TCP ASC +solution 00002137 00427516 030926-1001 A RT TCP BIN +solution 00002372 00007612 030926-1038 A RT TCP BIN +solution 00003043 06669222 <3963338.fix.001> 030926-1236 A RT TCP BIN +solution 00001079 57267791 030926-1301 A RT TCP BIN +solution 00003188 06669222 030926-1312 A R TCP BIN +solution 00002172 120072022 030929-1059 A RT TCP BIN +Total Number of batches listed: 14 +} + function IsValidDate(const ADate : String) : Boolean; + var + LLBuf, LDate : String; + LDay, LMonth, LHour, LMin : Word; + begin + LLBuf := ADate; + LDate := Fetch(LLBuf, '-'); {do not localize} + LMonth := IndyStrToInt(Copy(LDate, 3, 2), 0); + Result := (LMonth > 0) and (LMonth < 13); + if not Result then begin + Exit; + end; + LDay := IndyStrToInt(Copy(LDate, 5, 2), 0); + Result := (LDay > 0) and (LDay < 32); + if not Result then begin + Exit; + end; + LHour := IndyStrToInt(Copy(LLBuf, 1, 2), 0); + Result := (LHour > 0) and (LHour < 25); + if not Result then begin + Exit; + end; + LMin := IndyStrToInt(Copy(LLBuf, 3, 2), 0); + Result := (LMin < 60); + end; + +begin + Result := False; + if AListing.Count > 0 then + begin + if IsFooter(AListing[0]) then + begin + Result := True; + Exit; + end; + if IndyPos('>', AListing[0]) > 0 then {do not localize} + begin + LBuf := AListing[0]; + Fetch(LBuf, '>'); {do not localize} + StripPlus(LBuf); + LBuf := TrimLeft(LBuf); + if IsValidDate(Fetch(LBuf)) then + begin + LBuf2 := RightStr(LBuf, 7); + if IsValidSterCommProt(Copy(LBuf2, 1, 3)) then + begin + if IsValidSterCommData(Copy(LBuf2, 5, 3)) then + begin + LBuf := Copy(LBuf, 1, Length(LBuf)-7); + Result := IsValidSterCommFlags(LBuf); + end; + end; + end; + end; + end; +end; + +class function TIdFTPLPSterCommEntUxNS.GetIdent: String; +begin + Result := STIRCOMUNIXNS; +end; + +class function TIdFTPLPSterCommEntUxNS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdSterCommEntUxNSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPSterCommEntUxNS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LYear, LMonth, LDay : Word; + LHour, LMin : Word; + LI : TIdSterCommEntUxNSFTPListItem; +begin + { + The format is like this: + + ACME 00000020 00000152 990926-1431 CD FTP ASC + ACME 00000019 00000152 990926-1435 CD FTP ASC + ACME 00000014 00000606 990929-1306 A R TCP BIN + ACME 00000013 00000606 990929-1308 A R TCP EBC + ACME 00000004 00000606 990929-1309 A R TCP ASC + Total Number of batches listed: 5 + + Note that this was taken from: + "Connect:Enterprise UNIX Remote Users Guide Version 2.1 " Copyright + 1999, 2002, 2003 Sterling Commerce, Inc. + } + LI := AItem as TIdSterCommEntUxNSFTPListItem; + // owner + LBuf := AItem.Data; + LI.OwnerName := Fetch(LBuf); + //8 digit batch - skip + LBuf := TrimLeft(LBuf); + Fetch(LBuf); + //size + LBuf := TrimLeft(LBuf); + LI.Size := IndyStrToInt64(Fetch(LBuf), 0); + //filename + Fetch(LBuf, '<'); {do not localize} + LI.FileName := Fetch(LBuf, '>'); {do not localize} + StripPlus(LBuf); + //date + LBuf := TrimLeft(LBuf); + //since we aren't going to do anything else after the date, + //we should process as a string; + //Date format: 990926-1431 + LBuf := Copy(LBuf, 1, 11); + LYear := IndyStrToInt(Copy(LBuf, 1, 2), 0); + LYear := Y2Year(LYear); + LMonth := IndyStrToInt(Copy(LBuf, 3, 2), 0); + LDay := IndyStrToInt(Copy(LBuf, 5, 2), 0); + // got the date + StripPlus(LBuf); + Fetch(LBuf, '-'); {do not localize} + LI.ModifiedDate := EncodeDate(LYear, LMonth, LDay); + LHour := IndyStrToInt(Copy(LBuf, 1, 2), 0); + LMin := IndyStrToInt(Copy(LBuf, 3, 2), 0); + LI.ModifiedDate := LI.ModifiedDate + EncodeTime(LHour, LMin, 0, 0); + Result := True; +end; + +class procedure TIdFTPLPSterCommEntUxNS.StripPlus(var VString: String); +begin + if TextStartsWith(VString, '+') then begin {do not localize} + IdDelete(VString, 1, 1); + end; +end; + +{ TIdFTPLPSterCommEntUxRoot } + +class function TIdFTPLPSterCommEntUxRoot.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuf : String; +begin + Result := False; + if AListing.Count > 0 then + begin + if IsFooter(AListing[0]) then begin + Result := True; + Exit; + end; + //The line may be something like this: + //d - - - - - - - steve + //123456789012345678901234567890 + // 1 2 3 + //do not check for the "-" in case its something we don't know + //about. Checking for "d" should be okay as a mailbox listed here + //is probably like a dir + LBuf := AListing[0]; + if (Length(LBuf) >= 26) and + (LBuf[1] = 'd') and {do not localize} + (Copy(LBuf, 2, 3) = ' ') and {do not localize} + (LBuf[5] <> ' ') and {do not localize} + (Copy(LBuf, 6, 3) = ' ') and {do not localize} + (LBuf[9] <> ' ') and {do not localize} + (Copy(LBuf, 10, 3) = ' ') and {do not localize} + (LBuf[13] <> ' ') and {do not localize} + (Copy(LBuf, 14, 2) = ' ') and {do not localize} + (LBuf[16] <> ' ') and {do not localize} + (Copy(LBuf, 17, 2) = ' ') and {do not localize} + (LBuf[19] <> ' ') and {do not localize} + (Copy(LBuf, 20, 2) = ' ') and {do not localize} + (LBuf[22] <> ' ') and {do not localize} + (Copy(LBuf, 23, 2) = ' ') and {do not localize} + (LBuf[25] <> ' ') and {do not localize} + (LBuf[26] = ' ') then {do not localize} + begin + Result := True; + end; + end; +end; + +class function TIdFTPLPSterCommEntUxRoot.GetIdent: String; +begin + Result := STIRCOMUNIXROOT; +end; + +class function TIdFTPLPSterCommEntUxRoot.IsFooter(const AData: String): Boolean; +begin + Result := TextStartsWith(AData, 'Total number of Mailboxes = '); {do not localize} +end; + +class function TIdFTPLPSterCommEntUxRoot.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdSterCommEntUxRootFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPSterCommEntUxRoot.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +begin + AItem.FileName := Copy(AItem.Data, 27, MaxInt); + //mailboxes are just subdirs + AItem.ItemType := ditDirectory; + Result := True; +end; + +{ TIdFTPLPSterComEntBase } + +class function TIdFTPLPSterComEntBase.IsFooter(const AData: String): Boolean; +var + LData : String; +begin + LData := UpperCase(AData); + Result := (IndyPos('TOTAL NUMBER OF ', LData) > 0) and {do not localize} + (IndyPos(' BATCH', LData) > 0) and {do not localize} + (IndyPos('LISTED:', LData) > 0); {do not localize} +end; + +initialization + RegisterFTPListParser(TIdFTPLPSterCommEntUx); + RegisterFTPListParser(TIdFTPLPSterCommEntUxNS); + RegisterFTPListParser(TIdFTPLPSterCommEntUxRoot); +finalization + UnRegisterFTPListParser(TIdFTPLPSterCommEntUx); + UnRegisterFTPListParser(TIdFTPLPSterCommEntUxNS); + UnRegisterFTPListParser(TIdFTPLPSterCommEntUxRoot); + +end. diff --git a/indy/Protocols/IdFTPListParseStratusVOS.pas b/indy/Protocols/IdFTPListParseStratusVOS.pas new file mode 100644 index 0000000..a880fac --- /dev/null +++ b/indy/Protocols/IdFTPListParseStratusVOS.pas @@ -0,0 +1,702 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 2/23/2005 6:34:26 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.0 11/24/2004 12:17:00 PM JPMugaas + New parser for Stratus VOS. This will work with: +} + +unit IdFTPListParseStratusVOS; + +{ + FTP server (FTP 1.0 for Stratus STCP) + FTP server (OS TCP/IP) +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdStratusVOSFTPListItem = class(TIdFTPListItem) + protected + FAccess : String; + FNumberBlocks : Integer; + FBlockSize : Integer; + FFileFormat : String; + FLinkedItemName : string; + public + property Access : String read FAccess write FAccess; + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + property BlockSize : Integer read FBlockSize write FBlockSize; + property FileFormat : String read FFileFormat write FFileFormat; + //This results will look odd for symbolic links + //Don't panic!!! + // + //Stratus VOS has an unusual path syntax such as: + // + //%phx_cac#m2_user>Stratus>Charles_Spitzer>junque>_edit.vterm1.1 + // + //where the > is a path separator + property LinkedItemName : string read FLinkedItemName write FLinkedItemName; + end; + + TIdFTPLPStratusVOS = class(TIdFTPListBase) + protected + class function IsValidFileEntry(const ALine : String) : Boolean; + class function IsValidDirEntry(const ALine : String): Boolean; + class function IsFilesHeader(const ALine : String): Boolean; + class function IsDirsHeader(const ALine : String): Boolean; + class function IsLinksHeader(const ALine : String): Boolean; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseDirEntry(const AItem: TIdFTPListItem): Boolean; + class function ParseFileEntry(const AItem : TIdFTPListItem): Boolean; + class function ParseLinkEntry(const AItem : TIdFTPListItem): Boolean; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseStratusVOS"'} + {$ENDIF} + +implementation + + { +From: Manual Name: VOS Reference Manual + +Part Number: R002 + +Revision Number: 01 + +Printing Date: April 1990 + +Stratus Computer, Inc. + + +Path Names + +The most important function of the directory hierarchy is to provide +a way to uniquely but conveniently name any object in the I/O +system. Any user on any processing module or system that can +communicate with the module containing the object can then refer to +the object. + +The unique name of an object is derived from the object's unique +path in the I/O system. The unique name is called the path name of +the object. A path name is constructed from the name of the object, +the names of the directories in the path leading to the object, and +the name of the system containing the root parent directory. + +The path name of a file or directory is a combination of the +following names: + +1. the name of the system containing the object preceded by a +percent sign (%). +2. the name of the disk containing the object preceded by a number +sign (#) +3. the names of the directories in the path of the object, in order, each preceded by the greater-than sign (>) + +4. the name of the object preceded by the greater-than sign (>). + +The symbol > is used to separate directories and files in the path +name. Its use is similar to the use of / or \ in other operating +systems. + +For example, suppose you have a system named %s containing a disk +named #d01. (The module containing the disk is %s#m1.) The following +is an example of a full path name for the file named this_week. + +%s#d01>Administration>Jones>reports>this_week + + +The file is immediately contained in the directory reports, which is +subordinate to the directory Jones. The home directory Jones is a +subdirectory of the group directory Administration which is a +subdirectory of the disk #d01. +Relative Path Names + +The path names defined so far are full path names. The full path +name of an object is unique because the path of an object is unique. +The operating system can also interpret relative path names. A +relative path name is a combination of object names and pecial +symbols, like a full path name, that identifies an object in the +directory hierarchy. A relative path name of the object generally +does not contain all the directory names that are in the full path +name. When you use a relative path name, the operating system +determines the missing information about the object's location rom +the location of the current directory. + +If the operating system reads a string that it expects to be a path +name and the leading character is not a percent sign, it interprets +the string as a relative path name. + +The single character < can be used to refer to the parent directory +of the current directory. For example, the command +change_current_dir < moves you up one directory in the directory +hierarchy. A single period (.) also refers to the current directory +and two periods (..) refers to the parent directory. Thus, +change_current_dir .. is the same as the change_current_dir <. + } + +uses + IdFTPCommon, IdGlobal, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPStratusVOS } + +class function TIdFTPLPStratusVOS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + i : Integer; + LMode : TIdDirItemType; +begin + Result := False; + LMode := ditFile; + for i := 0 to AListing.Count - 1 do + begin + if AListing[i] <> '' then + begin + if IsFilesHeader(AListing[i]) then begin + LMode := ditFile; + end + else if IsDirsHeader(AListing[i]) then begin + LMode := ditDirectory; + end + else if IsLinksHeader(AListing[i]) then begin + LMode := ditSymbolicLink; + end else + begin + case LMode of + ditFile : + begin + if not IsValidFileEntry(AListing[i]) then begin + Exit; + end; + end; + ditDirectory : + begin + if not IsValidDirEntry(AListing[i]) then begin + Exit; + end; + end; + end; + end; + end; + end; + Result := True; +end; + +class function TIdFTPLPStratusVOS.GetIdent: String; +begin + Result := 'Stratus VOS'; {do not localize} +end; + +class function TIdFTPLPStratusVOS.IsDirsHeader(const ALine: String): Boolean; +begin + { Dirs: 0 } + Result := TextStartsWith(ALine, 'Dirs: '); {do not localize} +end; + +class function TIdFTPLPStratusVOS.IsFilesHeader(const ALine: String): Boolean; +begin + { Files: 4 Blocks: 609 } + Result := TextStartsWith(ALine, 'Files: ') and (IndyPos('Blocks: ', ALine) > 8); {do not localize} +end; + +class function TIdFTPLPStratusVOS.IsLinksHeader(const ALine: String): Boolean; +begin + { Links: 0 } + Result := TextStartsWith(ALine, 'Links: '); {do not localize} +end; + +class function TIdFTPLPStratusVOS.IsValidDirEntry(const ALine: String): Boolean; +var + s, s2 : String; +begin + Result := False; + s := ALine; + //a listing may start of with one space + //permissions + if TextStartsWith(s, ' ') then begin {do not localize} + IdDelete(s, 1, 1); + end; + if Length(Fetch(s)) <> 1 then begin + Exit; + end; + s := TrimLeft(s); + //block count + if not IsNumeric(Fetch(s)) then begin + Exit; + end; + s := TrimLeft(s); + s2 := Fetch(s); + //date + if not IsYYYYMMDD(s2) then begin + Exit; + end; + s := TrimLeft(s); + s2 := Fetch(s); + //time + Result := IsHHMMSS(s2, ':'); {do not localize} +end; + +class function TIdFTPLPStratusVOS.IsValidFileEntry(const ALine: String): Boolean; +var + s, s2 : String; +begin + Result := False; + s := ALine; + //a listing may start of with one space + if TextStartsWith(s, ' ') then begin {do not localize} + IdDelete(s, 1, 1); + end; + if Length(Fetch(s)) <> 1 then begin + Exit; + end; + s := TrimLeft(s); + if not IsNumeric(Fetch(s)) then begin + Exit; + end; + s := TrimLeft(s); + s2 := Fetch(s); + if not IsNumeric(s2, 2) then + begin + s := TrimLeft(s); + s2 := Fetch(s); + end; + if not IsYYYYMMDD(s2) then begin + Exit; + end; + s := TrimLeft(s); + s2 := Fetch(s); + Result := IsHHMMSS(s2, ':'); {do not localize} +end; + +class function TIdFTPLPStratusVOS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdStratusVOSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPStratusVOS.ParseDirEntry(const AItem: TIdFTPListItem): Boolean; +var + LV : TIdStratusVOSFTPListItem; + LBuf, LPart : String; +begin +//w 158 stm 90-05-19 11:53:44 acctng.cobol +{ +Files +Access Access Description +Right Code +-------------------------------- +undefined u Denies the user all access to the file. This code + occurs only if the effective access list for the + file does not contain any entry applicable to the + given user name. + +nul n Denies the user all access to the file. + +execute e Allows the user to execute a program module or + command macro, but not to read, modify, or delete + it. + +read r Allows the user to read the file (or to execute + it, if it is executable), but not to modify or + delete it. + +write w Gives the user full access to the contents of + the file. (However, to delete or write to the + file, the user must have modify access to the + directory in which the file is contained.) + + +Directory +Access Access Description +Right Code +-------------------------------- +undefined u Denies the user all access to the directory. + This code occurs only if the effective access + list for the directory does not contain any + entry applicable to the given user name. + +nul n Denies the user all access to the directory. + +status s Allows the user to list the contents of the + directory and to see other status information, + but not to change any of the contents. + +modify m Gives the user full access to the contents of + the directory. + } + Result := False; + LV := AItem as TIdStratusVOSFTPListItem; + LBuf := AItem.Data; + if TextStartsWith(LBuf, ' ') then begin {do not localize} + IdDelete(LBuf, 1, 1); + end; + LV.FAccess := Fetch(LBuf); + if Length(LV.FAccess) <> 1 then + begin + //invalid + LV.FAccess := ''; + Exit; + end; + LBuf := TrimLeft(LBuf); + //block count + LPart := Fetch(LBuf); + if not IsNumeric(LPart) then begin + Exit; + end; + LV.NumberBlocks := IndyStrToInt(LPart, 0); + //size + LV.Size := (LV.NumberBlocks * 4096); + LV.SizeAvail := True; + //Note that will NOT be accurate but it's the best you can do. + //date + LBuf := TrimLeft(LBuf); + LPart := Fetch(LBuf); + if not IsYYYYMMDD(LPart) then begin + Exit; + end; + LV.ModifiedDate := DateYYMMDD(LPart); + //time + LBuf := TrimLeft(LBuf); + LPart := Fetch(LBuf); + if not IsHHMMSS(LPart, ':') then begin {do not localize} + Exit; + end; + LV.ModifiedDate := LV.ModifiedDate + TimeHHMMSS(LPart); + LBuf := TrimLeft(LBuf); + LV.FileName := LBuf; + Result := True; +end; + +class function TIdFTPLPStratusVOS.ParseFileEntry(const AItem: TIdFTPListItem): Boolean; +var + LV : TIdStratusVOSFTPListItem; + LBuf, LPart : String; +begin +//w 158 stm 90-05-19 11:53:44 acctng.cobol +{ +Files +Access Access Description +Right Code +-------------------------------- +undefined u Denies the user all access to the file. This code + occurs only if the effective access list for the + file does not contain any entry applicable to the + given user name. + +nul n Denies the user all access to the file. + +execute e Allows the user to execute a program module or + command macro, but not to read, modify, or delete + it. + +read r Allows the user to read the file (or to execute + it, if it is executable), but not to modify or + delete it. + +write w Gives the user full access to the contents of + the file. (However, to delete or write to the + file, the user must have modify access to the + directory in which the file is contained.) + + +Directory +Access Access Description +Right Code +-------------------------------- +undefined u Denies the user all access to the directory. + This code occurs only if the effective access + list for the directory does not contain any + entry applicable to the given user name. + +nul n Denies the user all access to the directory. + +status s Allows the user to list the contents of the + directory and to see other status information, + but not to change any of the contents. + +modify m Gives the user full access to the contents of + the directory. + } + Result := False; + LV := AItem as TIdStratusVOSFTPListItem; + LBuf := AItem.Data; + if TextStartsWith(LBuf, ' ') then begin {do not localize} + IdDelete(LBuf, 1, 1); + end; + LV.FAccess := Fetch(LBuf); + LV.PermissionDisplay := LV.Access; + if Length(LV.FAccess) <> 1 then + begin + //invalid + LV.FAccess := ''; + Exit; + end; + LBuf := TrimLeft(LBuf); + //block count + LPart := Fetch(LBuf); + if not IsNumeric(LPart) then begin + Exit; + end; + LV.NumberBlocks := IndyStrToInt(LPart, 0); + //file format + LBuf := TrimLeft(LBuf); + LV.FileFormat := Fetch(LBuf); + { +Charlie Spitzer, stratus customer service, made this note in an E-Mail to me: + +not all files can be directly calculated in size. there are different file +types, each of which has a different file calculation. for example, in the +above list, stm means stream, and is directly equal to a unix file. however, +seq stands for sequential, and there is a 4 byte overhead per record, and no +way to determine the number of records from ftp. there are other file types +which you can see, rel (relative) being one of them, and the overhead is 2 +bytes per record, but each record doesn't have to be the same size, and +again there is no way to determine the # of records. + +READ THIS!!! + +In a further correspondance, Charlie Spitzer did note this: + +a block count is the number of 4096 byte blocks allocated to the file. it +contains data blocks + index blocks, if any. there is no way to get a record +count, and if the file is sparse (not all records of the file are written, +since it's possible to write a record not at the beginning of a file), the +block count may be wildly inaccurate. + } + LV.Size := LV.NumberBlocks; + + { +John M. Cassidy, CISSP, euroConex noted in a private E-Mail that the blocksize +is 4096 bytes. + +This will NOT be exact. That's one reason why I don't use file sizes right from +a directory listing when writing FTP programs. + } + LV.Size := LV.NumberBlocks * 4096; + +{ +Otto Newman noted this, Stratus Technologies noted this: + +Transmit sizes are shown in terms of bytes which are blocks * 4096. +} + LV.SizeAvail := True; + //date + LBuf := TrimLeft(LBuf); + LPart := Fetch(LBuf); + if not IsYYYYMMDD(LPart) then begin + Exit; + end; + LV.ModifiedDate := DateYYMMDD(LPart); + //time + LBuf := TrimLeft(LBuf); + LPart := Fetch(LBuf); + if not IsHHMMSS(LPart, ':') then begin {do not localize} + Exit; + end; + LV.ModifiedDate := LV.ModifiedDate + TimeHHMMSS(LPart); + { From: + + Manual Name: VOS Reference Manual + +Part Number: R002 + +Revision Number: 01 + +Printing Date: April 1990 + +Stratus Computer, Inc. + +55 Fairbanks Blvd. + +Marlboro, Massachusetts 01752 + + 1990 by Stratus Computer, Inc. All rights reserved. + +A name is an ASCII character string that contains no more than 32 characters. The characters must be chosen from the following set of 81 characters: +the upper-case letters +the lower-case letters +the decimal digits +the ASCII national use characters +//@ [ \ ] ^ ` { | close-bracket ~ +" $ + , - . / : _ + } + LBuf := TrimLeft(LBuf); + LV.FileName := LBuf; + Result := True; + //item type can't be determined here, that has to be done in the main parsing procedure +end; + +class function TIdFTPLPStratusVOS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +begin + Result := False; + case AItem.ItemType of + DitFile : Result := ParseFileEntry(AItem); + DitDirectory : Result := ParseDirEntry(AItem); + ditSymbolicLink : Result := ParseLinkEntry(AItem); + end; +end; + +class function TIdFTPLPStratusVOS.ParseLinkEntry(const AItem: TIdFTPListItem): Boolean; +var + LV : TIdStratusVOSFTPListItem; + LBuf, LPart : String; +begin + //04-07-13 21:15:43 backholding_logs -> %descc#m2_d01>l3s>db>lti>in>cp_exception + Result := False; + LV := AItem as TIdStratusVOSFTPListItem; + LBuf := AItem.Data; + //date + LPart := Fetch(LBuf); + if not IsYYYYMMDD(LPart) then begin + Exit; + end; + LV.ModifiedDate := DateYYMMDD(LPart); + //time + LBuf := TrimLeft(LBuf); + + LPart := Fetch(LBuf); + if not IsHHMMSS(LPart, ':') then begin {do not localize} + Exit; + end; + LV.ModifiedDate := LV.ModifiedDate + TimeHHMMSS(LPart); + //name + LBuf := TrimLeft(LBuf); + LV.FileName := TrimRight(Fetch(LBuf, '->')); {do not localize} + //link to + LBuf := TrimLeft(LBuf); + LV.LinkedItemName := Trim(LBuf); + //size + LV.SizeAvail := False; + Result := True; +end; + +class function TIdFTPLPStratusVOS.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + LDit : TIdDirItemType; //for tracking state + LItem : TIdFTPListItem; + i : Integer; + LIsContinuedLine : Boolean; + LLine, LPart, LBuf : String; +begin + Result := False; + LDit := ditFile; + LIsContinuedLine := False; + for i := 0 to AListing.Count -1 do + begin + LBuf := AListing[i]; + if LBuf <> '' then + begin + if IsFilesHeader(LBuf) then begin + LDit := ditFile; + end + else if IsDirsHeader(LBuf) then begin + LDit := ditDirectory; + end + else if IsLinksHeader(LBuf) then begin + LDit := ditSymbolicLink; + end + else if LDit <> ditSymbolicLink then + begin + LItem := MakeNewItem(ADir); + LItem.ItemType := LDit; + LItem.Data := LBuf; + if not ParseLine(LItem) then begin + FreeAndNil(LItem); + Exit; + end; + end + else if not LIsContinuedLine then + begin + LLine := TrimRight(LBuf); + if TextEndsWith(LLine, '->') then begin {do not localize} + LIsContinuedLine := True; + end else + begin + LItem := MakeNewItem(ADir); + LItem.ItemType := LDit; + LItem.Data := LLine; + if not ParseLine(LItem) then begin + FreeAndNil(LItem); + Exit; + end; + end; + end else + begin + LPart := LBuf; + if TextStartsWith(LPart, '+') then begin + IdDelete(LPart, 1, 1); + end; + LLine := LLine + LPart; + LIsContinuedLine := False; + if i < (AListing.Count-2) then + begin + if TextStartsWith(AListing[i+1], '+') then begin + LIsContinuedLine := True; + end else + begin + LItem := MakeNewItem(ADir); + LItem.ItemType := LDit; + LItem.Data := LLine; + if not ParseLine(LItem) then begin + FreeAndNil(LItem); + Exit; + end; + end; + end else + begin + LItem := MakeNewItem(ADir); + LItem.ItemType := LDit; + LItem.Data := LLine; + if not ParseLine(LItem) then begin + FreeAndNil(LItem); + Exit; + end; + end; + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPStratusVOS); +finalization + UnRegisterFTPListParser(TIdFTPLPStratusVOS); + +end. diff --git a/indy/Protocols/IdFTPListParseSuperTCP.pas b/indy/Protocols/IdFTPListParseSuperTCP.pas new file mode 100644 index 0000000..996c10b --- /dev/null +++ b/indy/Protocols/IdFTPListParseSuperTCP.pas @@ -0,0 +1,290 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 11/29/2004 11:26:00 PM JPMugaas + This should now support SuperTCP 7.1 running under Windows 2000. That does + support long filenames by the dir entry ending with one space followed by the + long-file name. + ShortFileName was added to the listitem class for completeness. + + Rev 1.0 11/29/2004 2:44:16 AM JPMugaas + New FTP list parsers for some legacy FTP servers. +} + +unit IdFTPListParseSuperTCP; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdSuperTCPFTPListItem = class(TIdFTPListItem) + protected + FShortFileName : String; + public + property ShortFileName : String read FShortFileName write FShortFileName; + end; + + TIdFTPLPSuperTCP = class(TIdFTPListBase) + protected + class function IsValidWin32FileName(const AFileName : String): Boolean; + class function IsValidMSDOSFileName(const AFileName : String): Boolean; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseSuperTCP"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ TIdFTPLPSuperTCP } + +class function TIdFTPLPSuperTCP.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + i : Integer; + LBuf, LBuf2 : String; +begin + { + Maybe like this: + + CMT 11-21-94 10:17 + DESIGN1.DOC 11264 05-11-95 14:20 + + or this: + + CMT 11/21/94 10:17 + DESIGN1.DOC 11264 05/11/95 14:20 + + or this with SuperTCP 7.1 running under Windows 2000: + + . 11-29-2004 22:04 . + .. 11-29-2004 22:04 .. + wrar341.exe 1164112 11-22-2004 15:34 wrar341.exe + test 11-29-2004 22:14 test + TESTDI~1 11-29-2004 22:16 Test Dir + TEST~1 11-29-2004 22:52 Test + } + Result := False; + for i := 0 to AListing.Count-1 do + begin + LBuf := AListing[i]; + //filename and extension - we assume an 8.3 filename type because + //Windows 3.1 only supports that. + Result := IsValidMSDOSFileName(Fetch(LBuf)); + if not Result then begin + Exit; + end; + LBuf := TrimLeft(LBuf); + // or file size + LBuf2 := Fetch(LBuf); + Result := (LBuf2 = '') or IsNumeric(LBuf2); {Do not localize} + if not Result then begin + Exit; + end; + //date + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsMMDDYY(LBuf2, '/') or IsMMDDYY(LBuf2, '-'); {Do not localize} + if Result then + begin + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsHHMMSS(LBuf2, ':'); {Do not localize} + end; + if Result then + begin + //long filename in Win32 + //if nothing, a Windows 3.1 server probably + if LBuf <> '' then begin + Result := IsValidWin32FileName(LBuf); + end; + end; + if not Result then begin + Break; + end; + end; +end; + +class function TIdFTPLPSuperTCP.GetIdent: String; +begin + Result := 'SuperTCP'; {Do not localize} +end; + +class function TIdFTPLPSuperTCP.IsValidMSDOSFileName(const AFileName: String): Boolean; +const + VALID_DOS_CHARS = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrtstuvwxyz0123456789_$~!#%&-{}()@'''+Char(180); {Do not localize} +var + LFileName, LExt : String; + i : Integer; +begin + Result := False; + if (AFileName = CUR_DIR) or (AFileName = PARENT_DIR) then + begin + Result := True; + Exit; + end; + LExt := AFileName; + LFileName := Fetch(LExt, '.'); {Do not localize} + if (Length(LFileName) > 0) and (Length(LFileName) < 9) then + begin + for i := 1 to Length(LFileName) do + begin + if IndyPos(LFileName[i], VALID_DOS_CHARS) = 0 then begin + Exit; + end; + end; + for i := 1 to Length(LExt) do + begin + if IndyPos(LExt[i], VALID_DOS_CHARS) = 0 then begin + Exit; + end; + end; + Result := True; + end; +end; + +class function TIdFTPLPSuperTCP.IsValidWin32FileName(const AFileName: String): Boolean; +//from: http://linux-ntfs.sourceforge.net/ntfs/concepts/filename_namespace.html +const + WIN32_INVALID_CHARS = '"*/:<>?\|' + #0; {Do not localize} + WIN32_INVALID_LAST = ' .'; //not permitted as the last character in Win32 {Do not localize} +var + i : Integer; +begin + Result := False; + if (AFileName = CUR_DIR) or (AFileName = PARENT_DIR) then + begin + Result := True; + Exit; + end; + if Length(AFileName) > 0 then + begin + if IndyPos(AFileName[Length(AFileName)], WIN32_INVALID_LAST) > 0 then begin + Exit; + end; + for i := 1 to Length(AFileName) do + begin + if IndyPos(AFileName[i], WIN32_INVALID_CHARS) > 0 then begin + Exit; + end; + end; + Result := True; + end; +end; + +class function TIdFTPLPSuperTCP.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdSuperTCPFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPSuperTCP.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LI : TIdSuperTCPFTPListItem; + LBuf, LBuf2 : String; +begin + { + with SuperTCP 7.1 running under Windows 2000: + + . 11-29-2004 22:04 . + .. 11-29-2004 22:04 .. + wrar341.exe 1164112 11-22-2004 15:34 wrar341.exe + test 11-29-2004 22:14 test + TESTDI~1 11-29-2004 22:16 Test Dir + TEST~1 11-29-2004 22:52 Test + } + LI := AItem as TIdSuperTCPFTPListItem; + LBuf := AItem.Data; + //short filename and extension - we assume an 8.3 filename + //type because Windows 3.1 only supports that and under Win32, + //a short-filename is returned here. That's with my testing. + LBuf2 := Fetch(LBuf); + LI.FileName := LBuf2; + LI.ShortFileName := LBuf2; + LBuf := TrimLeft(LBuf); + // or file size + LBuf2 := Fetch(LBuf); + if LBuf2 = '' then {Do not localize} + begin + LI.ItemType := ditDirectory; + LI.SizeAvail := False; + end else + begin + LI.ItemType := ditFile; + Result := IsNumeric(LBuf2); + if not Result then begin + Exit; + end; + LI.Size := IndyStrToInt64(LBuf2, 0); + end; + //date + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + if IsMMDDYY(LBuf2, '/') or IsMMDDYY(LBuf2, '-') then begin {Do not localize} + LI.ModifiedDate := DateMMDDYY(LBuf2); + end else + begin + Result := False; + Exit; + end; + //time + LBuf := TrimLeft(LBuf); + LBuf2 := Fetch(LBuf); + Result := IsHHMMSS(LBuf2, ':'); {do not localize} + if Result then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LBuf2); + end; + // long filename + //We do not use TrimLeft here because a space can start a filename in Windows + //2000 and the entry would be like this: + // + //TESTDI~1 11-29-2004 22:16 Test Dir + //TEST~1 11-29-2004 22:52 Test + // + if LBuf <> '' then begin + LI.FileName := LBuf; + end; +end; + +initialization + RegisterFTPListParser(TIdFTPLPSuperTCP); +finalization + UnRegisterFTPListParser(TIdFTPLPSuperTCP); + +end. diff --git a/indy/Protocols/IdFTPListParseTOPS20.pas b/indy/Protocols/IdFTPListParseTOPS20.pas new file mode 100644 index 0000000..8003c16 --- /dev/null +++ b/indy/Protocols/IdFTPListParseTOPS20.pas @@ -0,0 +1,325 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.13 2/5/2005 12:33:46 PM JPMugaas + A bug was causing a false positive for TOPS20 for a Windows NT directory + listing only containing one folder. + + Rev 1.12 2/3/2005 11:26:50 PM JPMugaas + Fix for DotNET problem. + + Rev 1.11 12/8/2004 8:35:18 AM JPMugaas + Minor class restructure to support Unisys ClearPath. + + Rev 1.10 11/30/2004 12:14:46 PM JPMugaas + Compiler error in DotNET. + + Rev 1.9 11/26/2004 3:14:14 PM JPMugaas + TOPS20 parser was causing a false positive with a WindowsNT machine. The fix + is detect a space in a file listing (that should not happen with one form of + the TOPS20 listing). + + Rev 1.8 11/22/2004 7:43:46 PM JPMugaas + Changed LocalFile property for directories to drop the .DIRECTORY extension + and build number. You don't use those with a CD command. + + Rev 1.7 11/20/2004 2:39:02 PM JPMugaas + Now works at twenex.org. That system is odd because it doesn't support the + SYST command so you have to parse the directory. + + Rev 1.6 10/26/2004 9:55:58 PM JPMugaas + Updated refs. + + Rev 1.5 6/5/2004 7:48:56 PM JPMugaas + In TOPS32, a FTP dir listing will often not contain dates or times. It's + usually just the name. + + Rev 1.4 4/19/2004 5:05:44 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:26 PM czhower + Name changes + + Rev 1.2 10/19/2003 3:36:22 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:04:18 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 05:49:58 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseTOPS20; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdTOPS20FTPListItem = class(TIdCreationDateFTPListItem); + + TIdFTPLPTOPS20 = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + +const + TOPS20_VOLPATH_SEP = ':<'; {do not localize} + TOPS20_DIRFILE_SEP = '>'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseTOPS20"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + + +{ TIdFTPLPTOPS20 } + +class function TIdFTPLPTOPS20.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : String; + LParts : TStrings; + i : Integer; +begin + s := ASysDescript; + s := Fetch(s); + Result := (s = 'TOPS20'); {do not localize} + if not Result then + begin + //one server doesn't give a SYST reply at all + //LOGIN.CMD.1;P777700;A,1,15-Aug-2003 09:42:12,15-Aug-2003 09:42:12,16-Nov-1858 16:00:00 + if AListing.Count >0 then + begin + LParts := TStringList.Create; + try + SplitDelimitedString(AListing[0], LParts, False, ','); {do not localize} + if LParts.Count > 0 then + begin + if PatternsInStr(';', LParts[0]) = 2 then {do not localize} + begin + if LParts.Count > 3 then + begin + s := LParts[2]; + + Result := IsDDMonthYY(Fetch(s), '-'); {do not localize} + if Result then begin + Result := IsHHMMSS(s, ':'); {do not localize} + end; + s := LParts[3]; + + Result := IsDDMonthYY(Fetch(s), '-'); {do not localize} + if Result then begin + Result := IsHHMMSS(s, ':'); {do not localize} + end; + end; + end; + { + maybe pattern like this: + TOPS20: + LOGIN.CMD.1 + MSGS.TXT.1 + } + s := AListing[0]; + if IndyPos(TOPS20_VOLPATH_SEP, s) >0 then + begin + if IndyPos(TOPS20_VOLPATH_SEP, s) < IndyPos(TOPS20_DIRFILE_SEP, s) then + begin + Result := True; + for i := 1 to AListing.Count-1 do + begin + LParts.Clear; + Result := IndyPos(' ', AListing[i]) = 0; {do not localize} + if Result then + begin + SplitDelimitedString(AListing[i], LParts, False, '.'); {do not localize} + if LParts.Count = 3 then begin + Result := IsNumeric(LParts[2]); + end; + end; + if not Result then begin + Break; + end; + end; + end; + end; + end; + finally + FreeAndNil(LParts); + end; + end; + end; +end; + +class function TIdFTPLPTOPS20.GetIdent: String; +begin + Result := 'TOPS20'; {do not localize} +end; + +class function TIdFTPLPTOPS20.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdTOPS20FTPListItem.Create(AOwner); +end; + +class function TIdFTPLPTOPS20.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LI : TIdTOPS20FTPListItem; + +{ +Notes from the FTP Server greeting at toad.xkl.com + +230- Welcome! You are logged in to a Tops-20 system, probably not familiar +230- to you. We therefore offer this short note on directory and file naming +230- conventions: +230- +230- A file name consists of 2 required parts, and several optional parts, of +230- which 3 are important to you as an FTP user. These 5 parts together are +230- +230- device:filename.filetype.generation +230- +230- where the punctuation is required. The DEVICE:, , and GENERATION +230- fields are optional, defaulting to current device and directory and latest +230- generation of the file. File names are NOT in general case-sensitive. +230- +230- may have subparts, separated by dots. All the following are +230- syntactically valid directory specifications (though they may not exist on +230- this particular system): +230- +230- or or or +230- +230- GENERATION is numeric; it may take the special values 0 (latest generation), +230- -1 (new, next higher generation), -2 (oldest generation), and -3 (wildcard +230- for all generations), as well as specific numeric generations. +230- +230- DEVICE: usually represents the name of a file system. +230- +230- Wildcards are specified as * (match 0 or more characters) and % (match 1 +230- single character). To obtain all the command files in a directory, you +230- would ask for the retrieval of +230- +230- *.CMD.* +230- +230- To obtain the latest version of all the files with a 1-character FILETYPE, +230- you would request +230- +230- *.%.0 +} + function StripBuild(const AFileName : String): String; + var + LPos : Integer; + begin + LPos := RPos('.', AFileName, -1); {do not localize} + if LPos = 0 then begin + Result := AFileName; + end else begin + Result := Copy(AFileName, 1, LPos-1); + end; + end; + +begin + LI := AItem as TIdTOPS20FTPListItem; + LBuf := AItem.Data; + if (IndyPos(TOPS20_VOLPATH_SEP, LBuf) > 0) and (IndyPos(TOPS20_DIRFILE_SEP, LBuf) = Length(LBuf)) then + begin + //Tape and subdir should work for CD + //Note this is probably something like a "CD ." on other systems. + //From what I saw at one server, they had to give a list including + //subdirectories because the server never returned those. + AItem.FileName := LBuf; + //You can tree this like a directory. It contains the device so it might + //look weird. + AItem.ItemType := ditDirectory; + //strip off device in and path suffix > + Fetch(LBuf, TOPS20_VOLPATH_SEP); + LBuf := Fetch(LBuf, TOPS20_DIRFILE_SEP); + AItem.LocalFileName := LowerCase(Fetch(LBuf, '.')); + AItem.SizeAvail := False; + AItem.ModifiedAvail := False; + Result := True; + Exit; + end; + if TextStartsWith(LBuf, '<') then {do not localize} + begin + //we may be dealing with a data format such as this: + // + //INSTALL.MEM.1;P775252;A,210,10-Apr-1990 13:17:41,10-Apr-1990 13:18:26,11-Jan-2003 11:34:26 + AItem.FileName := Fetch(LBuf, ';'); {do not localize} + //P775252; + Fetch(LBuf, ';'); {do not localize} + //A, + Fetch(LBuf, ','); {do not localize} + //210, + Fetch(LBuf, ','); {do not localize} + //Creation Date - date - I think + LI.CreationDate := DateDDStrMonthYY(Fetch(LBuf)); + //creation date - time + LI.CreationDate := LI.CreationDate + TimeHHMMSS(Trim(Fetch(LBuf, ','))); {do not localize} + //Last modified - date + AItem.ModifiedDate := DateDDStrMonthYY(Fetch(LBuf)); + //Last modified - time + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(Trim(LBuf)); + //strip off path information and build no for file + LBuf := LowerCase(AItem.FileName); + Fetch(LBuf, TOPS20_DIRFILE_SEP); + if IndyPos('.DIRECTORY.', LBuf) > 0 then {do not localize} + begin + AItem.ItemType := ditDirectory; + AItem.LocalFileName := Fetch(LBuf, '.'); {do not localize} + end else begin + AItem.LocalFileName := StripBuild(LBuf); + end; + end else + begin + //That's right - it only returned the file name, no dates, no size, nothing else + AItem.FileName := LBuf; + AItem.LocalFileName := LowerCase(StripBuild(LBuf)); + AItem.ModifiedAvail := False; + AItem.SizeAvail := False; + if IndyPos('.DIRECTORY.', LBuf) > 0 then begin {do not localize} + AItem.ItemType := ditDirectory; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPTOPS20); +finalization + UnRegisterFTPListParser(TIdFTPLPTOPS20); + +end. diff --git a/indy/Protocols/IdFTPListParseTSXPlus.pas b/indy/Protocols/IdFTPListParseTSXPlus.pas new file mode 100644 index 0000000..19eb8b6 --- /dev/null +++ b/indy/Protocols/IdFTPListParseTSXPlus.pas @@ -0,0 +1,196 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 10/26/2004 9:55:58 PM JPMugaas + Updated refs. + + Rev 1.1 6/11/2004 9:38:48 AM DSiders + Added "Do not Localize" comments. + + Rev 1.0 6/7/2004 7:46:26 PM JPMugaas +} + +unit IdFTPListParseTSXPlus; + +{ + FTP List parser for TSX+. This is based on: + http://www.gweep.net/~shifty/music/miragehack/gcc/xasm/cug292.lst +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdTSXPlusFTPListItem = class(TIdMinimalFTPListItem) + protected + FNumberBlocks : Integer; + public + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + end; + + TIdFTPLPTSXPlus = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseTSXPlus"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobal, SysUtils; + +{ TIdFTPLPTSXPlus } + +class function TIdFTPLPTSXPlus.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + i : Integer; +begin + Result := False; + if AListing.Count > 0 then + begin + for i := AListing.Count-1 downto 0 do + begin + if AListing[i] <> '' then + begin + if IsFooter(AListing[i]) then + begin + Result := True; + Break; + end; + end; + end; + end; +end; + +class function TIdFTPLPTSXPlus.GetIdent: String; +begin + Result := 'TSX+'; {do not localize} +end; + +class function TIdFTPLPTSXPlus.IsFooter(const AData: String): Boolean; +var + LBuf, LPart : String; +begin + //The footer is like this: + //Directory [du3:/cug292/pcdsk4/*.*] / 9 Files / 563 Blocks + Result := False; + LBuf := AData; + LPart := Fetch(LBuf, '['); {do not localize} + if LBuf = '' then begin + Exit; + end; + LPart := TrimRight(LPart); + if LPart = 'Directory' then {do not localize} + begin + Fetch(LBuf, ']'); {do not localize} + if LBuf = '' then + begin + Exit; + end; + LBuf := TrimLeft(LBuf); + if TextStartsWith(LBuf, '/') then {do not localize} + begin + IdDelete(LBuf, 1, 1); + if IndyPos('Files', LBuf) > 0 then {do not localize} + begin + LPart := Fetch(LPart, '/'); {do not localize} + if LBuf = '' then begin + Exit; + end; + Result := (IndyPos('Block', LBuf) > 0); {do not localize} + end; + end; + end; +end; + +class function TIdFTPLPTSXPlus.IsHeader(const AData: String): Boolean; +begin + Result := False; +end; + +class function TIdFTPLPTSXPlus.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdTSXPlusFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPTSXPlus.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf, LExt : String; + LNewItem : TIdFTPListItem; +begin + { + Note that this parser is odd because it will create a new TIdFTPListItem. + I know that is not according to the current conventional design. However, KA9Q + is unusual because a single line can have two items (maybe more) + } + Result := True; + LBuf := TrimLeft(AItem.Data); + AItem.FileName := Fetch(LBuf, '.'); {do not localize} + LExt := Fetch(LBuf); + if LExt = 'dsk' then begin {do not localize} + AItem.ItemType := ditDirectory; + end else + begin + AItem.ItemType := ditFile; + AItem.FileName := AItem.FileName + '.' + LExt; {do not localize} + end; + LBuf := TrimLeft(LBuf); + //block count + (AItem as TIdTSXPlusFTPListItem).NumberBlocks := IndyStrToInt(Fetch(LBuf), 0); + LBuf := TrimRight(LBuf); + if LBuf <> '' then + begin + LNewItem := MakeNewItem(AItem.Collection as TIdFTPListItems); + LNewItem.Data := LBuf; + Result := ParseLine(LNewItem, APath); + if not Result then + begin + FreeAndNil(LNewItem); + Exit; + end; + LNewItem.Data := AItem.Data; + end; +end; + +initialization + RegisterFTPListParser(TIdFTPLPTSXPlus); +finalization + UnRegisterFTPListParser(TIdFTPLPTSXPlus); + +end. diff --git a/indy/Protocols/IdFTPListParseTandemGuardian.pas b/indy/Protocols/IdFTPListParseTandemGuardian.pas new file mode 100644 index 0000000..e550b3b --- /dev/null +++ b/indy/Protocols/IdFTPListParseTandemGuardian.pas @@ -0,0 +1,270 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2/23/2005 6:34:30 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.1 10/26/2004 11:21:16 PM JPMugaas + Updated refs. + + Rev 1.0 7/30/2004 8:03:42 AM JPMugaas + FTP List parser for the Tandem NonStop Guardian file-system. +} + +unit IdFTPListParseTandemGuardian; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ + This parser is based on the Tandem NonStop Server with a Guardian file system. + This is primarily based on some correspondances and samples I got from Steeve Howe. + His correspondance is here and I'm noting it in case I need to refer back later: + + What are the rules for acceptable filenames on the Tandem? + >>Must start with a character and must be at least on character long. + + What charactors can be used? + >>Alpha characters and numerals. + + What's the length? + >> 8 characters max. + + Can you have file extensions, if so how many and what's the length for + those? + >>No file extensions. + + Is the system case insensitive or case-sensitive? + >>All filenames are converted to uppercase (from what I can tell) + + What's Code? Is it always a number? + >>Code is the type of file. 101 is an editable file, 100 is an + exacutable, 1000 is an user defined executable, there is a type 1600 + and I have seen type 0 (which I think is an unknown binary type of + file) + + What's EOF? Is that the file size? I don't know. + >>Yes, That is the file size. + + In the Owner column, you have something like this "155, 76". Are + their only numbers and what do the numbers in the column mean (I + assume that there is two). Will the Owner column ever have letters? + >>Never letters. + first byte is group, second is user + it describes user and security level + + What is valid for "RWEP" and what do the letters in that mean and what + letters are there? + >>Read, Write, Execute, Purge + + some valid letters (there are about 7 and I don't know them all): + N - anyone across system has access + U - only the user has this priviledge + A - anyone on local system has priviledge + G - anyone belonging to same group + - (dash) - only local super.super has access + + some further references from Tandem that might help: + + http://www.hp.com/go/NTL - General technical reference + http://h30163.www3.hp.com/NTL/library/G06_RVUs/G06_20/Publications/ -HP + G06.20 Publications + + hope this helps! +} +{ + This parses something like this: + ==== + File Code EOF Last Modification Owner RWEP + ALV 101 2522 27-Aug-02 13:57:10 155,106 "nnnn" + ALVO 1000 2048 27-Aug-02 13:57:22 155,106 "nunu" + ==== +} + +type + TIdTandemGuardianFTPListItem = class(TIdOwnerFTPListItem) + protected + //this is given as a numbeer like the owner. We keep it as a string + //for consistancy with other FTP list parsers. + FGroupName : String; + //this may be an integer value but I'm not sure + //because one + FCode : UInt32; + //This is the RWEP value + { It's done like this: + + Read, Write, Execute, Purge + + some valid letters (there are about 7 and I don't know them all): + N - anyone across system has access + U - only the user has this priviledge + A - anyone on local system has priviledge + G - anyone belonging to same group + - (dash) - only local super.super has access + + } + FPermissions : String; + public + property GroupName : String read FGroupName write FGroupName; + property Code : UInt32 read FCode write FCode; + property Permissions : String read FPermissions write FPermissions; + end; + + TIdFTPLPTandemGuardian = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + +const + TANDEM_GUARDIAN_ID = 'Tandem NonStop Guardian'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseTandemGuardian"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPTandemGuardian } + +class function TIdFTPLPTandemGuardian.GetIdent: String; +begin + Result := TANDEM_GUARDIAN_ID; +end; + +class function TIdFTPLPTandemGuardian.IsHeader(const AData: String): Boolean; +var + LCols : TStrings; +begin + Result := False; + LCols := TStringList.Create; + try + SplitDelimitedString(AData, LCols, True); + if LCols.Count = 7 then + begin + Result := (LCols[0] = 'File') and {do not localize} + (LCols[1] = 'Code') and {do not localize} + (LCols[2] = 'EOF') and {do not localize} + (LCols[3] = 'Last') and {do not localize} + (LCols[4] = 'Modification') and {do not localize} + (LCols[5] = 'Owner') and {do not localize} + (LCols[6] = 'RWEP') {do not localize} + end; + finally + FreeAndNil(LCols); + end; +end; + +class function TIdFTPLPTandemGuardian.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdTandemGuardianFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPTandemGuardian.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LItem : TIdTandemGuardianFTPListItem; + LLine, LBuffer : String; + LDay, LMonth, LYear : Integer; +begin + { + Parse lines like these: + + ALV 101 2522 27-Aug-02 13:57:10 155,106 "nnnn" + ALVO 1000 2048 27-Aug-02 13:57:22 155,106 "nunu" + } + // + { Note from Steeve Howe: + === +The directories are flat. The Guardian system appears like this: + +\.$. + +you can change from server to server, volume to volume, subvolume to +subvolume, but +nothing deeper. what you get from a directory listing then is just the +files within that +subvolume of the volume on the server. +=== + } + Result := True; + LItem := AItem as TIdTandemGuardianFTPListItem; + LLine := Trim(LItem.Data); + LItem.ItemType := ditFile; + //name + // Steve Howe advised me that a filename can not have a space and is 8 chars + // with no filename extensions. It is case insensitive. + LItem.FileName := Fetch(LLine); + LLine := TrimLeft(LLine); + //code + LItem.Code := IndyStrToInt(Fetch(LLine), 0); + LLine := TrimLeft(LLine); + //EOF + LItem.Size := IndyStrToInt64(Fetch(LLine), 0); + LLine := TrimLeft(LLine); + //Last Modification + //date + LBuffer := Fetch(LLine); + LLine := TrimLeft(LLine); + LDay := IndyStrToInt(Fetch(LBuffer, '-'), 1); {do not localize} + LMonth := StrToMonth(Fetch(LBuffer, '-')); {do not localize} + + LYear := IndyStrToInt(Fetch(LBuffer), 1989); + LYear := Y2Year(LYear); + //time + LItem.ModifiedDate := EncodeDate(LYear, LMonth, LDay); + LBuffer := Fetch(LLine); + LLine := TrimLeft(LLine); + LItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LBuffer); + LLine := TrimLeft(LLine); + //group,Owner + //Steve how advised me that the first number in this column is a group + //and the number after the comma is an owner + LItem.GroupName := Fetch(LLine, ','); {do not localize} + LLine := TrimLeft(LLine); + LItem.OwnerName := Fetch(LLine); + //RWEP + LItem.Permissions := UnquotedStr(LLine); + LItem.PermissionDisplay := '"' + LItem.Permissions + '"'; +end; + +initialization + RegisterFTPListParser(TIdFTPLPTandemGuardian); +finalization + UnRegisterFTPListParser(TIdFTPLPTandemGuardian); + +end. diff --git a/indy/Protocols/IdFTPListParseUnisysClearPath.pas b/indy/Protocols/IdFTPListParseUnisysClearPath.pas new file mode 100644 index 0000000..a581108 --- /dev/null +++ b/indy/Protocols/IdFTPListParseUnisysClearPath.pas @@ -0,0 +1,239 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 12/8/2004 8:45:02 AM JPMugaas +} + +unit IdFTPListParseUnisysClearPath; + +{ + Unisys ClearPath (MCP and OS/2) + DIRECTORY_FORMAT=NATIVE + + Much of this is based on: + + ClearPath Enterprise Servers + FTP Services for ClearPath + OS 2200 User's Guide + ClearPath OS 2200 Release 8.0 January 2003 + 2003 Unisys Corporation. + All rights reserved. + + and + + ClearPath Enterprise Servers + TCP/IP Distributed Systems Services Operations Guide + ClearPath MCP Release 9.0 April 2004 + 2004 Unisys Corporation. + All rights reserved. + + With a sample showing a multiline response from: + + http://article.gmane.org/gmane.text.xml.cocoon.devel/24912 + + This parses data in this form: + === + Report for: (UC)A ON PACK + A SEQDATA 84 03/08/1998 15:32 + A/B SEQDATA 84 06/09/1998 12:03 + A/B/C SEQDATA 84 06/09/1998 12:03 + A/C SEQDATA 84 06/09/1998 12:03 + A/C/C SEQDATA 84 06/09/1998 12:04 + A/C/C/D SEQDATA 84 06/09/1998 12:04 + 6 Files 504 Octets + === + + The parserm only support DIRECTORY_FORMAT=NATIVE which is the default on that server. + DIRECTORY_FORMAT=STANDARD does not need be supported because that is probably listed + in Unix format. If not, we'll deal with it given some data samples. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdUnisysClearPathFTPListItem = class(TIdCreationDateFTPListItem) + protected + FFileKind : String; + public + property FileKind : String read FFileKind write FFileKind; + end; + + TIdFTPLPUnisysClearPath = class(TIdFTPListBaseHeader) + protected + class function IsContinuedLine(const AData: String): Boolean; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseUnisysClearPath"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; + +{ TIdFTPLPUnisysClearPath } + +class function TIdFTPLPUnisysClearPath.GetIdent: String; +begin + Result := 'Unisys Clearpath'; {Do not localize} +end; + +class function TIdFTPLPUnisysClearPath.IsContinuedLine(const AData: String): Boolean; +begin + Result := TextStartsWith(AData, ' '); {Do not localize} +end; + +class function TIdFTPLPUnisysClearPath.IsFooter(const AData: String): Boolean; +var + s : TStrings; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count = 4 then + begin + if (s[1] = 'Files') or (s[1] = 'File') then begin {Do not localize} + Result := (s[3] = 'Octets') or (s[3] = 'Octet'); {Do not localize} + end; + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPUnisysClearPath.IsHeader(const AData: String): Boolean; +var + s : TStrings; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count > 2 then begin + Result := (s[0] = 'Report') and (s[1] = 'for:'); + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPUnisysClearPath.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdUnisysClearPathFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPUnisysClearPath.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + s : TStrings; + LI : TIdUnisysClearPathFTPListItem; +begin + Result := False; + LI := AItem as TIdUnisysClearPathFTPListItem; + LI.ItemType := ditFile; + s := TStringList.Create; + try + SplitDelimitedString(LI.Data, s, True); + if s.Count > 4 then + begin + LI.FileName := s[0]; + LI.FileKind := s[1]; + //size + if IsNumeric(s[2]) then + begin + LI.Size := IndyStrToInt(s[2], 0); + AItem.SizeAvail := True; + //creation date + if IsMMDDYY(s[3], '/') then {Do not localize} + begin + LI.CreationDate := DateMMDDYY(s[3]); + if IsHHMMSS(s[4], ':') then {Do not localize} + begin + LI.CreationDate := LI.CreationDate + TimeHHMMSS(s[4]); + Result := True; + end; + end; + end; + s.Clear; + //remove path from localFileName + SplitDelimitedString(LI.FileName, s, True, '/'); {Do not localize} + if s.Count > 0 then begin + LI.LocalFileName := s[s.Count-1]; + end else begin + Result := False; + end; + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPUnisysClearPath.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + i : Integer; + LItem : TIdFTPListItem; +begin + for i := 0 to AListing.Count-1 do + begin + if not IsWhiteString(AListing[i]) then + begin + if not (IsHeader(AListing[i]) or IsFooter(AListing[i])) then + begin + if (not IsContinuedLine(AListing[i])) then //needed because some VMS computers return entries with multiple lines + begin + LItem := MakeNewItem(ADir); + LItem.Data := UnfoldLines(AListing[i], i, AListing); + Result := ParseLine(LItem); + if not Result then begin + FreeAndNil(LItem); + Exit; + end; + end; + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPUnisysClearPath); +finalization + UnRegisterFTPListParser(TIdFTPLPUnisysClearPath); + +end. diff --git a/indy/Protocols/IdFTPListParseUnix.pas b/indy/Protocols/IdFTPListParseUnix.pas new file mode 100644 index 0000000..fbb1cec --- /dev/null +++ b/indy/Protocols/IdFTPListParseUnix.pas @@ -0,0 +1,852 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.21 2/23/2005 6:34:28 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.20 10/26/2004 9:56:00 PM JPMugaas + Updated refs. + + Rev 1.19 8/5/2004 11:18:16 AM JPMugaas + Should fix a parsing problem I introeduced that caused errors with Unitree + servers. + + Rev 1.18 8/4/2004 12:40:12 PM JPMugaas + Fix for problem with total line. + + Rev 1.17 7/15/2004 4:02:48 AM JPMugaas + Fix for some FTP servers. In a Unix listing, a : at the end of a filename + was wrongly being interpretted as a subdirectory entry in a recursive + listing. + + Rev 1.16 6/14/2004 12:05:54 AM JPMugaas + Added support for the following Item types that appear in some Unix listings + (particularly a /dev or /tmp dir): + + FIFO, Socket, Character Device, Block Device. + + Rev 1.15 6/13/2004 10:44:06 PM JPMugaas + Fixed a problem with some servers returning additional columns in the owner + and group feilds. Note that they will not be parsed correctly in all cases. + That's life. + + drwx------ 1 BUILTIN NT AUTHORITY 0 Dec 7 2001 + System Volume Information + + Rev 1.14 4/20/2004 4:01:18 PM JPMugaas + Fix for nasty typecasting error. The wrong create was being called. + + Rev 1.13 4/19/2004 5:05:20 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.12 2004.02.03 5:45:18 PM czhower + Name changes + + Rev 1.11 2004.01.23 9:53:32 PM czhower + REmoved unneded check because of CharIsInSet functinoalty. Also was a short + circuit which is not permitted. + + Rev 1.10 1/23/2004 12:49:52 PM SPerry + fixed set problems + + Rev 1.9 1/22/2004 8:29:02 AM JPMugaas + Removed Ansi*. + + Rev 1.8 1/22/2004 7:20:48 AM JPMugaas + System.Delete changed to IdDelete so the code can work in NET. + + Rev 1.7 10/19/2003 3:48:10 PM DSiders + Added localization comments. + + Rev 1.6 9/28/2003 03:02:30 AM JPMugaas + Now can handle a few non-standard date types. + + Rev 1.5 9/3/2003 07:34:40 PM JPMugaas + Parsing for /bin/ls with devices now should work again. + + Rev 1.4 4/7/2003 04:04:26 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.3 4/3/2003 03:37:36 AM JPMugaas + Fixed a bug in the Unix parser causing it not to work properly with Unix BSD + servers using the -T switch. Note that when a -T switch s used on a FreeBSD + server, the server outputs the millaseconds and an extra column giving the + year instead of either the year or time (the regular /bin/ls standard + behavior). + + Rev 1.2 3/3/2003 07:17:58 PM JPMugaas + Now honors the FreeBSD -T flag and parses list output from a program using + it. Minor changes to the File System component. + + Rev 1.1 2/19/2003 05:53:14 PM JPMugaas + Minor restructures to remove duplicate code and save some work with some + formats. The Unix parser had a bug that caused it to give a False positive + for Xercom MicroRTOS. + + Rev 1.0 2/19/2003 02:02:02 AM JPMugaas + Individual parsing objects for the new framework. +} + +unit IdFTPListParseUnix; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ +Notes: + +- The Unitree and Unix parsers are closely tied together and share just +about all of the same code. The reason is that Unitee is very similar to +a Unix dir list except it has an extra column which the Unix line parser +can handle in the Unitree type. + +- The Unix parser can parse MACOS - Peters server (no relationship to this +author :-) ). + +- It is worth noting that the parser does handle /bin/ls -s and -i switches as +well as -g and -o. This is important sometimes as the Unix format comes +from FTP servers that simply piped output from the Unix /bin/ls command. + +- This parser also handles recursive lists which is good for mirroring software. +} + +type + { + Note that for this, I am violating a convention. + The violation is that I am putting parsers for two separate servers + in the same unit. + The reason is this, Unitree has two additional columns (a file family + and a file migration status. The line parsing code is the same because + I thought it was easier to do that way in this case. +} + TIdUnixFTPListItem = class(TIdUnixBaseFTPListItem) + protected + FNumberBlocks : Integer; + FInode : Integer; + public + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + property Inode : Integer read FInode write FInode; + end; + + TIdUnitreeFTPListItem = class(TIdUnixFTPListItem) + protected + FMigrated : Boolean; + FFileFamily : String; + public + property Migrated : Boolean read FMigrated write FMigrated; + property FileFamily : String read FFileFamily write FFileFamily; + end; + + TIdFTPLPUnix = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function InternelChkUnix(const AData : String) : Boolean; virtual; + class function IsUnitree(const AData: string): Boolean; virtual; + class function IsUnitreeBanner(const AData: String): Boolean; virtual; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + TIdFTPLPUnitree = class(TIdFTPLPUnix) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + public + class function GetIdent : String; override; + end; + +const + UNIX = 'Unix'; {do not localize} + UNITREE = 'Unitree'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseUnix"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, + {$IFDEF VCL_6_OR_ABOVE}DateUtils,{$ENDIF} + SysUtils; + +{ TIdFTPLPUnix } + +class function TIdFTPLPUnix.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + i : Integer; +begin + // TODO: return True if ASysDescript starts with 'Unix'? + Result := False; + for i := 0 to AListing.Count - 1 do + begin + if AListing[i] <> '' then begin + //workaround for the XBox MediaCenter FTP Server + //which returns something like this: + // + //dr-xr-xr-x 1 ftp ftp 1 Feb 23 00:00 D: + //and the trailing : is falsely assuming that a ":" means + //a subdirectory entry in a recursive list. + if InternelChkUnix(AListing[i]) then begin + if GetIdent = UNITREE then begin + Result := IsUnitree(AListing[i]); + end else begin + Result := not IsUnitree(AListing[i]); + end; + Break; + end; + if not (IsTotalLine(AListing[i]) or IsSubDirContentsBanner(AListing[i])) then begin + Break; + end; + end; + end; +end; + +class function TIdFTPLPUnix.GetIdent: String; +begin + Result := UNIX; +end; + +class function TIdFTPLPUnix.InternelChkUnix(const AData: String): Boolean; +var + s : TStrings; + LCData : String; +begin + //pos 1 values + // d - dir + // - - file + // l - symbolic link + // b - block device + // c - charactor device + // p - pipe (FIFO) + // s - socket + LCData := UpperCase(AData); + Result := IsValidUnixPerms(AData); + if Result then begin + //Do NOT attempt to do Novell Netware Print Services for Unix FTPD in NFS + //namespace if we have a block device. + if CharIsInSet(LCData, 1, 'CB') then begin + Exit; + end; + //This extra complexity is required to distinguish Unix from + //a Novell Netware server in NFS namespace which is somewhat similar + //to a Unix listing. Beware. + s := TStringList.Create; + try + SplitDelimitedString(LCData, s, True); + if s.Count > 9 then begin + Result := PosInStrArray(s[9], ['AM', 'PM']) = -1; {do not localize} + if Result then begin + // allow localized months longer than 3 characters + Result := not ((IndyPos(':', s[8]) = 0) and (StrToMonth(s[6]) > 0)); {do not localize} + end; + end; + finally + FreeAndNil(s); + end; + end else begin + //we make an additional check for two additional rows before the + //the permissions. These are the inode and block count for the item. + //These are specified with the -i and -s parameters. + s := TStringList.Create; + try + SplitDelimitedString(LCData, s, True); + if s.Count > 3 then begin + if IsNumeric(s[0]) then begin + Result := IsValidUnixPerms(S[1]); + if not Result then begin + Result := IsNumeric(s[1]) and IsValidUnixPerms(S[2]); + end; + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPUnix.IsUnitree(const AData: string): Boolean; +var + s : TStrings; +begin + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + Result := (s.Count > 4) and (PosInStrArray(s[4], UnitreeStoreTypes) <> -1); + if not Result then begin + Result := IsUnitreeBanner(AData); + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPUnix.IsUnitreeBanner(const AData: String): Boolean; +begin + Result := TextStartsWith(AData, '/') and TextEndsWith(AData, ').') and (IndyPos('(', AData) > 0); {do not localize} +end; + +class function TIdFTPLPUnix.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdUnixFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPUnix.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +{Note that we also use this parser for Unitree FTP Servers because that server +is like Unix except that in Unitree, there's two additional columns before the size. + +Those are: + +Storage Type - AR - archived or migrated to tape and DK +File family - +} +type + TParseUnixSteps = (pusINode, pusBlocks, pusPerm, pusCount, pusOwner, pusGroup, + pusSize, pusMonth, pusDay, pusYear, pusTime, pusName, pusDone); +var + LStep: TParseUnixSteps; + LData, LTmp: String; + LInode, LBlocks, LDir, LGPerm, LOPerm, LUPerm, LCount, LOwner, LGroup: String; + LName, LSize, LLinkTo: String; + wYear, wMonth, wDay: Word; + wCurrYear, wCurrMonth, wCurrDay: Word; + // wYear, LCurrentMonth, wMonth, wDay: Word; + wHour, wMin, wSec, wMSec: Word; + ADate: TDateTime; + i: Integer; + LI : TIdUnixFTPListItem; + wDayStr: string; + + function IsGOSwitches(const AString : String) : Boolean; + var + s : TStrings; + begin + //check to see if both the -g and -o switches were used. Both + //owner and group are surpressed in that case. We have to check + //that so our interpretation does not cause an error. + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AString, s, True); + if s.Count > 2 then begin + //if either inode or block count were given + if IsNumeric(s[0]) then begin + s.Delete(0); + end; + //if both inode and block count were given + if IsNumeric(s[0]) then begin + s.Delete(0); + end; + if s.Count > 5 then begin + if StrToMonth(s[3]) > 0 then begin + Result := IsNumeric(s[4]) and (IsNumeric(s[5]) or (IndyPos(':', s[5]) > 0)); {do not localize} + end; + end; + end; + finally + FreeAndNil(s); + end; + end; + + function FixBonkedYear(const AStrPart : String) : String; + var + LB : String; + begin + LB := AStrPart; + Result := Fetch(LB); + //TODO: use StringsReplace() instead + //Result := StringsReplace(Result, ['-', '/'], [' ', ' ']); {do not localize} + Result := ReplaceAll(Result, '-', ' '); {do not localize} + Result := ReplaceAll(Result, '/', ' '); {do not localize} + Result := Result + ' ' + LB; {do not localize} + end; + +begin + LI := AItem as TIdUnixFTPListItem; + // Get defaults for modified date/time + ADate := Now; + DecodeDate(ADate, wYear, wMonth, wDay); + DecodeTime(ADate, wHour, wMin, wSec, wMSec); + LData := AItem.Data; + LStep := pusINode; + repeat + case LStep of + pusINode: begin + //we do it this way because the column for inode is right justified + //and we don't want to create a problem if the -i parameter was never used + LTmp := TrimLeft(LData); + LTmp := Fetch(LTmp); + if IsValidUnixPerms(LTmp) then begin + LStep := pusPerm; + end else begin + //the inode column is right justified + LData := TrimLeft(LData); + LTmp := Fetch(LData); + LData := TrimLeft(LData); + LInode := LTmp; + LStep := pusBlocks; + end; + end; + pusBlocks: begin + //Note that there is an ambigioutity because this value could + //be the inode if only the -i switch was used. + LTmp := Fetch(LData, ' ', False); {do not localize} + if not IsValidUnixPerms(LTmp) then begin + LTmp := Fetch(LData); + LData := TrimLeft(LData); + LBlocks := LTmp; + end; + LStep := pusPerm; + end; + pusPerm: begin //1.-rw-rw-rw- + LTmp := Fetch(LData); + LData := TrimLeft(LData); + // Copy the predictable pieces + LI.PermissionDisplay := Copy(LTmp, 1, 10); + LDir := UpperCase(Copy(LTmp, 1, 1)); + LOPerm := Copy(LTmp, 2, 3); + LGPerm := Copy(LTmp, 5, 3); + LUPerm := Copy(LTmp, 8, 3); + LStep := pusCount; + end; + pusCount: begin + LData := TrimLeft(LData); + LTmp := Fetch(LData); + LData := TrimLeft(LData); + //Patch for NetPresenz + // "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */ + // "drwxrwxr-x folder 2 May 10 1996 network" */ + if TextIsSame(LTmp, 'folder') then begin {do not localize} + LStep := pusSize; + end else begin + //APR + //Patch for overflow -r--r--r-- 0526478 128 Dec 30 2002 DE292000 + if (Length(LTmp) > 3) and (LTmp[1] = '0') then begin + LData := Copy(LTmp, 2, MaxInt) + ' ' + LData; + LCount := '0'; + end else begin + LCount := LTmp; + end; + //this check is necessary if both the owner and group were surpressed. + if IsGOSwitches(AItem.Data) then begin + LStep := pusSize; + end else begin + LStep := pusOwner; + end; + end; + LData := TrimLeft(LData); + end; + pusOwner: begin + LTmp := Fetch(LData); + LData := TrimLeft(LData); + LOwner := LTmp; + LStep := pusGroup; + end; + pusGroup: begin + LTmp := Fetch(LData); + LData := TrimLeft(LData); + LGroup := LTmp; + LStep := pusSize; + end; + pusSize: begin + //Ericsson - Switch FTP returns empty owner + //Do not apply Ericson patch to Unitree + if IsAlpha(LData, 1, 1) and (GetIdent <> UNITREE) then begin + LSize := LGroup; + LGroup := LOwner; + LOwner := ''; + //we do this just after the erickson patch because + //a few servers might return additional columns. + // + //e.g. + // + //drwx------ 1 BUILTIN NT AUTHORITY 0 Dec 7 2001 System Volume Information + if not IsNumeric(LSize) then begin + //undo the Ericson patch + LOwner := LGroup; + LGroup := ''; + repeat + LGroup := LGroup + ' ' + LSize; + LOwner := LGroup; + LData := TrimLeft(LData); + LSize := Fetch(LData); + until IsNumeric(LSize); + //delete the initial space we had added in the repeat loop + IdDelete(LGroup, 1, 1); + end; + end else begin + LTmp := Fetch(LData); + //This is necessary for cases where are char device is listed + //e.g. + //crw-rw-rw- 1 0 1 11, 42 Aug 8 2000 tcp + // + //Note sure what 11, 42 is so size is not returned. + if IndyPos(',', LTmp) > 0 then begin {do not localize} + LData := TrimLeft(LData); + Fetch(LData); + LData := TrimLeft(LData); + LSize := ''; + end else begin + LSize := LTmp; + end; + LData := TrimLeft(LData); + case PosInStrArray(LSize, UnitreeStoreTypes) of + 0 : //AR - archived to tape - migrated + begin + if AItem is TIdUnitreeFTPListItem then begin + (LI as TIdUnitreeFTPListItem).Migrated := True; + (LI as TIdUnitreeFTPListItem).FileFamily := Fetch(LData); + end; + LData := TrimLeft(LData); + LSize := Fetch(LData); + LData := TrimLeft(LData); + end; + 1 : //DK - disk + begin + if AItem is TIdUnitreeFTPListItem then begin + (LI as TIdUnitreeFTPListItem).FileFamily := Fetch(LData); + end; + LData := TrimLeft(LData); + LSize := Fetch(LData); + LData := TrimLeft(LData); + end; + end; + end; + LStep := pusMonth; + end; + pusMonth: begin // Scan modified MMM + // Handle Chinese listings; the month, day, and year may not have spaces between them + if IndyPos(ChineseYear, LData) > 0 then begin + wYear := IndyStrToInt(Fetch(LData, ChineseYear)); + LData := TrimLeft(LData); + // Set time info to 00:00:00.999 + wHour := 0; + wMin := 0; + wSec := 0; + wMSec := 999; + LStep := pusName + end; + if IndyPos(ChineseDay, LData) > 0 then begin + wMonth := IndyStrToInt(Fetch(LData, ChineseMonth)); + LData := TrimLeft(LData); + wDay := IndyStrToInt(Fetch(LData, ChineseDay)); + LData := TrimLeft(LData); + if LStep <> pusName then begin + LTmp := Fetch(LData); + LStep := pusTime; + end; + Continue; + end; + //fix up a bonked date such as: + //-rw-r--r-- 1 root other 531 09-26 13:45 README3 + LData := FixBonkedYear(LData); + //we do this in case there's a space + LTmp := Fetch(LData); + if (Length(LTmp) > 3) and IsNumeric(LTmp) then begin + //must be a year + wYear := IndyStrToInt(LTmp, wYear); + LTmp := Fetch(LData); + end; + LData := TrimLeft(LData); + // HPUX can output the dates like "28. Jan., 16:48", "5. Mai, 05:34" or + // "7. Nov. 2004" + if TextEndsWith(LTmp, '.') then begin + Delete(LTmp, Length(LTmp), 1); + end; + // Korean listings will have the Korean "month" character + DeleteSuffix(LTmp,KoreanMonth); + // Just in case + DeleteSuffix(LTmp,KoreanEUCMonth); + { if IndyPos(KoreanMonth, LTmp) = Length(LTmp) - Length(KoreanMonth) + 1 then + begin + Delete(LTmp, Length(LTmp) - Length(KoreanMonth) + 1, Length(KoreanMonth)); + end; + // Japanese listings will have the Japanese "month" character +} DeleteSuffix(LTmp,JapaneseMonth); + if IsNumeric(LTmp) then begin + wMonth := IndyStrToInt(LTmp, wMonth); + // HPUX + LTmp := Fetch(LData, ' ', False); + if TextEndsWith(LTmp, ',') then begin + Delete(LTmp, Length(LTmp), 1); + end; + if TextEndsWith(LTmp, '.') then begin + Delete(LTmp, Length(LTmp), 1); + end; + // Handle dates where the day preceeds a string month (French, Dutch) + i := StrToMonth(LTmp); + if i > 0 then begin + wDay := wMonth; + LTmp := Fetch(LData); + LData := TrimLeft(LData); + wMonth := i; + LStep := pusYear; + end else begin + if wMonth > 12 then begin + wDay := wMonth; + LTmp := Fetch(LData); + LData := TrimLeft(LData); + wMonth := IndyStrToInt(LTmp, wMonth); + LStep := pusYear; + end else begin + LStep := pusDay; + end; + end; + end else begin + wMonth := StrToMonth(LTmp); + LStep := pusDay; + // Korean listings can have dates in the form "2004.10.25" + if wMonth = 0 then begin + wYear := IndyStrToInt(Fetch(LTmp, '.'), wYear); + wMonth := IndyStrToInt(Fetch(LTmp, '.'), 0); + wDay := IndyStrToInt(LTmp); + LStep := pusName; + end; + end; + end; + pusDay: begin // Scan DD + LTmp := Fetch(LData); + LData := TrimLeft(LData); + // Korean dates can have their "Day" character as included +{ if IndyPos(KoreanDay, LTmp) = Length(LTmp) - Length(KoreanDay) + 1 then + begin + Delete(LTmp, Length(LTmp) - Length(KoreanDay) + 1, Length(KoreanDay)); + end; } + DeleteSuffix(LTmp,KoreanDay); + //Ditto for Japanese + DeleteSuffix(LTmp,JapaneseDay); + wDay := IndyStrToInt(LTmp, wDay); + LStep := pusYear; + end; + pusYear: begin + LTmp := Fetch(LData); + //Some localized Japanese listings include a year sybmol + DeleteSUffix(LTmp,JapaneseYear); + // Not time info, scan year + if IndyPos(':', LTmp) = 0 then begin {Do not Localize} + wYear := IndyStrToInt(LTmp, wYear); + // Set time info to 00:00:00.999 + wHour := 0; + wMin := 0; + wSec := 0; + wMSec := 999; + LStep := pusName; + end else begin + // Time info, scan hour, min + LStep := pusTime; + end; + end; + pusTime: begin + // correct year and Scan hour + wYear := AddMissingYear(wDay, wMonth); + wHour:= IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not Localize} + // Set sec and ms to 0.999 except for Serv-U or FreeBSD with the -T parameter + //with the -T parameter, Serve-U returns something like this: + // + //drwxrwxrwx 1 user group 0 Mar 3 04:49:59 2003 upload + // + //instead of: + // + //drwxrwxrwx 1 user group 0 Mar 3 04:49 upload + if (IndyPos(':', LTmp) > 0) and (IsNumeric(Fetch(LData, ' ', False))) then begin {Do not localize} + // Scan minutes + wMin := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize} + wSec := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize} + wMSec := IndyStrToInt(Fetch(LTmp,':'), 999); {Do not localize} + LTmp := Fetch(LData); + wYear := IndyStrToInt(LTmp, wYear); + end else begin + // Scan minutes + wMin := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize} + wSec := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize} + wMSec := IndyStrToInt(Fetch(LTmp), 999); + end; + LStep := pusName; + end; + pusName: begin + LName := LData; + LStep := pusDone; + end; + end;//case LStep + until LStep = pusDone; + AItem.ItemType := ditFile; + if LDir <> '' then begin + case LDir[1] of + 'D' : AItem.ItemType := ditDirectory; {Do not Localize} + 'L' : AItem.ItemType := ditSymbolicLink; {Do not Localize} + 'B' : AItem.ItemType := ditBlockDev; {Do not Localize} + 'C' : AItem.ItemType := ditCharDev; {Do not Localize} + 'P' : AItem.ItemType := ditFIFO; {Do not Localize} + 'S' : AItem.ItemType := ditSocket; {Do not Localize} + end; + end; + LI.UnixOwnerPermissions := LOPerm; + LI.UnixGroupPermissions := LGPerm; + LI.UnixOtherPermissions := LUPerm; + LI.LinkCount := IndyStrToInt(LCount, 0); + LI.OwnerName := LOwner; + LI.GroupName := LGroup; + LI.Size := IndyStrToInt64(LSize, 0); + if (wMonth = 2) and (wDay = 29) and (not IsLeapYear(wYear)) then + begin + {temporary workaround for Leap Year, February 29th. Encode with day - 1, but do NOT decrement wDay since this will give us the wrong day when we adjust/re-calculate the date later} + LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay - 1) + EncodeTime(wHour, wMin, wSec, wMSec); + end else begin + LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay) + EncodeTime(wHour, wMin, wSec, wMSec); + end; + + {PATCH: If Indy incorrectly decremented the year then it will be almost a year behind. + Certainly well past 90 days and so we will have the day and year in the raw data. + (Files that are from within the last 90 days do not show the year as part of the date.)} + wdayStr := IntToStr(wDay); + while Length(wDayStr) < 2 do begin + wDayStr := '0' + wDayStr; {do not localize} + end; + DecodeDate(Now, wCurrYear, wCurrMonth, wCurrDay); + if (wYear < wCurrYear) and ((Now-LI.ModifiedDate) > 90) and + (Pos(IntToStr(wMonth) + ' ' + IntToStr(wYear), LI.Data) = 0) and + (Pos(IntToStr(wMonth) + ' ' + wDayStr + ' ' + IntToStr(wYear), LI.Data) = 0) and + (Pos(monthNames[wMonth] + ' ' + IntToStr(wYear), LI.Data) = 0) and + (Pos(monthNames[wMonth] + ' ' + wDayStr + ' ' + IntToStr(wYear), LI.Data) = 0) then + begin + {sanity check to be sure we aren't making future dates!!} + {$IFDEF VCL_6_OR_ABOVE} + if IncYear(LI.ModifiedDate) <= (Now + 7) then + {$ELSE} + if IncMonth(LI.ModifiedDate,12) <= (Now + 7) then + {$ENDIF} + begin + Inc(wYear); + LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay) + EncodeTime(wHour, wMin, wSec, wMSec); + end; + end; + + if LI.ItemType = ditSymbolicLink then begin + i := IndyPos(UNIX_LINKTO_SYM, LName); + LLinkTo := Copy(LName, i + 4, Length(LName) - i - 3); + LName := Copy(LName, 1, i - 1); + //with ls -F (DIR -F in FTP, you will sometimes symbolic links with the linked + //to item file name ending with a /. That indicates that the item being pointed to + //is a directory + if TextEndsWith(LLinkTo, PATH_FILENAME_SEP_UNIX) then begin + LI.ItemType := ditSymbolicLinkDir; + LLinkTo := Copy(LLinkTo, 1, Length(LLinkTo)-1); + end; + LI.LinkedItemName := LLinkTo; + end; + LI.NumberBlocks := IndyStrToInt(LBlocks, 0); + LI.Inode := IndyStrToInt(LInode, 0); + //with servers using ls -F, / is returned after the name of dir names and a * + //will be returned at the end of a file name for an executable program. + //Based on info at http://www.skypoint.com/help/tipgettingaround.html + //Note that many FTP servers obtain their DIR lists by piping output from the /bin/ls -l command. + //The -F parameter does work with ftp.netscape.com and I have also tested a NcFTP server + //which simulates the output of the ls command. + if CharIsInSet(LName, Length(LName), PATH_FILENAME_SEP_UNIX + '*') then begin {Do not localize} + LName := Copy(LName, 1, Length(LName)-1); + end; + + if APath <> '' then begin + // a path can sometimes come into the form of: + // pub: + // or + // ./pub + // + //Deal with both cases + LI.LocalFileName := LName; + LName := APath + PATH_FILENAME_SEP_UNIX + LName; + if TextStartsWith(LName, UNIX_CURDIR) then begin + IdDelete(LName, 1, Length(UNIX_CURDIR)); + if TextStartsWith(LName, PATH_FILENAME_SEP_UNIX) then begin + IdDelete(LName, 1, Length(PATH_FILENAME_SEP_UNIX)); + end; + end; + end; + + LI.FileName := LName; + Result := True; +end; + +class function TIdFTPLPUnix.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + i : Integer; + LPathSpec : String; + LItem : TIdFTPListItem; +begin + for i := 0 to AListing.Count-1 do begin + if not ((AListing[i] = '') or IsTotalLine(AListing[i]) or IsUnixLsErr(AListing[i]) or IsUnitreeBanner(AListing[i])) then begin + //workaround for the XBox MediaCenter FTP Server + //which returns something like this: + // + //dr-xr-xr-x 1 ftp ftp 1 Feb 23 00:00 D: + //and the trailing : is falsely assuming that a ":" means + //a subdirectory entry in a recursive list. + if (not InternelChkUnix(AListing[i])) and IsSubDirContentsBanner(AListing[i]) then begin + LPathSpec := Copy(AListing[i], 1, Length(AListing[i])-1); + end else begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + Result := ParseLine(LItem, LPathSpec); + if not Result then begin + FreeAndNil(LItem); + Exit; + end; + end; + end; + end; + Result := True; +end; + +{ TIdFTPLPUnitree } + +class function TIdFTPLPUnitree.GetIdent: String; +begin + Result := UNITREE; +end; + +class function TIdFTPLPUnitree.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdUnitreeFTPListItem.Create(AOwner); +end; + +initialization + RegisterFTPListParser(TIdFTPLPUnix); + RegisterFTPListParser(TIdFTPLPUnitree); +finalization + UnRegisterFTPListParser(TIdFTPLPUnix); + UnRegisterFTPListParser(TIdFTPLPUnitree); + +end. diff --git a/indy/Protocols/IdFTPListParseVM.pas b/indy/Protocols/IdFTPListParseVM.pas new file mode 100644 index 0000000..eb008dd --- /dev/null +++ b/indy/Protocols/IdFTPListParseVM.pas @@ -0,0 +1,607 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.8 2/24/2005 10:01:34 AM JPMugaas + Fixed estimation of filesize for variable record length files (V) in z/VM to + conform to what was specified in: + + z/VMTCP/IP Users Guide Version 5 Release 1.0 + + This will not always give the same estimate as the server would when listing + in Unix format "SITE LISTFORMAT UNIX" because we can't know the block size + (we have to assume a size of 4096). + + Rev 1.7 10/26/2004 10:03:20 PM JPMugaas + Updated refs. + + Rev 1.6 9/7/2004 10:02:30 AM JPMugaas + Tightened the VM/BFS parser detector so that valid dates have to start the + listing item. This should reduce the likelyhood of error. + + Rev 1.5 6/28/2004 4:34:18 AM JPMugaas + VM_CMS-ftp.marist.edu-7.txt was being detected as VM/BFS instead of VM/CMS + causing a date encode error. + + Rev 1.4 4/19/2004 5:05:32 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:22 PM czhower + Name changes + + Rev 1.2 10/19/2003 3:48:12 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:04:30 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 04:18:20 AM JPMugaas + More things restructured for the new list framework. +} + +unit IdFTPListParseVM; + +{ + IBM VM and z/VM parser +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdVMCMSFTPListItem = class(TIdRecFTPListItem) + protected + FOwnerName : String; + FNumberBlocks : Integer; + public + property RecLength : Integer read FRecLength write FRecLength; + property RecFormat : String read FRecFormat write FRecFormat; + property NumberRecs : Integer read FNumberRecs write FNumberRecs; + property OwnerName : String read FOwnerName write FOwnerName; + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + end; + + TIdVMVirtualReaderFTPListItem = class(TIdFTPListItem) + protected + FNumberRecs : Integer; + public + constructor Create(AOwner: TCollection); override; + property NumberRecs : Integer read FNumberRecs write FNumberRecs; + end; + + TIdVMBFSFTPListItem = class(TIdFTPListItem); + + TIdFTPLPVMCMS = class(TIdFTPListBaseHeaderOpt) + protected + class function IsHeader(const AData : String): Boolean; override; + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + class function CheckListingAlt(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + public + class function GetIdent : String; override; + end; + + TIdFTPLPVMBFS = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdFTPLVirtualReader = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseVM"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +function IsFileMode(const AStr : String) : Boolean; +begin + Result := CharIsInSet(AStr,1,'ABCDEFGHIJKLMNOPQRSTUV') and + CharIsInSet(AStr,2,'0123456'); +end; + +{ TIdFTPLPVMCMS } + +class function TIdFTPLPVMCMS.CheckListingAlt(AListing: TStrings; const ASysDescript: String; + const ADetails: Boolean): Boolean; +const + VMTypes : array [1..3] of string = ('F','V','DIR'); {do not localize} +var + LData : String; + +begin + Result := False; + if AListing.Count > 0 then begin + LData := AListing[0]; + if IsFileMode(Trim(Copy(LData, 19, 3))) then begin + Result := PosInStrArray(Trim(Copy(LData, 22, 3)), VMTypes) <> -1; + if Result then begin + Result := IsMMDDYY(Trim(Copy(LData,52,10)),'/'); + end; + end else begin + Result := PosInStrArray(Trim(Copy(LData, 19, 3)), VMTypes) <> -1; + if Result then begin + Result := (Copy(LData, 56, 1) = '/') and (Copy(LData, 59, 1) = '/'); {do not localize} + if not Result then begin + Result := (Copy(LData, 58, 1) = '-') and (Copy(LData, 61, 1) = '-'); {do not localize} + if not Result then begin + Result := (Copy(LData, 48, 1) = '-') and (Copy(LData, 51, 1) = '-'); {do not localize} + end; + end; + end; + end; + end; +end; + +class function TIdFTPLPVMCMS.GetIdent: String; +begin + Result := 'VM/CMS'; {do not localize} +end; + +class function TIdFTPLPVMCMS.IsHeader(const AData: String): Boolean; +begin + Result := Trim(AData) = 'Filename FileType Fm Format Lrecl Records Blocks Date Time' +end; + +class function TIdFTPLPVMCMS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVMCMSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVMCMS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; + LCols : TStrings; + LI : TIdVMCMSFTPListItem; + LSize : Int64; + LPRecLn,LLRecLn : Integer; + LPRecNo, LLRecNo : Integer; + LPBkNo,LLBkNo : Integer; + LPCol : Integer; +begin +{Some of this is based on the following: + +http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=4e7k0p%24t1v%40blackice.winternet.com&rnum=4&prev=/groups%3Fq%3DVM%2BFile%2BRecords%2Bdirectory%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D4e7k0p%2524t1v%2540blackice.winternet.com%26rnum%3D4 + +and + +http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 +} +{ +123456789012345678901234567890123456789012345678901234567890123456789012 + 1 2 3 4 5 6 7 +OMA00215 PLAN V 64 28 1 6/26/02 9:33:21 - +WEBSHARE DIR - - - 5/30/97 18:44:17 - + +or + +README ANONYMOU V 71 26 1 1997-04-02 12:33:20 TCP291 + +or maybe this: + +ENDTRACE TCPIP F 80 1 1 1999-07-28 12:24:01 TCM191 +123456789012345678901234567890123456789012345678901234567890123456789012 + 1 2 3 4 5 6 7 + +or possibly this FILELIST format: + +Filename FileType Fm Format Lrecl Records Blocks Date Time +LASTING GLOBALV A1 V 41 21 1 9/16/91 15:10:32 +J43401 NETLOG A0 V 77 1 1 9/12/91 12:36:04 +PROFILE EXEC A1 V 17 3 1 9/12/91 12:39:07 +DIRUNIX SCRIPT A1 V 77 1216 17 1/04/93 20:30:47 +MAIL PROFILE A2 F 80 1 1 10/14/92 16:12:27 +BADY2K TEXT A0 V 1 1 1 1/03/102 10:11:12 +AUTHORS A1 DIR - - - 9/20/99 10:31:11 +--------------- + +123456789012345678901234567890123456789012345678901234567890123456789012 + 1 2 3 4 5 6 7 + +} + LI := AItem as TIdVMCMSFTPListItem; + //File Name + LI.FileName := Trim(Copy(AItem.Data, 1, 8)); + //File Type - extension + if LI.Data[9] = ' ' then begin + LBuffer := Copy(AItem.Data, 10, 9); + end else begin + LBuffer := Copy(AItem.Data, 9, 9); + end; + LBuffer := Trim(LBuffer); + if LBuffer <> '' then begin + LI.FileName := LI.FileName + '.' + LBuffer; {do not localize} + end; + //Record format + LBuffer := Trim(Copy(AItem.Data, 19, 3)); + if IsFileMode(LBuffer) then begin + LBuffer := Trim(Copy(AItem.Data, 23, 3)); + LPRecLn := 30; + LLRecLn := 7; + LPRecNo := 37; + LLRecNo := 7; + LPBkNo := 44; + LLBkNo := 8; + LPCol := 52; + end else begin + LPRecLn := 22; + LLRecLn := 9; + LPRecNo := 31; + LLRecNo := 11; + LPBkNo := 42; + LLBkNo := 11; + if (Copy(AItem.Data, 48, 1) = '-') and (Copy(AItem.Data, 51, 1) = '-') then begin {do not localize} + LPCol := 44; + end else begin + LPCol := 54; + end; + end; + LI.RecFormat := LBuffer; + if LI.RecFormat = 'DIR' then begin {do not localize} + LI.ItemType := ditDirectory; + LI.RecLength := 0; + end else begin + LI.ItemType := ditFile; + //Record Length - for files + LBuffer := Copy(AItem.Data, LPRecLn, LLRecLn); + LI.RecLength := IndyStrToInt(LBuffer, 0); + //Record numbers + LBuffer := Trim(Copy(AItem.Data, LPRecNo, LLRecNo)); + LBuffer := Fetch(LBuffer); + LI.NumberRecs := IndyStrToInt(LBuffer, 0); + //Number of Blocks + { + From: + + http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 + + Block sizes can be 800, 512, 1024, + 2048, or 4096, per the whim of the user. IBM loves 4096, but it wastes + space (just like on FAT partitions on DOS.) + + For F files (any type which begins with F), record count times logical + record length. + + For V files, you need to read the file for an exact count, or the block + size (times block count) for a good guess. In other words, you're up + the creek because you don't KNOW the block size. Use record size times + record length for a _maximum_ file size. + + Anyway, you can not know from the directory list. + } + LBuffer := Trim(Copy(AItem.Data, LPBkNo, LLBkNo)); + LI.NumberBlocks := IndyStrToInt(LBuffer, 0); + LI.Size := LI.RecLength * LI.NumberRecs; + //File Size - note that this is just an estimiate + + {From: + z/VMTCP/IP Users Guide Version 5 Release 1.0 + Copyright International Business Machines Corporation 1987, 2004. + All rights reserved. + + For fixed-record (F) format minidisk and SFS files, + the size field indicates the actual size of a file. + For variable-record (V) format minidisk and SFS files, + the size field contains an estimated file size, this + being the lesser value determined by: + + the number of records in the file and its maximum record length + the size and number of blocks required to maintain the file. + + For virtual reader files, a size of 0 is always indicated.} + + if LI.RecFormat = 'V' then begin + LSize := LI.NumberBlocks * 4096; + if LI.Size > LSize then begin + LI.Size := LSize; + end; + end; + if LI.RecFormat = 'DIR' then begin + LI.SizeAvail := False; + end; + end; + LCols := TStringList.Create; + try + // we do things this way for the rest because vm.sc.edu has + // a variation on VM/CMS that does directory dates differently + //and some columns could be off. + //Note that the start position in one server it's column 44 while in others, it's column 54 + // handle both cases. + LBuffer := Trim(Copy(AItem.Data, LPCol, MaxInt)); + SplitDelimitedString(LBuffer, LCols, True); + //LCols - 0 - Date + //LCols - 1 - Time + //LCols - 2 - Owner if present + if LCols.Count > 0 then begin + //date + if IsNumeric(LCols[0], 3) then begin + // vm.sc.edu date stamps yyyy-mm-dd + LI.ModifiedDate := DateYYMMDD(LCols[0]); + end else begin + //Note that the date is displayed as 2 digits not 4 digits + //mm/dd/yy + LI.ModifiedDate := DateMMDDYY(LCols[0]); + end; + //time + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LCols[1]); + //owner + if (LCols.Count > 2) and (LCols[2] <> '-') then begin {do not localize} + LI.OwnerName := LCols[2]; + end; + end; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPVMBFS } + +{List format like: + +=== +05/20/2000 13:38:19 F 1 65758 bfsline.cpy +05/19/2000 11:02:15 F 1 65758 bfsline.txt +06/03/2000 12:27:48 F 1 15414 bfstest.cpy +05/20/2000 13:38:05 F 1 15414 bfstest.output +05/20/2000 13:38:42 F 1 772902 bfswork.output +03/31/2000 15:49:27 F 1 782444 bfswork.txt +05/20/2000 13:39:20 F 1 13930 lotsonl.putdata +05/19/2000 09:41:21 F 1 13930 lotsonl.txt +06/15/2000 09:29:25 F 1 278 mail.maw +05/20/2000 13:39:34 F 1 278 mail.putdata +05/20/2000 15:30:45 F 1 13930 nls.new +05/20/2000 14:02:24 F 1 13931 nls.txt +08/21/2000 10:03:17 F 1 328 rock.rules +05/20/2000 13:40:05 F 1 58 testfil2.putdata +04/26/2000 14:34:42 F 1 63 testfil2.txt +08/21/2000 05:28:40 D - - ALTERNATE +12/28/2000 17:36:19 D - - FIRST +=== +} +class function TIdFTPLPVMBFS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; +begin + Result := False; + + if AListing.Count > 0 then begin + //should have a "'" as the terminator + if AListing[0] <> '' then begin + if not TextEndsWith(AListing[0], '''') then begin + Exit; + end; + end; + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + if s.Count > 4 then begin + if not IsMMDDYY(s[0], '/') then begin {do not localize} + Exit; + end; + Result := CharIsInSet(s[2], 1, 'FD'); {do not localize} + if Result then begin + Result := IsNumeric(s[4]) or (s[4] <> '-'); {do not localize} + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPVMBFS.GetIdent: String; +begin + Result := 'VM/BFS'; {do not localize} +end; + +class function TIdFTPLPVMBFS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVMBFSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVMBFS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; + LCols : TStrings; +begin + // z/VM Byte File System + + //This is based on: + // + // z/VM: TCP/IP Level 430 User's Guide Version 4 Release 3.0 + // + // http://www.vm.ibm.com/pubs/pdf/hcsk7a10.pdf + // + LBuffer := AItem.Data; + LCols := TStringList.Create; + try + SplitDelimitedString(Fetch(LBuffer, #39), LCols, True); {do not localize} + //0 - date + //1 - time + //2 - (D) dir or file (F) + //3 - not sure what this is + //4 - file size + AItem.FileName := LBuffer; + if TextEndsWith(AItem.FileName, '''') then begin + AItem.FileName := Copy(AItem.FileName, 1, Length(AItem.FileName)-1); + end; + //date + if LCols.Count > 0 then begin + AItem.ModifiedDate := DateMMDDYY(LCols[0]); + end; + if LCols.Count > 1 then begin + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LCols[1]); + end; + if LCols.Count > 2 then + begin + if LCols[2] = 'D' then begin + AItem.ItemType := ditDirectory; + end else begin + AItem.ItemType := ditFile; + end; + end; + //file size + if LCols.Count > 4 then begin + if IsNumeric(LCols[3]) then begin + AItem.Size := IndyStrToInt64(LCols[4], 0); + AItem.SizeAvail := True; + end else begin + AItem.SizeAvail := False; + end; + end; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLVirtualReader } + +class function TIdFTPLVirtualReader.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; +begin + Result := False; + if AListing.Count > 0 then begin + s := TStringList.Create; + try + SplitDelimitedString(AListing[0], s, True); + if s.Count > 2 then begin + if (Length(s[0]) = 4) and IsNumeric(s[0]) then begin + Result := (Length(s[2]) = 8) and (IsNumeric(s[2])); + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLVirtualReader.GetIdent: String; +begin + Result := 'VM Virtual Reader'; {do not localize} +end; + +class function TIdFTPLVirtualReader.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVMVirtualReaderFTPListItem.Create(AOwner); +end; + +class function TIdFTPLVirtualReader.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; + LI : TIdVMVirtualReaderFTPListItem; +begin + // z/VM Byte File System + + //This is based on: + // + // z/VM: TCP/IP Level 430 User's Guide Version 4 Release 3.0 + // + // http://www.vm.ibm.com/pubs/pdf/hcsk7a10.pdf + // + LI := AItem as TIdVMVirtualReaderFTPListItem; + LCols := TStringList.Create; + try + // z/VM Virtual Reader (RDR) + //Col 0 - spool ID + //Col 1 - origin + //Col 2 - records + //Col 3 - date + //Col 4 - time + //Col 5 - filename + //Col 6 - file type + SplitDelimitedString(AItem.Data, LCols, True); + if LCols.Count > 5 then begin + LI.FileName := LCols[5]; + end; + if LCols.Count > 6 then begin + LI.FileName := LI.FileName + '.' + LCols[6]; {do not localize} + end; + //record count + if LCols.Count > 2 then begin + LI.NumberRecs := IndyStrToInt(LCols[2], 0); + end; + //date + if LCols.Count > 3 then begin + LI.ModifiedDate := DateYYMMDD(LCols[3]); + end; + //Time + if LCols.Count > 4 then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LCols[1]); + end; + //Note that IBM does not even try to give an estimate + //with reader file sizes when emulating Unix. We can't support file sizes + //with this. + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdVMVirtualReaderFTPListItem } + +constructor TIdVMVirtualReaderFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + //There's no size for things in a virtual reader + SizeAvail := False; +end; + +initialization + RegisterFTPListParser(TIdFTPLVirtualReader); + RegisterFTPListParser(TIdFTPLPVMBFS); + RegisterFTPListParser(TIdFTPLPVMCMS); +finalization + UnRegisterFTPListParser(TIdFTPLVirtualReader); + UnRegisterFTPListParser(TIdFTPLPVMBFS); + UnRegisterFTPListParser(TIdFTPLPVMCMS); + +end. diff --git a/indy/Protocols/IdFTPListParseVMS.pas b/indy/Protocols/IdFTPListParseVMS.pas new file mode 100644 index 0000000..ef8efc6 --- /dev/null +++ b/indy/Protocols/IdFTPListParseVMS.pas @@ -0,0 +1,457 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.12 2/23/2005 6:34:28 PM JPMugaas + New property for displaying permissions ina GUI column. Note that this + should not be used like a CHMOD because permissions are different on + different platforms - you have been warned. + + Rev 1.11 10/26/2004 10:03:22 PM JPMugaas + Updated refs. + + Rev 1.10 7/31/2004 1:08:24 PM JPMugaas + Now should handle listings without time. + + Rev 1.9 6/21/2004 10:57:42 AM JPMugaas + Now indicates that ModifiedDate and File Size are not available if VMS + returns an error in the entry. + + Rev 1.8 6/11/2004 9:35:08 AM DSiders + Added "Do not Localize" comments. + + Rev 1.7 6/7/2004 3:47:48 PM JPMugaas + VMS Recursive Dir listings now supported. This is done with a [...]. Note + that VMS does have some strange syntaxes with their file system. + + Rev 1.6 4/20/2004 4:01:16 PM JPMugaas + Fix for nasty typecasting error. The wrong create was being called. + + Rev 1.5 4/19/2004 5:05:18 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:16 PM czhower + Name changes + + Rev 1.3 10/19/2003 3:48:12 PM DSiders + Added localization comments. + + Rev 1.2 10/1/2003 12:53:08 AM JPMugaas + Indicated that VMS returns block sizes. Note that in VMS, the traditional + block size is 512 bytes (this is a fixed constant). + + Rev 1.1 4/7/2003 04:04:36 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 02:01:58 AM JPMugaas + Individual parsing objects for the new framework. +} +unit IdFTPListParseVMS; + +{ + This parser works with VMS (OpenVMS) systems including UCX, MadGoat, Multinet, + VMS TCPWare, plus some non-multinet systems. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdVMSFTPListItem = class(TIdOwnerFTPListItem) + protected + FGroupName : String; + FVMSOwnerPermissions: String; + FVMSWorldPermissions: String; + FVMSSystemPermissions: String; + FVMSGroupPermissions: String; + FNumberBlocks : Integer; + FBlockSize : Integer; + FVersion : Integer; + public + property GroupName : String read FGroupName write FGroupName; + //VMS File Protections + //These are different than Unix. See: + //See http://www.djesys.com/vms/freevms/mentor/vms_prot.html#prvs + + property VMSSystemPermissions : String read FVMSSystemPermissions write FVMSSystemPermissions; + property VMSOwnerPermissions : String read FVMSOwnerPermissions write FVMSOwnerPermissions; + property VMSGroupPermissions : String read FVMSGroupPermissions write FVMSGroupPermissions; + property VMSWorldPermissions : String read FVMSWorldPermissions write FVMSWorldPermissions; + property Version : Integer read FVersion write FVersion; + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + property BlockSize : Integer read FBlockSize write FBlockSize; + end; + + TIdFTPLPVMS = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsVMSHeader(const AData: String): Boolean; + class function IsVMSFooter(const AData: String): Boolean; + class function IsContinuedLine(const AData: String): Boolean; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseVMS"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, + SysUtils; + +{ TIdFTPLPVMS } + +class function TIdFTPLPVMS.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LData : String; + i : Integer; +begin + Result := False; + for i := 0 to AListing.Count - 1 do + begin + if AListing[i] <> '' then + begin + LData := AListing[i]; + Result := Length(LData) > 1; + if Result then + begin + Result := IsVMSHeader(LData); + //see if file listing starts a file name + if not Result then + begin + LData := Fetch(LData); + Fetch(LData, ';'); {do not localize} + Result := IsNumeric(LData); + end; + end; + Break; + end; + end; +end; + +class function TIdFTPLPVMS.GetIdent: String; +begin + Result := 'VMS'; {do not localize} +end; + +class function TIdFTPLPVMS.IsContinuedLine(const AData: String): Boolean; +begin + Result := TextStartsWith(AData, ' ') and (IndyPos(';', AData) = 0); {do not localize} +end; + +class function TIdFTPLPVMS.IsVMSFooter(const AData: String): Boolean; +var + LData : String; +begin + //The bottum banner may be in the following forms: + //Total of 1 file, 0 blocks. + //Total of 6 Files, 1582 Blocks. + //Total of 90 files. + //Grand total of 87 directories, 2593 files, 2220036 blocks. + //*.*; <%RMS-E-FNF, file not found> + + //VMS returns TOTAL at the end. We test for " files" at the end of the line + //so we don't break something with another parser. + LData := UpperCase(AData); + Result := TextStartsWith(LData, 'TOTAL OF ') or {do not localize} + TextStartsWith(LData, 'GRAND TOTAL OF '); {do not localize} + if Result then + begin + Result := (IndyPos(' FILE', LData) > 9); {do not localize} + if not Result then begin + Result := Fetch(LData) = '*.*;'; {do not localize} + end; + end; +end; + +class function TIdFTPLPVMS.IsVMSHeader(const AData: String): Boolean; +begin + Result := TextEndsWith(AData, ']') and {Do not localize} + (IndyPos(':[', AData) > 0); {Do not localize} +end; + +class function TIdFTPLPVMS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVMSFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVMS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer, LBuf2, LLine : String; + LDay, LMonth, LYear : Integer; + //LHour, LMinute, LSec : Integer; + LCols : TStrings; + LOwnerIdx : Integer; + LVMSError : Boolean; + LI : TIdVMSFTPListItem; +begin + { + 1 2 3 4 5 6 + 1234567890123456789012345678901234567890123456789012345678901234567890 + BILF.C;2 13 5-JUL-1991 12:00 [1,1] (RWED,RWED,RE,RE) + + + and non-MutliNet VMS: + + CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,) + + or possibly VMS TCPware V5.5-3 + + .WELCOME;1 2 13-FEB-2002 23:32:40.47 + } + LI := AItem as TIdVMSFTPListItem; + LVMSError := False; + + LLine := LI.Data; + // Charon VAX 5.4.2 uses tabs between some of its columns and spaces between others + LLine := ReplaceAll(LLine, #9, ' '); + //File Name + //We do this in a roundabout way because spaces in VMS files may actually + //be legal and that throws of a typical non-position based column parser. + //this assumes that the file contains a ";". In VMS, this separates the name + //from the version number. + LBuffer := Fetch(LLine, ';'); {do not localize} + LI.LocalFileName := LowerCase(LBuffer); + LBuf2 := Fetch(LLine); + //Some FTP servers might follow the filename with a tab and than + //give an error such as this: + //1KBTEST.PTF;10#9No privilege for attempted operation + LI.Version := IndyStrToInt(LBuf2, 0); + LBuffer := LBuffer + ';' + LBuf2; {do not localize} + + //Dirs have to be processed differently then + //files because a version mark and .DIR exctension + //are not used to CWD into a subdir although they are + //listed in a dir listed. + if (IndyPos('.DIR;', LBuffer) > 0) then {do not localize} + begin + AItem.ItemType := ditDirectory; + //note that you can NOT simply do a Fetch('.') to extract the dir name + //you use with a CD because the period is also a separator between pathes + // + //e.g. + // + //[VMSSERV.FILES]ALARM.DIR;1 1/3 5-MAR-1993 18:09 + if IndyPos(PATH_FILENAME_SEP_VMS, LBuffer) = 0 then begin + LBuf2 := ''; + end else begin + LBuf2 := Fetch(LBuffer, PATH_FILENAME_SEP_VMS) + PATH_FILENAME_SEP_VMS; {Do not localize} + end; + AItem.FileName := LBuf2 + Fetch(LBuffer, '.'); {do not localize} + AItem.LocalFileName := LowerCase(AItem.FileName); + end else + begin + AItem.ItemType := ditFile; + AItem.FileName := LBuffer; + end; + if APath <> '' then begin + AItem.FileName := APath + AItem.FileName; + end; + LCols := TStringList.Create; + try + SplitDelimitedString(LLine, LCols, True); + LOwnerIdx := 3; + //if this isn't numeric, there may be an error that is + //is reported in the File list. Do not parse the line further. + if LCols.Count > 0 then + begin + LBuffer := LCols[0]; + LBuffer := Fetch(LBuffer, '/'); + if IsNumeric(LBuffer) then + begin + //File Size + LI.NumberBlocks := IndyStrToInt(LBuffer, 0); + LI.BlockSize := VMS_BLOCK_SIZE; + LI.Size := IndyStrToInt64(LBuffer, 0) * VMS_BLOCK_SIZE; //512 is the size of a VMS block + end else + begin + //on the UCX VMS server, the file size might not be reported. Probably the file owner + if not TextStartsWith(LCols[0], '[') then {do not localize} + begin + if not IsNumeric(LCols[0], 1, 1) then + begin + //the server probably reported an error in the FTP list such as no permission + //we need to stop right there. + LVMSError := True; + AItem.SizeAvail := False; + AItem.ModifiedAvail := False; + end; + end else begin + LOwnerIdx := 0; + end; + end; + if not LVMSError then + begin + if LOwnerIdx > 0 then + begin + //Date + if LCols.Count > 1 then + begin + LBuffer := LCols[1]; + LDay := IndyStrToInt(Fetch(LBuffer, '-'), 1); {do not localize} + LMonth := StrToMonth(Fetch(LBuffer, '-')); {do not localize} + LYear := IndyStrToInt(Fetch(LBuffer), 1989); + LI.ModifiedDate := EncodeDate(LYear, LMonth, LDay); + end; + //Time + if LCols.Count > 2 then + begin + //Modified Time of Day + //Some dir listings might be missing the time + //such as this: + // + //vms_dir_2.DIR;1 1 19-NOV-2001 [root,root] (RWE,RWE,RE,RE) + + if IndyPos(':', LCols[2]) = 0 then begin {do not localize} + Dec(LOwnerIdx); + end else begin + LI.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LCols[2]); + end; + end; + end; + //Owner/Group + //This is in the order of Group/Owner + //See: + // http://seqaxp.bio.caltech.edu/www/vms_beginners_faq.html#FILE00 + if LCols.Count > LOwnerIdx then + begin + LBuffer := LCols[LOwnerIdx]; + Fetch(LBuffer, '['); {do not localize} + LBuffer := Fetch(LBuffer,']'); + LI.GroupName := Trim(Fetch(LBuffer, ',')); {do not localize} + LI.OwnerName := Trim(LBuffer); {do not localize} + end; + //Protections + if LCols.Count > (LOwnerIdx+1) then + begin + LBuffer := LCols[LOwnerIdx+1]; + Fetch(LBuffer, '('); {do not localize} + LBuffer := Fetch(LBuffer, ')'); {do not localize} + LI.PermissionDisplay := '(' + LBuffer + ')'; {do not localize} + LI.VMSSystemPermissions := Trim(Fetch(LBuffer, ',')); {do not localize} + LI.VMSOwnerPermissions := Trim(Fetch(LBuffer, ',')); {do not localize} + LI.VMSGroupPermissions := Trim(Fetch(LBuffer, ',')); {do not localize} + LI.VMSWorldPermissions := Trim(LBuffer); + end; + end; + end; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +class function TIdFTPLPVMS.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + i : Integer; + LItem : TIdFTPListItem; + LStartLine, LEndLine : Integer; + LRootPath : String; //needed for recursive dir listings "DIR [...]" + LRelPath : String; +begin + { + VMS is really a ball because the listing + can start and end with blank lines as well as a begging and ending + banner + } + LStartLine := 0; + LRelPath := ''; + LEndLine := AListing.Count-1; + for i := 0 to LEndLine do + begin + if IsWhiteString(AListing[i]) then begin + Inc(LStartLine); + end else + begin + if IsVMSHeader(AListing[i]) then + begin + LRootPath := AListing[i]; + + //to make things easy, we will only use entire banner for deteriming a subdir + //such as this: + // + //Directory ANONYMOUS_ROOT:[000000.VMS-FREEWARE.NARNIA] + // if + //Directory ANONYMOUS_ROOT:[000000.VMS-FREEWARE.NARNIA.COM] + // then result = [.COM] + + LRootPath := Fetch(LRootPath, PATH_FILENAME_SEP_VMS) + '.'; {do not localize} + Inc(LStartLine); + end; + Break; + end; + end; + //find the end of our parsing + for i := LEndLine downto LStartLine do + begin + if IsWhiteString(AListing[i]) or IsVMSFooter(AListing[i]) then begin + Dec(LEndLine); + end else begin + Break; + end; + end; + for i := LStartLine to LEndLine do + begin + if not IsWhiteString(AListing[i]) then + begin + if IsVMSHeader(AListing[i]) then + begin + //+1 is used because there's a period that we are dropping and then adding back + LRelPath := Copy(AListing[i], Length(LRootPath)+1, MaxInt); + LRelPath := VMS_RELPATH_PREFIX + LRelPath; + end + else if not IsContinuedLine(AListing[i]) then //needed because some VMS computers return entries with multiple lines + begin + LItem := MakeNewItem(ADir); + LItem.Data := UnfoldLines(AListing[i], i, AListing); + Result := ParseLine(LItem, LRelPath); + if not Result then + begin + FreeAndNil(LItem); + Exit; + end; + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPVMS); +finalization + UnRegisterFTPListParser(TIdFTPLPVMS); + +end. diff --git a/indy/Protocols/IdFTPListParseVSE.pas b/indy/Protocols/IdFTPListParseVSE.pas new file mode 100644 index 0000000..58dfa9f --- /dev/null +++ b/indy/Protocols/IdFTPListParseVSE.pas @@ -0,0 +1,631 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 10/26/2004 10:03:22 PM JPMugaas + Updated refs. + + Rev 1.5 4/19/2004 5:05:34 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.4 2004.02.03 5:45:24 PM czhower + Name changes + + Rev 1.3 1/23/2004 12:44:52 PM SPerry + fixed set problems + + Rev 1.2 10/19/2003 3:48:14 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:04:38 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 04:18:24 AM JPMugaas + More things restructured for the new list framework. +} + +unit IdFTPListParseVSE; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPCommon, IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +type + TIdVSERootDirFTPListItem = class(TIdMinimalFTPListItem); + + TIdVSELibraryFTPListItem = class(TIdFTPListItem) + protected + FNumberBlocks : Integer; + public + property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks; + end; + + TIdVSEPowerQueueFTPListItem = class(TIdOwnerFTPListItem) + protected + FVSEPQDisposition : TIdVSEPQDisposition; + FVSEPQPriority : Integer; + FNumberRecs : Integer; + public + property NumberRecs : Integer read FNumberRecs write FNumberRecs; + property VSEPQDisposition : TIdVSEPQDisposition read FVSEPQDisposition write FVSEPQDisposition; + property VSEPQPriority : Integer read FVSEPQPriority write FVSEPQPriority; + end; + + TIdVSESubLibraryFTPListItem = class(TIdVSELibraryFTPListItem) + protected + FNumberRecs : Integer; + FCreationDate: TDateTime; + public + property CreationDate: TDateTime read FCreationDate write FCreationDate; + property NumberRecs : Integer read FNumberRecs write FNumberRecs; + end; + + TIdFTPLPVSESubLibrary = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdVSEVSAMCatalogFTPListItem = class(TIdFTPListItem); + + TIdFTPLPVSERootDir = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdFTPLPVSELibrary = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdFTPLPVSEVSAMCatalog = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdVSEVTOCFTPListItem = class(TIdFTPListItem) + public + constructor Create(AOwner: TCollection); override; + end; + + TIdFTPLPVSEVTOC = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + TIdFTPLPVSEPowerQueue = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseVSE"'} + {$ENDIF} + +implementation + +uses + IdException, + IdGlobal, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPVSERootDir } + +class function TIdFTPLPVSERootDir.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LBuffer : String; +begin + if AListing.Count > 0 then + begin + LBuffer := AListing[0]; + Fetch(LBuffer); + LBuffer := Trim(LBuffer); + Result := PosInStrArray(LBuffer, VSERootDirItemTypes) > -1; + end else begin + Result := False; + end; +end; + +class function TIdFTPLPVSERootDir.GetIdent: String; +begin + Result := 'VSE: Root Directory'; {do not localize} +end; + +class function TIdFTPLPVSERootDir.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSERootDirFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSERootDir.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; +begin + //Based on: TCP/IP for VSE User's Guide Version 1 Release 4.0A + //URL: http://publibz.boulder.ibm.com/epubs/pdf/iestcu02.pdf + LBuffer := AItem.Data; + AItem.FileName := Fetch(LBuffer); + LBuffer := Trim(LBuffer); + if PosInStrArray(LBuffer, VSERootDirItemTypes) = 5 then begin + AItem.ItemType := ditFile; + end + else + begin + AItem.ItemType := ditDirectory; + end; + Result := True; +end; + +{ TIdFTPLPVSEVTOC } + +class function TIdFTPLPVSEVTOC.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +const + //S for Sequential + //D for BDAM + //V for VSAM + //I for ISAM + //U for Undefined + ValidFileTypeSet = 'SDVIU'; {Do not translate} +var + s : TStrings; + LData : String; +begin + Result := False; + if AListing.Count > 0 then + begin + LData := AListing[0]; + s := TStringList.Create; + try + SplitDelimitedString(LData, s, True); + if s.Count = 5 then begin + Result := (IndyPos(s[4], ValidFileTypeSet) > 0) and IsNumeric(s[3]); + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPVSEVTOC.GetIdent: String; +begin + Result := 'VSE: VTOC'; {do not localize} +end; + +class function TIdFTPLPVSEVTOC.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSEVTOCFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSEVTOC.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; +begin + LCols := TStringList.Create; + try + //Cols: + // 0 - File name + // 1 - Modified Date + // 2 - Modified Time + // 3 - logical length of records + // 4 - file type (S for Sequential, D for BDAM, V for VSAM, I for ISAM, U for Undefined) + SplitDelimitedString(AItem.Data, LCols, True); + AItem.FileName := LCols[0]; + AItem.ModifiedDate := DateYYMMDD(LCols[1]); + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LCols[2]); + AItem.ItemType := ditFile; + AItem.SizeAvail := False; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPVSEPowerQueue } + +class function TIdFTPLPVSEPowerQueue.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + s : TStrings; + LData : String; +begin + Result := False; + if AListing.Count > 0 then + begin + s := TStringList.Create; + try + LData := AListing[0]; + SplitDelimitedString(LData, s, True); + if (s.Count = 6) or (s.Count = 7) then + begin + //There must be three subentries in the first col separated by + //periods. entries + Result := CharsInStr('.', s[0]) = 2; {do not localize} + if Result then + begin + Result := IsNumeric(s[1]) and IsNumeric(s[2]) and + IsNumeric(s[3]) and IsNumeric(s[4]); + end; + if Result then begin + Result := (s[5] <> '') and (IndyPos(s[5][1], VSE_PowerQueue_Dispositions) <> 0); + end; + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPVSEPowerQueue.GetIdent: String; +begin + Result := 'VSE: PowerQueue'; {do not localize} +end; + +class function TIdFTPLPVSEPowerQueue.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSEPowerQueueFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSEPowerQueue.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; + LI : TIdVSEPowerQueueFTPListItem; +begin + //Based on: TCP/IP for VSE User's Guide Version 1 Release 4.0A + //URL: http://publibz.boulder.ibm.com/epubs/pdf/iestcu02.pdf + LI := AItem as TIdVSEPowerQueueFTPListItem; + LCols := TStringList.Create; + try + SplitDelimitedString(AItem.Data, LCols, True); + //0 - Job name, job number, and job suffix. This information is contained in + // one string, with the three subfields separated by dots. + //1 - records in file + //2 - pages in file + //3 - lines in file + //4 - priority in queue entry + //5 - Disposition of Job + //6 - user ID that owns the job + //contents are always files + if LCols.Count > 0 then begin + LI.FileName := LCols[0]; + end; + if LCols.Count > 1 then + begin + LI.Size := IndyStrToInt(LCols[1], 0); + LI.NumberRecs := AItem.Size; + end; + if LCols.Count > 4 then begin + LI.VSEPQPriority := IndyStrToInt(LCols[4], 0); + end; + if (LCols.Count > 5) and (LCols[5] <> '') then begin + LI.VSEPQDisposition := DispositionCodeToTIdVSEPQDisposition(LCols[5][1]); + end; + if LCols.Count > 6 then begin + LI.OwnerName := LCols[6]; + end; + LI.ItemType := ditFile; + LI.ModifiedAvail := False; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPVSEVSAMCatalog } + +class function TIdFTPLPVSEVSAMCatalog.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +const + //E for ESDS + //K for KSDS + //R for RRDS + ValidFileTypeSet = 'EKR'; {do not localize} +var + s : TStrings; + LData : String; +begin + Result := False; + if AListing.Count >0 then + begin + LData := AListing[0]; + s := TStringList.Create; + try + SplitDelimitedString(LData, s, True); + if s.Count = 5 then begin + Result := (IndyPos(s[4], ValidFileTypeSet) > 0) and IsNumeric(s[3]); + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPVSEVSAMCatalog.GetIdent: String; +begin + Result := 'VSE: VSAM Catalog'; {do not localize} +end; + +class function TIdFTPLPVSEVSAMCatalog.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSEVSAMCatalogFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSEVSAMCatalog.ParseLine( const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LCols : TStrings; + LI : TIdVSEVSAMCatalogFTPListItem; +begin + //Based on: TCP/IP for VSE User's Guide Version 1 Release 4.0A + //URL: http://publibz.boulder.ibm.com/epubs/pdf/iestcu02.pdf + LI := AItem as TIdVSEVSAMCatalogFTPListItem; + LCols := TStringList.Create; + try + //Cols: + // 0 - File name + // 1 - Modified Date + // 2 - Modified Time + // 3 - Number of records (might be reported in Unix emulation mode as size) + // 4 - file type (E for ESDS, K for KSDS, R for RRDS) + SplitDelimitedString(AItem.Data, LCols, True); + LI.FileName := LCols[0]; + LI.ModifiedDate := DateYYMMDD(LCols[1]); + LI.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LCols[2]); + LI.Size := IndyStrToInt64(LCols[3], 0); + LI.ItemType := ditFile; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPVSELibrary } + +class function TIdFTPLPVSELibrary.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): boolean; +var + LBuffer : String; +begin + if AListing.Count > 0 then + begin + LBuffer := AListing[0]; + Fetch(LBuffer); + LBuffer := TrimLeft(LBuffer); + LBuffer := Fetch(LBuffer, '>') + '>'; {do not localize} + Result := LBuffer = ''; //Note that for Libraries, this {Do not translate} + //is always + end else begin + Result := False; + end; +end; + +class function TIdFTPLPVSELibrary.GetIdent: String; +begin + Result := 'VSE: Library'; {do not localize} +end; + +class function TIdFTPLPVSELibrary.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSELibraryFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSELibrary.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; + LCols : TStrings; + LI : TIdVSELibraryFTPListItem; +begin + //Based on: TCP/IP for VSE User's Guide Version 1 Release 4.0A + //URL: http://publibz.boulder.ibm.com/epubs/pdf/iestcu02.pdf + LI := AItem as TIdVSELibraryFTPListItem; + LBuffer := LI.Data; + + AItem.FileName := Fetch(LBuffer); + Fetch(LBuffer, '>'); //This is always {do not localize} + LCols := TStringList.Create; + try + SplitDelimitedString(LBuffer, LCols, True); + //0 - number of members - used as file size when emulating Unix, I think + //1 - number of blocks + //2 - date + //3 - time + if LCols.Count > 0 then begin + LI.Size := IndyStrToInt64(LCols[0], 0); + end; + if LCols.Count > 1 then begin + LI.NumberBlocks := IndyStrToInt(LCols[1], 0); + end; + if LCols.Count > 2 then begin + LI.ModifiedDate := DateYYMMDD(Lcols[2]); + end; + if LCols.Count > 3 then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(Lcols[3]); + end; + //sublibraries are always types of directories + LI.ItemType := ditDirectory; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdFTPLPVSESubLibrary } + +class function TIdFTPLPVSESubLibrary.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +const + ValidEntry : array [0..1] of string = (' F',' S'); {Do not localize} + VSE_SUBLIBTYPES = 'FS'; {do not localize} +var + s : TStrings; + LData : String; +begin + Result := False; + if AListing.Count > 0 then + begin + LData := AListing[0]; + Result := (Length(LData) > 2) and + (PosInStrArray(Copy(LData, Length(LData)-1, 2), ValidEntry) > -1); + if Result then + begin + s := TStringList.Create; + try + SplitDelimitedString(LData, s, True); + Result := (s.Count > 4) and + (IndyPos('/', s[3]) > 0) and {do not localize} + (IndyPos(':', s[4]) > 0) and {do not localize} + CharIsInSet(s[s.Count-1], 1, VSE_SUBLIBTYPES); + finally + FreeAndNil(s); + end; + end; + end; +end; + +class function TIdFTPLPVSESubLibrary.GetIdent: String; +begin + Result := 'VSE: Sublibrary'; {do not localize} +end; + +class function TIdFTPLPVSESubLibrary.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVSESubLibraryFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVSESubLibrary.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; + LCols : TStrings; + LI : TIdVSESubLibraryFTPListItem; +begin + //Based on: TCP/IP for VSE User's Guide Version 1 Release 4.0A + //URL: http://publibz.boulder.ibm.com/epubs/pdf/iestcu02.pdf + LI := AItem as TIdVSESubLibraryFTPListItem; + LBuffer := AItem.Data; + if Length(LBuffer) < 2 then + begin + Result := False; + Exit; + end; + LBuffer := Copy(LBuffer, 1, Length(LBuffer)-1); + LCols := TStringList.Create; + try + SplitDelimitedString(LBuffer, LCols, True); + //0 - file name + //1 - records in file - might be reported as size in Unix emulation + //2 - number of library blocks + //3 - creation date + //4 - creation time + //5 - last modified date (may not be present) + //6 - last modified time (may not be present) + //sublibrary contents are always files + if LCols.Count >0 then begin + LI.FileName := LCols[0]; + end; + if LCols.Count >1 then + begin + LI.Size := IndyStrToInt64(LCols[1], 0); + LI.NumberRecs := AItem.Size; + end; + if LCols.Count > 2 then begin + LI.NumberBlocks := IndyStrToInt(LCols[2], 0); + end; + //creation time + if LCols.Count >3 then begin + LI.CreationDate := DateYYMMDD(LCols[3]); + end; + if LCols.Count > 4 then begin + LI.CreationDate := LI.CreationDate + TimeHHMMSS(LCols[4]); + end; + //modified time + if LCols.Count > 5 then begin + LI.ModifiedDate := DateYYMMDD(LCols[5]); + end else begin + LI.ModifiedDate := DateYYMMDD(LCols[3]); + end; + if LCols.Count > 6 then begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LCols[6]); + end else begin + LI.ModifiedDate := LI.ModifiedDate + TimeHHMMSS(LCols[4]); + end; + AItem.ItemType := ditFile; + finally + FreeAndNil(LCols); + end; + Result := True; +end; + +{ TIdVSEVTOCFTPListItem } + +constructor TIdVSEVTOCFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + SizeAvail := False; +end; + +initialization + RegisterFTPListParser(TIdFTPLPVSELibrary); + RegisterFTPListParser(TIdFTPLPVSEPowerQueue); + RegisterFTPListParser(TIdFTPLPVSERootDir); + RegisterFTPListParser(TIdFTPLPVSESubLibrary); + RegisterFTPListParser(TIdFTPLPVSEVSAMCatalog); + RegisterFTPListParser(TIdFTPLPVSEVTOC); +finalization + UnRegisterFTPListParser(TIdFTPLPVSELibrary); + UnRegisterFTPListParser(TIdFTPLPVSEPowerQueue); + UnRegisterFTPListParser(TIdFTPLPVSERootDir); + UnRegisterFTPListParser(TIdFTPLPVSESubLibrary); + UnRegisterFTPListParser(TIdFTPLPVSEVSAMCatalog); + UnRegisterFTPListParser(TIdFTPLPVSEVTOC); + +end. diff --git a/indy/Protocols/IdFTPListParseVxWorks.pas b/indy/Protocols/IdFTPListParseVxWorks.pas new file mode 100644 index 0000000..4437aca --- /dev/null +++ b/indy/Protocols/IdFTPListParseVxWorks.pas @@ -0,0 +1,147 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 10/26/2004 10:03:22 PM JPMugaas + Updated refs. + + Rev 1.3 4/19/2004 5:06:08 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.2 2004.02.03 5:45:40 PM czhower + Name changes + + Rev 1.1 10/19/2003 3:48:16 PM DSiders + Added localization comments. + + Rev 1.0 4/7/2003 04:10:34 PM JPMugaas + Renamed IdFTPListParseVsWorks. The s was a typo. + + Rev 1.0 2/19/2003 05:49:54 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseVxWorks; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +type + TIdVxWorksFTPListItem = class(TIdFTPListItem); + + TIdFTPLPVxWorks = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseVxWorks"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, SysUtils; + +{ TIdFTPLPVxWorks } + +class function TIdFTPLPVxWorks.GetIdent: String; +begin + Result := 'Wind River VxWorks'; {do not localize} +end; + +class function TIdFTPLPVxWorks.IsFooter(const AData: String): Boolean; +begin + {Not sure if the value string is in the FTP list + because I didn't see it first hand, it could've been a VxWorks command + prompt, but just in case.} + Result := TextStartsWith(AData, 'value'); {do not localize} +end; + +class function TIdFTPLPVxWorks.IsHeader(const AData: String): Boolean; +var + LCols : TStrings; +begin + Result := False; + LCols := TStringList.Create; + try + SplitDelimitedString(AData, LCols, True); + if LCols.Count > 3 then + begin + Result := (LCols[0] = 'size') and {do not localize} + (LCols[1] = 'date') and {do not localize} + (LCols[2] = 'time') and {do not localize} + (LCols[3] = 'name'); {do not localize} + end; + finally + FreeAndNil(LCols); + end; +end; + +class function TIdFTPLPVxWorks.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdVxWorksFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPVxWorks.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuffer : String; +begin + LBuffer := Trim(AItem.Data); + //Size + AItem.Size := IndyStrToInt64(Fetch(LBuffer), 0); + // date + LBuffer := TrimLeft(LBuffer); + AItem.ModifiedDate := DateStrMonthDDYY(Fetch(LBuffer)); + + // time + LBuffer := TrimLeft(LBuffer); + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(Fetch(LBuffer)); + + // item type + if TextEndsWith(LBuffer, '') then {do not localize} + begin + AItem.ItemType := ditDirectory; + LBuffer := Copy(LBuffer, 1, Length(LBuffer)-5); + end; + //I hope filenames and dirs don't start or end with a space + AItem.FileName := Trim(LBuffer); + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPVxWorks); +finalization + UnRegisterFTPListParser(TIdFTPLPVxWorks); + +end. diff --git a/indy/Protocols/IdFTPListParseWfFTP.pas b/indy/Protocols/IdFTPListParseWfFTP.pas new file mode 100644 index 0000000..68b4a7a --- /dev/null +++ b/indy/Protocols/IdFTPListParseWfFTP.pas @@ -0,0 +1,208 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 10/26/2004 11:21:16 PM JPMugaas + Updated refs. + + Rev 1.0 10/21/2004 10:27:32 PM JPMugaas +} + +unit IdFTPListParseWfFTP; + +{ + BayNetworks WfFTP FTP Server. WfFTP is a FTP interface for Bay Network's + Wellfleet router. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase, IdFTPListTypes; + +{ + WfFTP is a FTP interface for BayNetwork's Wellfleet Routers. + + Based on: + Configuration Guide + Contivity Secure IP Services Gateway + CG040301 + from Nortell Networks + Dated March 2004 + + Notation, the dir format is like this: + + === + + Volume - drive 1: + Directory of 1: + + File Name Size Date Day Time + ------------------------------------------------------ + startup.cfg 2116 03/06/03 Thur. 07:38:50 + configPppChap 2996 03/12/03 Wed. 16:43:58 + bgpOspf.log 32428 03/20/03 Thur. 13:08:26 + an.exe 7112672 03/20/03 Thur. 13:18:09 + bcc.help 492551 03/20/03 Thur. 13:21:43 + debug.al 12319 03/20/03 Thur. 13:22:46 + install.bat 236499 03/20/03 Thur. 13:22:54 + ti.cfg 132 03/20/03 Thur. 13:23:09 + log2.log 32428 03/20/03 Thur. 14:31:46 + configFrRip 386 07/18/03 Fri. 12:02:25 + config 1720 07/25/03 Fri. 08:52:00 + hosts 17 09/04/03 Thur. 15:56:51 + + 33554432 bytes - Total size + 25627726 bytes - Available free space + 17672120 bytes - Contiguous free space + 226 ASCII Transfer Complete. + === + === + + Volume - drive 2: + Directory of 2: + + File Name Size Date Day Time + ------------------------------------------------------ + config.isp 45016 08/22/97 Fri. 17:05:51 + startup.cfg 7472 08/24/97 Sun. 23:31:31 + asnboot.exe 237212 08/24/97 Sun. 23:31:41 + asndiag.exe 259268 08/24/97 Sun. 23:32:28 + debug.al 12372 08/24/97 Sun. 23:33:17 + ti_asn.cfg 504 08/24/97 Sun. 23:33:31 + install.bat 189114 08/24/97 Sun. 23:33:41 + config 50140 04/20/98 Mon. 22:08:01 + + 4194304 bytes - Total size + 3375190 bytes - Available free space + 3239088 bytes - Contiguous free space + + ==== + + From: http://www.insecure.org/sploits/bay-networks.baynets.html +} + +type + TIdWfFTPFTPListItem = class(TIdOwnerFTPListItem) + end; + + TIdFTPLPWfFTP = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + +const + WFFTP = 'WfFTP'; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseWfFTP"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobal, SysUtils; + +{ TIdFTPLPWfFTP } + +class function TIdFTPLPWfFTP.GetIdent: String; +begin + Result := WFFTP; +end; + +class function TIdFTPLPWfFTP.IsFooter(const AData: String): Boolean; +var + s : TStrings; +begin + Result := (IndyPos('bytes - Total size', AData) > 1) or {do not localize} + (IndyPos('bytes - Contiguous free space', AData) > 1) or {do not localize} + (IndyPos('bytes - Available free space', AData) > 1); {do not localize} + if not Result then + begin + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count = 6 then + begin + Result := (s[0] = 'File') and {do not localize} + (s[1] = 'Name') and {do not localize} + (s[2] = 'Size') and {do not localize} + (s[3] = 'Date') and {do not localize} + (s[4] = 'Day') and {do not localize} + (s[5] = 'Time'); {do not localize} + end; + finally + FreeAndNil(s); + end; + end; +end; + +class function TIdFTPLPWfFTP.IsHeader(const AData: String): Boolean; +begin + Result := TextStartsWith(AData, ' Volume - drive ') or TextStartsWith(AData, ' Directory of '); {Do not translate} +end; + +class function TIdFTPLPWfFTP.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdWfFTPFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPWfFTP.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LLine : String; +begin + Result := True; + //we'll assume that this is flat - not unusual in some routers + AItem.ItemType := ditFile; + //config 50140 04/20/98 Mon. 22:08:01 + LLine := AItem.Data; + //file name + AItem.FileName := Fetch(LLine); + //size + LLine := TrimLeft(LLine); + AItem.Size := IndyStrToInt64(Fetch(LLine), 0); + // date + LLine := TrimLeft(LLine); + AItem.ModifiedDate := DateMMDDYY(Fetch(LLine)); + //day of week - discard + LLine := TrimLeft(LLine); + Fetch(LLine); + LLine := TrimLeft(LLine); + //time + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(Fetch(LLine)); +end; + +initialization + RegisterFTPListParser(TIdFTPLPWfFTP); +finalization + UnRegisterFTPListParser(TIdFTPLPWfFTP); + +end. diff --git a/indy/Protocols/IdFTPListParseWinQVTNET.pas b/indy/Protocols/IdFTPListParseWinQVTNET.pas new file mode 100644 index 0000000..dba04d5 --- /dev/null +++ b/indy/Protocols/IdFTPListParseWinQVTNET.pas @@ -0,0 +1,149 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 10:03:22 PM JPMugaas + Updated refs. + + Rev 1.4 4/19/2004 5:05:42 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:26 PM czhower + Name changes + + Rev 1.2 1/22/2004 7:20:56 AM JPMugaas + System.Delete changed to IdDelete so the code can work in NET. + + Rev 1.1 10/19/2003 3:48:20 PM DSiders + Added localization comments. + + Rev 1.0 2/19/2003 05:49:50 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseWinQVTNET; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +{ + This was tested with data obtained from WinQVT/Net Version 3.98.15 running + on Windows 2000. + + No parser is required for later versions because those use the Unix listing + format. +} + +type + TIdWinQVNetFTPListItem = class(TIdFTPListItem); + + TIdFTPLPWinQVNet = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseWinQVTNET"'} + {$ENDIF} + +implementation + +uses + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ TIdFTPLPWinQVNet } + +class function TIdFTPLPWinQVNet.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + LData : String; +begin + Result := False; + + if AListing.Count > 0 then + { + test.txt 0 10-23-2003 01:01 + 123456789012345678901234567890123456789012345678901234567890 + 1 2 3 4 5 6 + } + begin + LData := AListing[0]; + Result := (Copy(LData, 38, 1) = '-') and {do not localize} + (Copy(LData, 41, 1) = '-') and {do not localize} + (Copy(LData, 49, 1) = ':') and {do not localize} + IsMMDDYY(Copy(LData, 36, 10), '-') and {do not localize} + (Copy(LData, 46, 1) = ' ') and {do not localize} + IsHHMMSS(Copy(LData, 47, 5), ':'); {do not localize} + end; +end; + +class function TIdFTPLPWinQVNet.GetIdent: String; +begin + Result := 'WinQVT/NET'; {do not localize} +end; + +class function TIdFTPLPWinQVNet.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdWinQVNetFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPWinQVNet.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; +begin + //filename (note that it can contain spaces on WinNT with my test case + AItem.FileName := ExtractQVNETFileName(AItem.Data); + LBuf := AItem.Data; + //item type + if IndyPos('/', Copy(LBuf, 1, 13)) > 0 then begin {do not localize} + AItem.ItemType := ditDirectory; + end; + IdDelete(LBuf, 1, 13); + LBuf := TrimLeft(LBuf); + //Size + AItem.Size := IndyStrToInt64(Fetch(LBuf), 0); + //Date + LBuf := TrimLeft(LBuf); + AItem.ModifiedDate := DateMMDDYY(Fetch(LBuf)); + //Time + LBuf := Trim(LBuf); + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LBuf); + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPWinQVNet); +finalization + UnRegisterFTPListParser(TIdFTPLPWinQVNet); + +end. diff --git a/indy/Protocols/IdFTPListParseWindowsNT.pas b/indy/Protocols/IdFTPListParseWindowsNT.pas new file mode 100644 index 0000000..f0eafe5 --- /dev/null +++ b/indy/Protocols/IdFTPListParseWindowsNT.pas @@ -0,0 +1,438 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.11 2/16/2005 7:26:52 AM JPMugaas + Should handle Microsoft IIS on Windows XP Professional if the + FtpDirBrowseShowLongDate metadata is enabled. That causes digit years to be + outputted instead of two digit years. + + Rev 1.10 10/26/2004 10:03:22 PM JPMugaas + Updated refs. + + Rev 1.9 9/7/2004 10:01:12 AM JPMugaas + FIxed problem parsing: + + drwx------ 1 user group 0 Sep 07 09:20 xxx + + It was mistakenly being detected as Windows NT because there was a - in the + fifth and eigth position in the string. The fix is to detect to see if the + other chactors in thbat column are numbers. + + I did the same thing to the another part of the detection so that something + similar doesn't happen there with "-" in Unix listings causing false + WindowsNT detection. + + Rev 1.8 6/5/2004 3:02:10 PM JPMugaas + Indicates SizeAvail = False for a directory. That is the standard MS-DOS + Format. + + Rev 1.7 4/20/2004 4:01:14 PM JPMugaas + Fix for nasty typecasting error. The wrong create was being called. + + Rev 1.6 4/19/2004 5:05:16 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.5 2004.02.03 5:45:16 PM czhower + Name changes + + Rev 1.4 1/22/2004 4:56:12 PM SPerry + fixed set problems + + Rev 1.3 1/22/2004 7:20:54 AM JPMugaas + System.Delete changed to IdDelete so the code can work in NET. + + Rev 1.2 10/19/2003 3:48:16 PM DSiders + Added localization comments. + + Rev 1.1 9/27/2003 10:45:50 PM JPMugaas + Added support for an alternative date format. + + Rev 1.0 2/19/2003 02:01:54 AM JPMugaas + Individual parsing objects for the new framework. +} + +unit IdFTPListParseWindowsNT; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFTPList, IdFTPListParseBase; + +{ + Note: + This parser comes from the code in Indy 9.0's MS-DOS parser. + It has been renamed Windows NT here because that is more accurate than + the old name. + + This is really the standard Microsoft IIS FTP Service format. We have + tested this parser with Windows NT 4.0, Windows 2000, and Windows XP. + + This parser also handles recursive dir lists. +} + +type + TIdWindowsNTFTPListItem = class(TIdFTPListItem); + + TIdFTPLPWindowsNT = class(TIdFTPListBase) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; + class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; + end; + +const + WINNTID = 'Windows NT'; {do not localize} + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseWindowsNT"'} + {$ENDIF} + +{ +Thanks to Craig Peterson of Scooter Software for his verison of +TIdFTPLPWindowsNT.CheckListing. +} +implementation + +uses + IdException, + IdGlobal, IdFTPCommon, IdGlobalProtocols, + SysUtils; + +{ TIdFTPLPWindowsNT } + +{ +IMPORTANT!!! + +This parser actually handles some variations in the IIS FTP Server. In addition, +it also handles some similar formats such as one found in Windows CE and in Rhinosoft's +-h:DOS DIR parameter. + +To do all of this, the detector routine must use string positions because there +are some relatively similar but unrelated patterns. + +Since this is such a highly used parser, it's a good idea to have +raw directory lines in comments with the line position 1 so that it could be +cut and pasted into a test program. In addition, I use something like +" + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 +" +above a section so that we more easily see the column positions in the string. +The header is not part of the actual listing. +} +class function TIdFTPLPWindowsNT.CheckListing(AListing: TStrings; + const ASysDescript: String; const ADetails: Boolean): Boolean; +var + SDir, sSize : String; + i : Integer; + SData : String; +begin + + //maybe, we are dealing with this pattern +{ + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 +2002-09-02 18:48 DOS dir 2 +} + // + //or +{ + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 +2002-09-02 19:06 9,730 DOS file 2 +} + // + // + //Those were obtained from soem comments in some FileZilla source-code. + //FtpListResult.cpp + //Note that none of that GNU source-code was used. + // + //I personally came accross the following when on Microsoft IIS + //FTP Service on WIndowsXP Pro when I enabled the "FtpDirBrowseShowLongDate" + //metadata property. + // +{ + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 + +02-16-2005 04:16AM pub +} + //Also, this really should cover a dir form that might be used on some FTP servers. + //Serv-U uses this if you specify -h:"DOS" when retreiving a DIR with LIST: + // +{ + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 + +09/09/2008 03:51 PM . +09/09/2008 03:51 PM .. +04/29/2008 01:39 PM 802 00index.txt +09/09/2008 03:51 PM allegrosurf +09/09/2008 03:51 PM FTP Voyager SDK +09/09/2008 03:51 PM ftptree +09/09/2008 03:51 PM ftpvoyager +09/22/2008 10:28 AM OpenSSL +09/09/2008 03:51 PM serv-u +09/09/2008 03:51 PM VISIT www.RhinoSoft.com +09/09/2008 03:51 PM WinKey +09/09/2008 03:51 PM zaep +} + +{Some Windows CE servers might return something like this: +{ + + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 +" +01-01-98 08:00AM Flash File Store +01-01-98 08:00AM SDMMC Disk +06-26-06 10:49AM install +06-21-06 01:59PM 1033 GACLOG.TXT +06-21-06 12:32PM 12 iqdbsett.iqd +03-21-03 04:02AM SmartSystems +03-21-03 04:00AM ftpdcmds +03-21-03 04:00AM ConnMgr +03-21-03 04:00AM CabFiles +03-20-03 07:59PM profiles +03-20-03 07:59PM Program Files +03-20-03 07:59PM My Documents +03-20-03 07:59PM Temp +03-20-03 07:59PM Windows +" +} + +{Treck Embedded FTP (treck.com) right-justifies : + + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 + +07-24-10 10:12PM FOUND.000 +03-23-10 02:28PM 28674 4500PMOD.ZIP +} + Result := False; + for i := 0 to AListing.Count - 1 do begin + if (AListing[i] <> '') and (not IsSubDirContentsBanner(AListing[i])) then begin + SData := UpperCase(AListing[i]); + if IndyPos(' ', SData) in [22..26] then begin {do not localize} + sDir := ''; {do not localize} + end; + sSize := Copy(SData, 20, 19); +{ Handle Windows CE listings with 2 less spaces for sizes + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 + +04-15-09 07:44 4608 AxcessE.exe +} + if (Length(sSize) = 19) and IsNumeric(sSize[17]) and (sSize[18] = ' ') then begin + SetLength(sSize, 17); + end; + sSize := ReplaceAll(TrimLeft(sSize), ',', ''); + //VM/BFS does share the date/time format as MS-DOS for the first two columns + // if ((CharIsInSet(SData, 3, ['/', '-'])) and (CharIsInSet(SData, 6, ['/', '-']))) then + if IsMMDDYY(Copy(SData, 1, 8), '-') or IsMMDDYY(Copy(SData, 1, 8), '/') then begin + if sDir = '' then begin {do not localize} + Result := not IsVMBFS(SData); + end else begin + if (sDir = '') and (IndyStrToInt64(sSize, -1) <> -1) then begin + //may be a file - see if we can get the size if sDir is empty + Result := not IsVMBFS(SData); + end; + end; + end + else if IsYYYYMMDD(SData) then begin + if sDir = '' then begin {do not localize} + Result := not IsVMBFS(SData); + end + else if (sDir = '') and (IndyStrToInt64(sSize, -1) <> -1) then begin {Do not Localize} + //may be a file - see if we can get the size if sDir is empty + Result := not IsVMBFS(SData); + end; + end + else if IsMMDDYY(SData, '-') then begin {do not localize} +{ +It might be like this: + + 1 2 3 4 5 +1234567890123456789012345678901234567890123456789012345678901234567890 + +02-16-2005 04:16AM pub +02-14-2005 07:22AM 9112103 ethereal-setup-0.10.9.exe + } + if sDir = '' then begin {do not localize} + Result := not IsVMBFS(SData); + end + else if (sDir = '') and (IndyStrToInt64(sSize, -1) <> -1) then begin {Do not Localize} + Result := not IsVMBFS(SData); + end; + end; + end; + end; +end; + + +class function TIdFTPLPWindowsNT.GetIdent: String; +begin + Result := WINNTID; +end; + +class function TIdFTPLPWindowsNT.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdWindowsNTFTPListItem.Create(AOwner); +end; + +class function TIdFTPLPWindowsNT.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LModified: string; + LTime: string; + LName: string; + LValue: string; + LBuffer: string; + LPosMarker : Integer; +begin + LPosMarker := 1; + //Note that there is quite a bit of duplicate code in this if. + //That is because there are two similar forms but the dates are in + //different forms and have to be processed differently. + if IsNumeric(AItem.Data, 4) and (not IsNumeric(AItem.Data, 1, 5)) then begin + LModified := Copy(AItem.Data, 1, 4) + '/' + {do not localize} + Copy(AItem.Data, 6, 2) + '/' + {do not localize} + Copy(AItem.Data, 9, 2) + ' '; {do not localize} + + LBuffer := Trim(Copy(AItem.Data, 11, MaxInt)); + // Scan time info + LTime := Fetch(LBuffer); + // Scan optional letter in a[m]/p[m] + LModified := LModified + LTime; + // Convert modified to date time + try + AItem.ModifiedDate := DateYYMMDD(Fetch(LModified)); + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LModified); + except + AItem.ModifiedDate := 0.0; + end; + end else begin + LBuffer := AItem.Data; + //get the date + LModified := Fetch(LBuffer); + LBuffer := TrimLeft(LBuffer); + // Scan time info + LTime := Fetch(LBuffer); + // Scan optional letter in a[m]/p[m] + LModified := LModified + ' ' + LTime; {do not localize} + // Convert modified to date time + try + AItem.ModifiedDate := DateMMDDYY(Fetch(LModified)); + AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(LModified); + except + AItem.ModifiedDate := 0.0; + end; + end; + + repeat + LBuffer := Trim(LBuffer); + + // Scan file size or dir marker + LValue := Fetch(LBuffer); + + // Strip commas or StrToInt64Def will barf + if IndyPos(',', LValue) <> 0 then begin {Do not Localize} + LValue := ReplaceAll(LValue, ',', ''); {Do not Localize} + end; + // What did we get? + if TextIsSame(LValue, '') then begin {Do not Localize} + AItem.ItemType := ditDirectory; + //must contain 17 spaces for WinCE pattern + if TextStartsWith(LBuffer,' ') then begin + LPosMarker := 18; + //Treck FTP server doesn't do any padding; all others must contain 9 spaces + end else if TextStartsWith(LBuffer,' ') then begin + LPosMarker := 10; + end; + AItem.SizeAvail := False; + Break; + end else begin + if not TextIsSame(LValue, 'AM') then begin + if TextIsSame(LValue, 'PM') then begin + AItem.ModifiedDate := AItem.ModifiedDate + EncodeTime(12,0,0,0); + end else begin + AItem.ItemType := ditFile; + AItem.Size := IndyStrToInt64(LValue, 0); + break; + end; + end; + end; + until False; + + // Rest of the buffer is item name + AItem.LocalFileName := LName; + LName := Copy(LBuffer, LPosMarker, MaxInt); + if APath <> '' then begin + //MS_DOS_CURDIR + AItem.LocalFileName := LName; + LName := APath + PATH_FILENAME_SEP_DOS + LName; + if TextStartsWith(LName, MS_DOS_CURDIR) then begin + IdDelete(LName, 1, Length(MS_DOS_CURDIR)); + end; + end; + AItem.FileName := LName; + Result := True; +end; + +class function TIdFTPLPWindowsNT.ParseListing(AListing: TStrings; + ADir: TIdFTPListItems): Boolean; +var + i : Integer; + LPathSpec : String; + LItem : TIdFTPListItem; +begin + for i := 0 to AListing.Count -1 do begin + if AListing[i] <> '' then begin + if IsSubDirContentsBanner(AListing[i]) then begin + LPathSpec := Copy(AListing[i], 1, Length(AListing[i])-1); + end else begin + LItem := MakeNewItem(ADir); + LItem.Data := AListing[i]; + Result := ParseLine(LItem, LPathSpec); + if not Result then begin + FreeAndNil(LItem); + Exit; + end + end; + end; + end; + Result := True; +end; + +initialization + RegisterFTPListParser(TIdFTPLPWindowsNT); +finalization + UnRegisterFTPListParser(TIdFTPLPWindowsNT); + +end. diff --git a/indy/Protocols/IdFTPListParseXecomMicroRTOS.pas b/indy/Protocols/IdFTPListParseXecomMicroRTOS.pas new file mode 100644 index 0000000..a22e629 --- /dev/null +++ b/indy/Protocols/IdFTPListParseXecomMicroRTOS.pas @@ -0,0 +1,178 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 10:03:24 PM JPMugaas + Updated refs. + + Rev 1.4 4/19/2004 5:05:40 PM JPMugaas + Class rework Kudzu wanted. + + Rev 1.3 2004.02.03 5:45:26 PM czhower + Name changes + + Rev 1.2 10/19/2003 3:48:22 PM DSiders + Added localization comments. + + Rev 1.1 4/7/2003 04:04:42 PM JPMugaas + User can now descover what output a parser may give. + + Rev 1.0 2/19/2003 05:49:46 PM JPMugaas + Parsers ported from old framework. +} + +unit IdFTPListParseXecomMicroRTOS; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList, IdFTPListParseBase; + +type + TIdXecomMicroRTOSTPListItem = class(TIdFTPListItem) + protected + FMemStart: UInt32; + FMemEnd: UInt32; + public + constructor Create(AOwner: TCollection); override; + property MemStart: UInt32 read FMemStart write FMemStart; + property MemEnd: UInt32 read FMemEnd write FMemEnd; + end; + + TIdFTPLPXecomMicroRTOS = class(TIdFTPListBaseHeader) + protected + class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; + class function IsHeader(const AData: String): Boolean; override; + class function IsFooter(const AData : String): Boolean; override; + class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; + public + class function GetIdent : String; override; + end; + + // RLebeau 2/14/09: this forces C++Builder to link to this unit so + // RegisterFTPListParser can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdFTPListParseXecomMicroRTOS"'} + {$ENDIF} + +implementation + +uses + IdFTPCommon, IdGlobalProtocols, IdStrings, + SysUtils; + +{ TIdFTPLPXecomMicroRTOS } + +class function TIdFTPLPXecomMicroRTOS.GetIdent: String; +begin + Result := 'Xercom Micro RTOS'; {do not localize} +end; + +class function TIdFTPLPXecomMicroRTOS.IsFooter(const AData: String): Boolean; +var + s : TStrings; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count = 7 then + begin + Result := (s[0] = '**') and {do not localize} + (s[1] = 'Total') and {do not localize} + IsNumeric(s[2]) and + (s[3] = 'files,') and {do not localize} + IsNumeric(s[4]) and + (s[5] = 'bytes') and {do not localize} + (s[6] = '**'); {do not localize} + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPXecomMicroRTOS.IsHeader(const AData: String): Boolean; +var + s : TStrings; +begin + Result := False; + s := TStringList.Create; + try + SplitDelimitedString(AData, s, True); + if s.Count = 5 then + begin + Result := (s[0] = 'Start') and {do not localize} + (s[1] = 'End') and {do not localize} + (s[2] = 'length') and {do not localize} + (s[3] = 'File') and {do not localize} + (s[4] = 'name'); {do not localize} + end; + finally + FreeAndNil(s); + end; +end; + +class function TIdFTPLPXecomMicroRTOS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; +begin + Result := TIdXecomMicroRTOSTPListItem.Create(AOwner); +end; + +class function TIdFTPLPXecomMicroRTOS.ParseLine(const AItem: TIdFTPListItem; + const APath: String): Boolean; +var + LBuf : String; + LI : TIdXecomMicroRTOSTPListItem; +begin + LI := AItem as TIdXecomMicroRTOSTPListItem; + LBuf := TrimLeft(AItem.Data); + //start memory offset + LBuf := TrimLeft(LBuf); + LI.MemStart := IndyStrToInt('$'+Fetch(LBuf), 0); + //end memory offset + LBuf := TrimLeft(LBuf); + LI.MemEnd := IndyStrToInt('$'+Fetch(LBuf),0); + //file size + LBuf := TrimLeft(LBuf); + LI.Size := IndyStrToInt64(Fetch(LBuf), 0); + //File name + LI.FileName := TrimLeft(LBuf); + //note that the date is not provided and I do not think there are + //dirs in this real-time operating system. + Result := True; +end; + +{ TIdXecomMicroRTOSTPListItem } + +constructor TIdXecomMicroRTOSTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + ModifiedAvail := False; +end; + +initialization + RegisterFTPListParser(TIdFTPLPXecomMicroRTOS); +finalization + UnRegisterFTPListParser(TIdFTPLPXecomMicroRTOS); + +end. diff --git a/indy/Protocols/IdFTPListTypes.pas b/indy/Protocols/IdFTPListTypes.pas new file mode 100644 index 0000000..c459d2c --- /dev/null +++ b/indy/Protocols/IdFTPListTypes.pas @@ -0,0 +1,663 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 3/23/2005 4:52:28 AM JPMugaas + Expansion with MLSD and WIN32.ea fact in MLSD directories as described by: + + http://www.raidenftpd.com/kb/kb000000049.htm + + This returns Win32 file attributes including some that Borland does not + support. + + Rev 1.5 12/8/2004 8:35:18 AM JPMugaas + Minor class restructure to support Unisys ClearPath. + + Rev 1.4 11/29/2004 2:45:30 AM JPMugaas + Support for DOS attributes (Read-Only, Archive, System, and Hidden) for use + by the Distinct32, OS/2, and Chameleon FTP list parsers. + + Rev 1.3 10/26/2004 9:27:34 PM JPMugaas + Updated references. + + Rev 1.2 6/27/2004 1:45:36 AM JPMugaas + Can now optionally support LastAccessTime like Smartftp's FTP Server could. + I also made the MLST listing object and parser support this as well. + + Rev 1.1 6/4/2004 2:11:00 PM JPMugaas + Added an indexed read-only Facts property to the MLST List Item so you can + get information that we didn't parse elsewhere. MLST is extremely flexible. + + Rev 1.0 4/20/2004 2:43:20 AM JPMugaas + Abstract FTPList objects for reuse. +} + +unit IdFTPListTypes; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdFTPList; + +type + { For FTP servers using OS/2 and other MS-DOS-like file systems that report file attributes } + TIdDOSAttributes = class(TPersistent) + protected + FFileAttributes: UInt32; + function GetRead_Only: Boolean; + procedure SetRead_Only(const AValue: Boolean); + function GetHidden: Boolean; + procedure SetHidden(const AValue: Boolean); + function GetSystem: Boolean; + procedure SetSystem(const AValue: Boolean); + function GetArchive: Boolean; + procedure SetArchive(const AValue: Boolean); + function GetDirectory: Boolean; + procedure SetDirectory(const AValue: Boolean); + function GetNormal: Boolean; + procedure SetNormal(const AValue: Boolean); + public + procedure Assign(Source: TPersistent); override; + function GetAsString: String; virtual; + function AddAttribute(const AString : String) : Boolean; + published + property FileAttributes : UInt32 read FFileAttributes write FFileAttributes; + property AsString : String read GetAsString; + // can't be ReadOnly because that's a reserved word + property Read_Only : Boolean read GetRead_Only write SetRead_Only; + property Archive : Boolean read GetArchive write SetArchive; + property System : Boolean read GetSystem write SetSystem; + property Directory : Boolean read GetDirectory write SetDirectory; + property Hidden : Boolean read GetHidden write SetHidden; + property Normal : Boolean read GetNormal write SetNormal; + end; + + { Win32 Extended Attributes as in WIN32_FIND_DATA data structure in the Windows API. + Analagous to the System.IO.FileAttributes enumeration in .Net } + TIdWin32ea = class(TIdDOSAttributes) + protected + function GetDevice: Boolean; + procedure SetDevice(const AValue: Boolean); + function GetTemporary: Boolean; + procedure SetTemporary(const AValue: Boolean); + function GetSparseFile: Boolean; + procedure SetSparseFile(const AValue: Boolean); + // this is also called a junction and it works like a Unix Symbolic link to a dir + function GetReparsePoint: Boolean; + procedure SetReparsePoint(const AValue: Boolean); + function GetCompressed: Boolean; + procedure SetCompressed(const AValue: Boolean); + function GetOffline: Boolean; + procedure SetOffline(const AValue: Boolean); + function GetNotContextIndexed: Boolean; + procedure SetNotContextIndexed(const AValue: Boolean); + function GetEncrypted: Boolean; + procedure SetEncrypted(const AValue: Boolean); + public + function GetAsString: String; override; + published + property Device : Boolean read GetDevice write SetDevice; + property Temporary : Boolean read GetTemporary write SetTemporary; + property SparseFile : Boolean read GetSparseFile write SetSparseFile; + property ReparsePoint : Boolean read GetReparsePoint write SetReparsePoint; + property Compressed : Boolean read GetCompressed write SetCompressed; + property Offline : Boolean read GetOffline write SetOffline; + property NotContextIndexed : Boolean read GetNotContextIndexed write SetNotContextIndexed; + property Encrypted : Boolean read GetEncrypted write SetEncrypted; + end; + + //For NLST and Cisco IOS + TIdMinimalFTPListItem = class(TIdFTPListItem) + public + constructor Create(AOwner: TCollection); override; + end; + + //This is for some mainframe items which are based on records + TIdRecFTPListItem = class(TIdFTPListItem) + protected + //These are for VM/CMS which uses a record type of file system + FRecLength : Integer; + FRecFormat : String; + FNumberRecs : Integer; + property RecLength : Integer read FRecLength write FRecLength; + property RecFormat : String read FRecFormat write FRecFormat; + property NumberRecs : Integer read FNumberRecs write FNumberRecs; + end; + + { listing formats that include Creation timestamp information } + TIdCreationDateFTPListItem = class(TIdFTPListItem) + protected + FCreationDate: TDateTime; + public + constructor Create(AOwner: TCollection); override; + property CreationDate: TDateTime read FCreationDate write FCreationDate; + + end; + + // for MLST, MLSD listing outputs + TIdMLSTFTPListItem = class(TIdCreationDateFTPListItem) + protected + FAttributesAvail : Boolean; + FAttributes : TIdWin32ea; + FCreationDateGMT : TDateTime; + FLastAccessDate: TDateTime; + FLastAccessDateGMT : TDateTime; + FLinkedItemName : String; + //Unique ID for an item to prevent yourself from downloading something twice + FUniqueID : String; + //MLIST things + FMLISTPermissions : String; + function GetFact(const AName : String) : String; + public + constructor Create(AOwner: TCollection); override; + destructor Destroy; override; + //Creation time values are for MLSD data output and can be returned by the + //the MLSD parser in some cases + property ModifiedDateGMT; + property CreationDateGMT : TDateTime read FCreationDateGMT write FCreationDateGMT; + + property LastAccessDate: TDateTime read FLastAccessDate write FLastAccessDate; + property LastAccessDateGMT : TDateTime read FLastAccessDateGMT write FLastAccessDateGMT; + + //Valid only with EPLF and MLST + property UniqueID : string read FUniqueID write FUniqueID; + //MLIST Permissions + property MLISTPermissions : string read FMLISTPermissions write FMLISTPermissions; + property Facts[const Name: string] : string read GetFact; + property AttributesAvail : Boolean read FAttributesAvail write FAttributesAvail; + property Attributes : TIdWin32ea read FAttributes; + property LinkedItemName : String read FLinkedItemName write FLinkedItemName; + end; + + //for some parsers that output an owner sometimes + TIdOwnerFTPListItem = class(TIdFTPListItem) + protected + FOwnerName : String; + public + property OwnerName : String read FOwnerName write FOwnerName; + end; + + { This class type is used by Novell Netware, Novell Print Services for + Unix with DOS namespace, and HellSoft FTPD for Novell Netware } + TIdNovellBaseFTPListItem = class(TIdOwnerFTPListItem) + protected + FNovellPermissions : String; + public + property NovellPermissions : string read FNovellPermissions write FNovellPermissions; + end; + + //Bull GCOS 8 uses this and Unix will use a descendent + TIdUnixPermFTPListItem = class(TIdOwnerFTPListItem) + protected + FUnixGroupPermissions: string; + FUnixOwnerPermissions: string; + FUnixOtherPermissions: string; + public + property UnixOwnerPermissions: string read FUnixOwnerPermissions write FUnixOwnerPermissions; + property UnixGroupPermissions: string read FUnixGroupPermissions write FUnixGroupPermissions; + property UnixOtherPermissions: string read FUnixOtherPermissions write FUnixOtherPermissions; + end; + + // Unix and Novell Netware Print Services for Unix with NFS namespace need to use this + TIdUnixBaseFTPListItem = class(TIdUnixPermFTPListItem) + protected + FLinkCount: Integer; + FGroupName: string; + FLinkedItemName : string; + public + property LinkCount: Integer read FLinkCount write FLinkCount; + property GroupName: string read FGroupName write FGroupName; + property LinkedItemName : string read FLinkedItemName write FLinkedItemName; + end; + + TIdDOSBaseFTPListItem = class(TIdFTPListItem) + protected + FAttributes : TIdDOSAttributes; + procedure SetAttributes(AAttributes : TIdDOSAttributes); + public + constructor Create(AOwner: TCollection); override; + destructor Destroy; override; + property Attributes : TIdDOSAttributes read FAttributes write SetAttributes; + end; + +{These are needed for interpretting Win32.ea in some MLSD output} +const + IdFILE_ATTRIBUTE_READONLY = $00000001; + IdFILE_ATTRIBUTE_HIDDEN = $00000002; + IdFILE_ATTRIBUTE_SYSTEM = $00000004; + IdFILE_ATTRIBUTE_DIRECTORY = $00000010; + IdFILE_ATTRIBUTE_ARCHIVE = $00000020; + IdFILE_ATTRIBUTE_DEVICE = $00000040; + IdFILE_ATTRIBUTE_NORMAL = $00000080; + IdFILE_ATTRIBUTE_TEMPORARY = $00000100; + IdFILE_ATTRIBUTE_SPARSE_FILE = $00000200; + IdFILE_ATTRIBUTE_REPARSE_POINT = $00000400; + IdFILE_ATTRIBUTE_COMPRESSED = $00000800; + IdFILE_ATTRIBUTE_OFFLINE = $00001000; + IdFILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $00002000; + IdFILE_ATTRIBUTE_ENCRYPTED = $00004000; + +implementation + +uses + IdException, + IdFTPCommon, SysUtils; + +{ TIdMinimalFTPListItem } + +constructor TIdMinimalFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FSizeAvail := False; + FModifiedAvail := False; +end; + +{ TIdMLSTFTPListItem } + +constructor TIdMLSTFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FAttributesAvail := False; + FAttributes := TIdWin32ea.Create; +end; + +destructor TIdMLSTFTPListItem.Destroy; +begin + FreeAndNil(FAttributes); + inherited Destroy; +end; + +function TIdMLSTFTPListItem.GetFact(const AName: String): String; +var + LFacts : TStrings; +begin + LFacts := TStringList.Create; + try + ParseFacts(Data, LFacts); + Result := LFacts.Values[AName]; + finally + FreeAndNil(LFacts); + end; +end; + +{ TIdDOSBaseFTPListItem } + +constructor TIdDOSBaseFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + FAttributes := TIdDOSAttributes.Create; +end; + +destructor TIdDOSBaseFTPListItem.Destroy; +begin + FreeAndNil(FAttributes); + inherited Destroy; +end; + +procedure TIdDOSBaseFTPListItem.SetAttributes( + AAttributes: TIdDOSAttributes); +begin + FAttributes.Assign(AAttributes); +end; + +{ TIdDOSAttributes } + +function TIdDOSAttributes.AddAttribute(const AString: String): Boolean; +var + i : Integer; + S: String; +begin + S := UpperCase(AString); + for i := 1 to Length(S) do begin + case CharPosInSet(S, i, 'RASHW-D') of + //R + 1 : Read_Only := True; + //A + 2 : Archive := True; + //S + 3 : System := True; + //H + 4 : Hidden := True; + //W - W was added only for Distinct32's FTP server which reports 'w' if you + //write instead of a r for read-only + 5 : Read_Only := False; + 6,7 : ;//for the "-" and "d" that Distinct32 may give + else + begin + Result := False; //failure + Exit; + end; + end; + end; + Result := True; +end; + +procedure TIdDOSAttributes.Assign(Source: TPersistent); +begin + if Source is TIdDOSAttributes then begin + FFileAttributes := (Source as TIdDOSAttributes).FFileAttributes; + end else begin + inherited Assign(Source); + end; +end; + +function TIdDOSAttributes.GetAsString: String; +//This is just a handy thing for some programs to try +//to output attribute bits similarly to the DOS +//ATTRIB command +// +//which is like this: +// +// R C:\File +//A SH C:\File +{$IFDEF STRING_IS_IMMUTABLE} +var + LSB: TIdStringBuilder; +{$ENDIF} +begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(' '); + {$ELSE} + Result := ' '; + {$ENDIF} + if Archive then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[0]{$ELSE}Result[1]{$ENDIF} := 'A'; {do not localize} + end; + if System then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[3]{$ELSE}Result[4]{$ENDIF} := 'S'; {do not localize} + end; + if Hidden then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[4]{$ELSE}Result[5]{$ENDIF} := 'H'; {do not localize} + end; + if Read_Only then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[5]{$ELSE}Result[6]{$ENDIF} := 'R'; {do not localize} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +function TIdDOSAttributes.GetRead_Only: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_READONLY) > 0; +end; + +function TIdDOSAttributes.GetHidden: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_HIDDEN) > 0; +end; + +function TIdDOSAttributes.GetSystem: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_SYSTEM) > 0; +end; + +function TIdDOSAttributes.GetDirectory: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_DIRECTORY) > 0; +end; + +function TIdDOSAttributes.GetArchive: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_ARCHIVE) > 0; +end; + +function TIdDOSAttributes.GetNormal: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_NORMAL) > 0; +end; + +procedure TIdDOSAttributes.SetRead_Only(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_READONLY; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_READONLY); + end; +end; + +procedure TIdDOSAttributes.SetHidden(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_HIDDEN; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_HIDDEN); + end; +end; + +procedure TIdDOSAttributes.SetSystem(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_SYSTEM; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_SYSTEM); + end; +end; + +procedure TIdDOSAttributes.SetDirectory(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_DIRECTORY; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_DIRECTORY); + end; +end; + +procedure TIdDOSAttributes.SetArchive(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_ARCHIVE; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_ARCHIVE); + end; +end; + +procedure TIdDOSAttributes.SetNormal(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_NORMAL; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_NORMAL); + end; +end; + +{ TIdCreationDateFTPListItem } + +constructor TIdCreationDateFTPListItem.Create(AOwner: TCollection); +begin + inherited Create(AOwner); + SizeAvail := False; + ModifiedAvail := False; +end; + +{ TIdWin32ea } + +function TIdWin32ea.GetDevice: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_DEVICE) > 0; +end; + +function TIdWin32ea.GetTemporary: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_TEMPORARY) > 0; +end; + +function TIdWin32ea.GetSparseFile: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_SPARSE_FILE) > 0; +end; + +function TIdWin32ea.GetReparsePoint: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_REPARSE_POINT) > 0; +end; + +function TIdWin32ea.GetCompressed: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_COMPRESSED) > 0; +end; + +function TIdWin32ea.GetOffline: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_OFFLINE) > 0; +end; + +function TIdWin32ea.GetNotContextIndexed: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_NOT_CONTENT_INDEXED) > 0; +end; + +function TIdWin32ea.GetEncrypted: Boolean; +begin + Result := (FFileAttributes and IdFILE_ATTRIBUTE_ENCRYPTED) > 0; +end; + +procedure TIdWin32ea.SetDevice(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_DEVICE; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_DEVICE); + end; +end; + +procedure TIdWin32ea.SetTemporary(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_TEMPORARY; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_TEMPORARY); + end; +end; + +procedure TIdWin32ea.SetSparseFile(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_SPARSE_FILE; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_SPARSE_FILE); + end; +end; + +procedure TIdWin32ea.SetReparsePoint(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_NORMAL; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_NORMAL); + end; +end; + +procedure TIdWin32ea.SetCompressed(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_NORMAL; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_NORMAL); + end; +end; + +procedure TIdWin32ea.SetOffline(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_OFFLINE; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_OFFLINE); + end; +end; + +procedure TIdWin32ea.SetNotContextIndexed(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_NOT_CONTENT_INDEXED); + end; +end; + +procedure TIdWin32ea.SetEncrypted(const AValue: Boolean); +begin + if AValue then begin + FFileAttributes := FFileAttributes or IdFILE_ATTRIBUTE_ENCRYPTED; + end else begin + FFileAttributes := FFileAttributes and (not IdFILE_ATTRIBUTE_ENCRYPTED); + end; +end; + +function TIdWin32ea.GetAsString: String; +//we'll do this similarly to 4NT +//which renders the bits like this order: +//RHSADENTJPCOI +{$IFDEF STRING_IS_IMMUTABLE} +var + LSB: TIdStringBuilder; +{$ENDIF} +begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(' '); + {$ELSE} + Result := ' '; + {$ENDIF} + if Read_Only then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[0]{$ELSE}Result[1]{$ENDIF} := 'R'; {do not localize} + end; + if Hidden then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[1]{$ELSE}Result[2]{$ENDIF} := 'H'; {do not localize} + end; + if System then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[2]{$ELSE}Result[3]{$ENDIF} := 'S'; {do not localize} + end; + if Archive then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[3]{$ELSE}Result[4]{$ENDIF} := 'A'; {do not localize} + end; + if Directory then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[4]{$ELSE}Result[5]{$ENDIF} := 'D'; {do not localize} + end; + if Encrypted then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[5]{$ELSE}Result[6]{$ENDIF} := 'E'; {do not localize} + end; + if Normal then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[6]{$ELSE}Result[7]{$ENDIF} := 'N'; {do not localize} + end; + if Temporary then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[7]{$ELSE}Result[8]{$ENDIF} := 'T'; {do not localize} + end; + if ReparsePoint then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[8]{$ELSE}Result[9]{$ENDIF} := 'J'; {do not localize} + end; + if SparseFile then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[9]{$ELSE}Result[10]{$ENDIF} := 'P'; {do not localize} + end; + if Compressed then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[10]{$ELSE}Result[11]{$ENDIF} := 'C'; {do not localize} + end; + if Offline then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[11]{$ELSE}Result[12]{$ENDIF} := 'O'; {do not localize} + end; + if NotContextIndexed then begin + {$IFDEF STRING_IS_IMMUTABLE}LSB[12]{$ELSE}Result[13]{$ENDIF} := 'I'; {do not localize} + end; + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +end. diff --git a/indy/Protocols/IdFTPServer.pas b/indy/Protocols/IdFTPServer.pas new file mode 100644 index 0000000..79b198e --- /dev/null +++ b/indy/Protocols/IdFTPServer.pas @@ -0,0 +1,7265 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.146 3/23/2005 5:16:56 AM JPMugaas + Should compile. + + Rev 1.145 3/14/05 11:28:50 AM RLebeau + Bug fix for CommandSIZE() not checking the FTPFileSystem property. + + Updated to reflect changes in TIdReply.NumericCode handling. + + Rev 1.144 3/5/2005 3:33:58 PM JPMugaas + Fix for some compiler warnings having to do with TStream.Read being platform + specific. This was fixed by changing the Compressor API to use TIdStreamVCL + instead of TStream. I also made appropriate adjustments to other units for + this. + + Rev 1.143 11/22/2004 8:29:20 PM JPMugaas + Fix for a compiler warning. + + Rev 1.142 11/22/2004 7:49:36 PM JPMugaas + You now can access help before you are logged in. This is done to conform + to RFC 959. + + Rev 1.141 2004.10.27 9:17:48 AM czhower + For TIdStrings + + Rev 1.140 10/26/2004 9:40:42 PM JPMugaas + Updated ref. + + Rev 1.139 9/15/2004 5:01:00 PM DSiders + Added localization comments. + + Rev 1.138 2004.08.13 11:03:22 czhower + Removed unused var. + + Rev 1.137 7/29/2004 1:33:10 AM JPMugaas + Reordered AUTH command values for a new property under development. This + should make things more logical. + + Rev 1.136 7/18/2004 3:00:42 PM DSiders + Added localization comments. + + Rev 1.135 7/15/2004 1:33:00 AM JPMugaas + Bug fix for error 105. I fixed this by changing data channel command + processing. If the command is not ABOR or STAT, the command is put into a + FIFO queue. After the data channel operation is completed, the commands from + the FIFO queue are processed. I have tested FlashFXP 3.0 RC4 and it does + worki as expected. The behavior is also the same as what NcFTPD does with a + NOOP being sent during a data transfer. + + This may also help with FTP command pipelining as proposed by: + http://cr.yp.to/ftp/pipelining.html + + Note that we can not use the regular command handler framework for data + channel commands because STAT and ABOR need to be handled IMMEDIATELY. + + Rev 1.134 7/13/04 9:08:10 PM RLebeau + Renamed OnPASV event to OnPASVBeforeBind and added new OnPASVReply event + + Rev 1.133 7/13/04 8:13:56 PM RLebeau + Various changed for DefaultDataPort handling + + Rev 1.132 7/13/2004 3:34:12 AM JPMugaas + CCC command and a few other minor modifications to comply with + http://www.ietf.org/internet-drafts/draft-murray-auth-ftp-ssl-14.txt . + + I also fixed a few minor bugs in the help and a problem with some error + replies sending an extra 200 after a 5xxx code messing up some clients. + + I also expanded the Security options to selectively disable CCC per user. + Some administrators may want to do this for security reasons. + + Rev 1.131 7/12/2004 11:46:44 PM JPMugaas + Improvement in OPTS MODE Z handling. It will give an error if there's only + one param. Params must be in pairs. If no valid parameters are present, we + give an error. + + Rev 1.130 07/07/2004 17:34:38 ANeillans + Corrected compile bug. + Line 6026, + if PosInStrArray(IntToStr(LNoVal),STATES,False)>-1 then + Function expected a string, not an integer. + + Rev 1.129 7/6/2004 4:52:16 PM DSiders + Corrected spelling of Challenge in properties, methods, types. + + Rev 1.128 6/29/2004 4:09:04 PM JPMugaas + OPTS MODE Z now supported as per draft-preston-ftpext-deflate-02.txt. This + should keep FTP Voyager 11 happy. + + Rev 1.127 6/28/2004 7:23:20 PM JPMugaas + Bugfix. An invalid site command would cause no reply to be given. Now a + syntax is given in such cases. + + Rev 1.126 6/27/2004 1:45:30 AM JPMugaas + Can now optionally support LastAccessTime like Smartftp's FTP Server could. + I also made the MLST listing object and parser support this as well. + + Rev 1.125 6/17/2004 3:56:28 PM JPMugaas + Fix for AV that happens after data channel operation. + + Rev 1.124 6/16/2004 2:29:32 PM JPMugaas + Removed direct access to a FConnection. We now use the Connection property + in the TIdContext. + + Rev 1.123 6/12/2004 9:05:52 AM JPMugaas + Telnet control sequences should now work during a data transfer. + Removed HandleTelnetSequences. It was part of a crude workaround which had + never works and the matter was fixed in another way. + OnCustomDir should now work if the DirStyle is custom. + + Rev 1.122 6/11/2004 9:35:12 AM DSiders + Added "Do not Localize" comments. + + Rev 1.121 2004.05.20 11:37:26 AM czhower + IdStreamVCL + + Rev 1.120 5/16/04 5:30:26 PM RLebeau + Added setter methods to the ReplyUnknownSITECommand and SITECommands + properties + + Added GetRepliesClass() overrides + + Rev 1.119 5/1/2004 1:52:20 PM JPMugaas + Updated for PeekBytes API change. + + Rev 1.118 4/8/2004 12:19:08 PM JPMugaas + Should work with new code. + + Rev 1.117 3/3/2004 6:34:46 PM JPMugaas + Improved help system. + Some manditory (RFC 1123 were rutning syntax errors instead of not + implemented. + Add some mention of some other RFC 2228 commands for completness. Not that + there are not supported or implemented. + + Rev 1.116 3/3/2004 6:02:14 AM JPMugaas + Command descriptions. + + Rev 1.115 3/2/2004 8:13:28 AM JPMugaas + Fixup for minor API change. + + Rev 1.113 3/1/2004 12:41:40 PM JPMugaas + Should compile with new code. + + Rev 1.112 2/29/2004 6:02:38 PM JPMugaas + Improved bug fix for problem with Telnet sequences not being handled properly + in the FTP server. Litteral CR and LF are now handled properly (according to + the Telnet Specification). + + Rev 1.111 2/25/2004 3:27:04 PM JPMugaas + STAT -l now works like a LIST command except that it returns output on the + control channel. This is for consistancy with microsoft FTP Service, + RaidenFTPD, and a few other servers. FlashFXP can take advantage of this + feature as well to gain some efficiency. Note that I do not do not advocate + doing this on the FTP client because some servers will act differently than + you would assume. I may see about possible options for using STAT -l but I + can NOT promise anything. + + Rev 1.110 2/17/2004 6:37:28 PM JPMugaas + OnPASV event added for people needing to change the IP address or port value + in commands such as PASV. This should only be done if you have a compelling + reason to do it. + + Note that the IP address parameter can NOT work with EPSV and SPSV because + only the port number is returned. The IP address is presumed to be the same + one that the host is connecting to. + + Rev 1.109 2/17/2004 12:26:06 PM JPMugaas + The client now supports MODE Z (deflate) uploads and downloads as specified + by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + + Rev 1.108 2/15/2004 12:11:04 AM JPMugaas + SPSV support. SPSV is an old propoal to help FTP support IPv6. This was + mentioned at: http://cr.yp.to/ftp/retr.html and is supported by PureFTPD. + + Rev 1.107 2/14/2004 10:00:40 PM JPMugaas + Both upload and download should now work in MODE Z. Dir already worked + properly. + + Rev 1.106 2/12/2004 11:34:38 PM JPMugaas + FTP Deflate preliminary support. Work still needs to be done for upload and + downloading. + + Rev 1.105 2004.02.08 3:08:10 PM czhower + .Net fix. + + Rev 1.104 2004.02.07 5:03:10 PM czhower + .net fixes. + + Rev 1.103 2004.02.03 5:45:54 PM czhower + Name changes + + Rev 1.102 1/29/2004 3:15:52 PM JPMugaas + Fix for P@SW in InitCommandHandlers used "PASV" isntead of "P@SW". Fixed. + + Rev 1.101 1/22/2004 8:29:06 AM JPMugaas + Removed Ansi*. + + Rev 1.100 1/21/2004 2:34:38 PM JPMugaas + Fixed SITE ZONE reply. + InitComponent + + Rev 1.99 1/19/2004 4:37:02 AM JPMugaas + MinutesFromGMT was moved to IdFTPCommon because the client now uses it. + + Rev 1.98 1/18/2004 9:19:08 AM JPMugaas + P@SW now supported. + + This is necessary as some routers that replace a PASV with a P@SW + as part of a misguided attempt to add a feature. + A router would do a replacement so a client would think that + PASV wasn't supported and then the client would do a PORT command + instead. That doesn't happen so this just caused the client not to work. + + See: http://www.gbnetwork.co.uk/smcftpd/ + + Rev 1.97 1/17/2004 7:40:08 PM JPMugaas + MLSD added to FEAT list for consistancy with other FTP servers. + Fixed bug that would cause FXP transfers to fail when receiving a PASV. + + Rev 1.96 1/16/2004 12:25:06 AM JPMugaas + Fixes for MTDM set modified time. + + Rev 1.94 1/15/2004 2:36:50 AM JPMugaas + XMD5 command support. + SITE ZONE command added for FTP Voyager. + Minor adjustment in AUTH line in the FEAT response to indicate that we + support the AUTH TLS, AUTH TLS-C, AUTH SSL, and AUTH TLS-P explicit TLS + commands. + + Rev 1.93 1/14/2004 4:11:30 PM JPMugaas + CPSV support added. This is like PASV but indicates that we use ssl_connect + instead of ssl_accept. CPSV is used in FlashFXP for secure site-to-site file + transfers. + + Rev 1.92 1/14/2004 12:24:06 PM JPMugaas + SSCN Support for secure Site to Site Transfers using SSL. + + SSCN is defined at: + + http://www.raidenftpd.com/kb/kb000000037.htm + + Rev 1.91 1/13/2004 6:30:38 AM JPMugaas + Numerous bug fixes. + Now supports XCWD (a predicessor to CWD). + Command Reply for unknown command works again. + Started putting some formatting into common routines. + CuteFTP goes bonkers with a "215 " reply to SYST command. Now indicate that + SYST isn't implemented instead of giving that "215 ". Note that a + "CustomSystID" should be provided when DirFormat is ftpdfCustom. + If DirFormat is ftpdfCustom and OnListDirectory is provided; MLST, MLSD, and + OPTS MLSD will be DISABLED. OnListDirectory is used in the custom format for + structed standardized output with the MLSD and MLST commands. + A not implemented is now given for some commands. + + Rev 1.90 1/5/2004 11:53:00 PM JPMugaas + Some messages moved to resource strings. Minor tweeks. EIdException no + longer raised. + + Rev 1.88 1/4/2004 3:51:32 PM JPMugaas + Fixed a CWD bug. The parameter was being ignored. + + Rev 1.87 1/3/2004 8:05:18 PM JPMugaas + Bug fix: Sometimes, replies will appear twice due to the way functionality + was enherited. + + Rev 1.86 1/3/2004 5:37:56 PM JPMugaas + Changes from Bas: + + added function GetReplyClass, this function returns the class of reply this + server class uses, this is because in dotnet there can be no code before the + inherited in the constructor ( that is used mow to determine thereply class ) + + changed System.Delete to IdDelete (in coreglobal) because System.Delete is + not in dotnet + + SplitLines is not enabled in dotnet yet, so i made it a todo, make sure to + enable it and remove the todo if you check it in + + + Rev 1.85 1/2/2004 1:02:08 AM JPMugaas + Made comment about why the SYST descriptor is determined the way it is. + + Rev 1.84 1/2/2004 12:55:32 AM JPMugaas + Now compiles. Removed the EmulateSystem property. Replaced one part with + the DirFormat property. + + Rev 1.83 1/1/2004 10:55:10 PM JPMugaas + Remy Lebeau found a bug with path processing in the FTP server. I was + passing an emptry Result string instead of APath in FTPNormalize. + + Rev 1.77 10/11/2003 10:17:28 AM JPMugaas + Checked in a more recent version which should be worked on instead. + + Rev 1.75 9/19/2003 12:50:18 PM JPMugaas + Started attempt to get the server to compile. + + Rev 1.74 9/18/2003 10:20:06 AM JPMugaas + Updated for new API. + + Rev 1.73 8/24/2003 06:50:02 PM JPMugaas + API Change in the FileSystem component so that a thread is passed instead of + some data from the thread. This should also make the API's easier to manage + than before and provide more flexibility for developers writing their own + file system components. + + Rev 1.72 7/13/2003 7:56:00 PM SPerry + fixed problem with commandhandlers + + Rev 1.69 6/17/2003 09:30:20 PM JPMugaas + Fixed an AV with the ALLO command if no parameters were passed. Stated in + HELP command that we don't support some old FTP E-Mail commands from RFC 765 + which have not been in use for many years. We now give a reply saying those + aren't implemented to be consistant with some Unix FTP deamons. + + Rev 1.68 6/17/2003 03:16:36 PM JPMugaas + I redid the help and site help implementations so that they list commands. + It did mean loosing the FHelpText TIdStrings property but this should be more + consistant with common practices. + + Rev 1.67 6/17/2003 09:07:40 AM JPMugaas + Improved SITE HELP handling. + + Rev 1.65 5/26/2003 12:22:50 PM JPMugaas + + Rev 1.64 5/25/2003 03:54:28 AM JPMugaas + + Rev 1.63 5/21/2003 3:59:32 PM BGooijen + removed with in InitializeCommandHandlers, and changed exception replies + + Rev 1.62 5/21/2003 09:29:40 AM JPMugaas + + Rev 1.61 5/19/2003 08:11:44 PM JPMugaas + Now should compile properly with new code in Core. + + Rev 1.60 4/10/2003 02:54:14 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.59 3/30/2003 12:18:38 AM BGooijen + bug fix + ssl one data channel fixed + + Rev 1.58 3/24/2003 11:08:42 PM BGooijen + 'transfer'-commands now block, until the transfer is done/aborted. + this made it possible to send the reply after the transfer in the + control-thread + + Rev 1.57 3/16/2003 06:11:18 PM JPMugaas + Server now derrives from a TLS framework. + + Rev 1.56 3/14/2003 11:33:46 PM JPMugaas + + Rev 1.55 3/14/2003 10:44:38 PM BGooijen + Removed warnings, changed StartSSL to PassThrough:=false; + + Rev 1.54 3/14/2003 10:00:24 PM BGooijen + Removed TIdServerIOHandlerSSLBase.PeerPassthrough, the ssl is now enabled in + the server-protocol-files + + Rev 1.53 3/13/2003 05:21:18 PM JPMugaas + Bas's bug fix. There was a wrong typecast. + + Rev 1.52 3/13/2003 8:57:30 PM BGooijen + changed TIdSSLIOHandlerSocketBase to TIdIOHandlerSocket in + TIdDataChannelContext.SetupDataChannel + + Rev 1.51 3/13/2003 09:49:06 AM JPMugaas + Now uses an abstract SSL base class instead of OpenSSL so 3rd-party vendors + can plug-in their products. + + Rev 1.50 3/13/2003 06:11:54 AM JPMugaas + Updated with Bas's change. + + Rev 1.49 3/10/2003 09:12:46 PM JPMugaas + Most command handlers now use Do methods for consistancy with other Indy code. + + Rev 1.48 3/10/2003 05:09:22 PM JPMugaas + MLST now works as expected with the file system. Note that the MLST means + simply to give information about an item instead of its contents. + GetRealFileName in IdFTPFileSystem now can accept the wildcard *. + When doing dirs in EPLF, only information about a directory is retruned if it + is specified. + + Rev 1.47 3/9/2003 02:11:34 PM JPMugaas + Removed server support for MODE B and MODE C. It turns out that we do not + support those modes properly. We only implemented Stream mode. We now + simply return a 504 for modes we don't support instead of a 200 okay. This + was throwing off Opera 7.02. + + Rev 1.46 3/6/2003 11:00:12 AM JPMugaas + Now handles the MFMT command and the MFCT (Modified Date fact) command. + + Rev 1.45 3/6/2003 08:26:28 AM JPMugaas + Bug fixes. + + FTP COMB command can now work in the FTPFileSystem component. + + Rev 1.44 3/5/2003 03:28:16 PM JPMugaas + MD5, MMD5, and XCRC are now supported in the Virtual File System. + + Rev 1.43 3/5/2003 11:46:38 AM JPMugaas + Rename now works in Virtual FileSystem. + + Rev 1.42 3/2/2003 04:54:34 PM JPMugaas + Now does recursive dir lists with the Virtual File System layer as well as + honors other switches. + + Rev 1.41 3/2/2003 02:18:32 PM JPMugaas + Bug fix for where a reply was not returned when using a file system component. + + Rev 1.40 3/2/2003 02:23:38 AM JPMugaas + fix for problem with pathes in the virtual file system. + + Rev 1.39 2/24/2003 08:50:44 PM JPMugaas + + Rev 1.38 2/24/2003 07:56:22 PM JPMugaas + Now uses /bin/ls strings. + + Rev 1.37 2/24/2003 07:21:10 AM JPMugaas + FTP Server now strips out any -R switches when emulating EPLF servers. + Recursive lists aren't supported with EPLF. + + Rev 1.36 2/21/2003 06:54:10 PM JPMugaas + The FTP list processing has been restructured so that Directory output is not + done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so + that the code is more scalable. + + Rev 1.35 2/15/2003 10:29:42 AM JPMugaas + Added support for some Unix specific facts with MLSD and MLST. + + Rev 1.34 2/14/2003 05:42:08 PM JPMugaas + Moved everything from IdFTPUtils to IdFTPCommon at Kudzu's suggestion. + + Rev 1.33 2/14/2003 11:57:48 AM JPMugaas + Updated for new API. Made sure that there were no calls to a function we + removed. + + Rev 1.32 2/14/2003 10:45:18 AM JPMugaas + Updated for minor API change. + + Rev 1.30 2/13/2003 01:28:08 AM JPMugaas + MLSD and MLST should now work better. + + Rev 1.29 2/12/2003 12:30:56 PM JPMugaas + Now honors parameters with the NLIST command. + + Rev 1.28 2/5/2003 10:30:04 PM BGooijen + Re-enabled ssl-support + + Rev 1.27 2/4/2003 05:31:40 PM JPMugaas + Added ASwitches parameter to the ListEvent so we can pass parameters such as + "-R" in addition to the standard path. + + Rev 1.26 2/3/2003 11:01:50 AM JPMugaas + Moved list export to IdFTPList. + + Rev 1.25 1/31/2003 01:59:18 PM JPMugaas + Security options are now reenabled. + + Rev 1.24 1/31/2003 01:19:00 PM JPMugaas + Now passes the ControlConnection context instead of the ControlConnection + object itself. + + Rev 1.23 1/31/2003 06:34:52 AM JPMugaas + Now SYST command works as expected. + + Rev 1.22 1/31/2003 04:23:24 AM JPMugaas + FTP Server security options can be set for individual users and the server + will now use the Context's security options. THis should permit more + flexibility in security. + + Rev 1.21 1/30/2003 03:31:06 AM JPMugaas + Now should also properly handle exceptions in the MLSx commands. + + Rev 1.20 1/30/2003 02:55:26 AM JPMugaas + Now properly handles exceptions in the ListEvent for the STAT and LIST + commands. + + Rev 1.19 1/29/2003 01:17:18 AM JPMugaas + Exception handling should mostly work as it should. There's still a problem + with the list. + + Rev 1.18 1/28/2003 02:27:26 AM JPMugaas + Improved exception handling in several events to try to be more consistant. + Now can optionally hide the exception message when giving an error reply to + the user. This should prevent some inadvertant information about a computer + going to a troublemaker. + + Rev 1.17 1/27/2003 05:03:16 AM JPMugaas + Now a developer can provide status information to a user with the STAT + command if they want. We format the reply in a standard manner for them. + They just provide the information. + + Rev 1.16 1/27/2003 02:13:30 AM JPMugaas + Added more security options as suggested by: + http://www.sans.org/rr/infowar/fingerprint.php to help slow down an attack. + You can optionally disable both SYST and the STAT commands. Trouble makers + can use those to help determine server type and then use known flaws to + compromise it. Note that these do not completely prevent attacks and should + not lull administrators into a false sense of security. + + Rev 1.15 1/27/2003 12:32:08 AM JPMugaas + Now can optionally return the identifier for the real operating system. By + default, this property is false for security reasons. + + Rev 1.14 1/26/2003 11:59:16 PM JPMugaas + SystemDescriptor behavior change as well as SYST command change. + SystemDescriptor no longer needs an OS type as the first word. That is now + handled by the SYST commandhandler to better comply with RFC 959. + + Rev 1.13 1/25/2003 02:00:58 AM JPMugaas + MMD5 (for multiple MD5 checksums) is now supported. + Refined MD5 command support slgihtly. + + This is based on: + http://www.ietf.org/internet-drafts/draft-twine-ftpmd5-00.txt + + Rev 1.12 1/24/2003 6:07:24 PM BGooijen + Changed TIdDataChannelThread to TIdDataChannelContext + + Rev 1.11 1/23/2003 9:06:26 PM BGooijen + changed the CommandAbor + + Rev 1.10 1/23/2003 10:39:38 AM BGooijen + TIdDataChannelContext.FServer was never assigned + + Rev 1.9 1/20/2003 1:15:40 PM BGooijen + Changed to TIdTCPServer / TIdCmdTCPServer classes + + Rev 1.8 1/17/2003 06:21:02 PM JPMugaas + Now works with new design. + + Rev 1.7 1/17/2003 05:28:42 PM JPMugaas + + Rev 1.6 1-9-2003 14:45:30 BGooijen + Added ABOR command with telnet escape characters + Fixed hanging of ABOR command + STOR and STOU now use REST-position + ABOR now returns 226 instead of 200 + + Rev 1.5 1-9-2003 14:35:52 BGooijen + changed TIdFTPServerContext(ASender.Context.Thread) to + TIdFTPServerContext(ASender.Context) on some places + + Rev 1.4 1/9/2003 06:08:10 AM JPMugaas + Updated to be based on IdContext. + + Rev 1.3 1-1-2003 20:13:06 BGooijen + Changed to support the new TIdContext class + + Rev 1.2 12-15-2002 21:15:46 BGooijen + IFDEF-ed all SSL code, the IFDEF-s are removed as soon as the SSL works again. + + Rev 1.1 11/14/2002 02:55:58 PM JPMugaas + FEAT and MLST now completely use the RFC Reply objects instead of + Connection.WriteLn. The Connection.WriteLn was a workaround for a deficit in + the original RFC Reply object. The workaround is no longer needed. +} + +unit IdFTPServer; + +{ + Original Author: Sergio Perry + Date: 04/21/2001 + Fixes and modifications: Doychin Bondzhev + Date: 08/10/2001 + Further Extensive changes by Chad Z. Hower (Kudzu) + EPSV/EPRT support for IPv6 by Johannes Berg + + TODO: + both EPSV and EPRT only allow data connections that have the same + protocol as the control connection, because the ftp server could be + used in a network only supporting one of them + +TODO: + Change events to use DoXXXX +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, IdCommandHandlers, IdGlobal, IdContext, IdException, + IdExplicitTLSClientServerBase, IdFTPBaseFileSystem, IdFTPCommon, + IdBaseComponent, IdFTPList, IdFTPListOutput, IdFTPServerContextBase, + IdReply, IdReplyFTP, IdReplyRFC, IdScheduler, IdServerIOHandler, + IdTCPConnection, IdCmdTCPServer, IdTCPServer, IdThread, IdUserAccounts, + IdYarn, IdZLibCompressorBase, SysUtils; + +type + TIdFTPDirFormat = (ftpdfDOS, ftpdfUnix, ftpdfEPLF, ftpdfCustom, ftpdfOSDependent); + TIdFTPPathProcessing = (ftppDOS, ftppUnix, ftpOSDependent, ftppCustom); + TIdFTPOperation = (ftpRetr, ftpStor); + + TIdMLSDAttr = (mlsdUniqueID, + mlsdPerms, + mlsdUnixModes, + mlsdUnixOwner, + mlsdUnixGroup, + mlsdFileCreationTime, + mlsdFileLastAccessTime, + mlsdWin32Attributes, + mlsdWin32DriveType, + mlstWin32DriveLabel); + + TIdMLSDAttrs = set of TIdMLSDAttr; + +const + DEF_DIRFORMAT = ftpdfUnix; //ftpdfOSDependent; + Id_DEF_AllowAnon = False; + Id_DEF_PassStrictCheck = True; + DEF_FTP_IMPLICIT_FTP = False; + + DEF_FTP_HIDE_INVALID_USER = True; + DEF_FTP_PASSWORDATTEMPTS = 3; + DEF_FTP_INVALIDPASS_DELAY = 3000; //3 seconds + DEF_FTP_PASV_SAME_IP = True; + DEF_FTP_PORT_SAME_IP = True; + DEF_FTP_NO_RESERVED_PORTS = True; + DEF_FTP_BLOCK_ALL_PORTS = False; + DEF_FTP_DISABLE_SYST = False; + DEF_FTP_DISABLE_STAT = False; + DEF_FTP_PERMIT_CCC = False; + DEF_FTP_REPORT_EX_MSG = False; + DEF_PASV_BOUND_MIN = 0; + DEF_PASV_BOUND_MAX = 0; + DEF_PATHPROCESSING = ftpOSDependent; + + {Do not change these as it could break some clients} + SYST_ID_UNIX = 'UNIX Type: L8'; {Do not translate} + SYST_ID_NT = 'Windows_NT'; {Do not translate} + +const AAlwaysValidOpts : array [0..2] of string = + ('SIZE', 'TYPE', 'MODIFY'); {Do not translate} + +type + TIdFTPServerContext = class; + //The final parameter could've been one item but I decided against that + //because occaisionally, you might have a situation where you need to specify + //the "type" fact to be several different things. + // + //http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt + TIdOnMLST = procedure(ASender : TIdFTPServerContext; const APath: TIdFTPFileName; + ADirectoryListing: TIdFTPListOutput) of object; + //data port binding events + TOnDataPortBind = procedure(ASender : TIdFTPServerContext) of object; + //note that the CHMOD value is now a VAR because we also want to support a "MFF UNIX.mode=" + //to do the same thing as a chmod. MFF is to "Modify a file fact". + TOnSetATTRIB = procedure(ASender: TIdFTPServerContext; var VAttr : UInt32; const AFileName : TIdFTPFileName; var VAUth : Boolean) of object; + //Note that VAuth : Boolean is used because you may want to deny permission for + //users to change their Unix permissions or UMASK - which is done in anonymous FTP + TOnSiteUMASK = procedure(ASender: TIdFTPServerContext; var VUMASK : Integer; var VAUth : Boolean) of object; + //note that the CHMOD value is now a VAR because we also want to support a "MFF UNIX.mode=" + //to do the same thing as a chmod. MFF is to "Modify a file fact". + TOnSiteCHMOD = procedure(ASender: TIdFTPServerContext; var APermissions : Integer; const AFileName : TIdFTPFileName; var VAUth : Boolean) of object; + //chown as an option can specify group + TOnSiteCHOWN = procedure(ASender: TIdFTPServerContext; var AOwner, AGroup : String; const AFileName : TIdFTPFileName; var VAUth : Boolean) of object; + + TOnSiteCHGRP = procedure(ASender: TIdFTPServerContext; var AGroup : String; const AFileName : TIdFTPFileName; var VAUth : Boolean) of object; + + TOnCustomPathProcess = procedure(ASender: TIdFTPServerContext; var VPath : TIdFTPFileName) of object; + // + TOnFTPUserLoginEvent = procedure(ASender: TIdFTPServerContext; const AUsername, APassword: string; + var AAuthenticated: Boolean) of object; + TOnFTPUserAccountEvent = procedure(ASender : TIdFTPServerContext; const AUsername, APassword,AAcount: string; var AAuthenticated: Boolean) of object; + + TOnAfterUserLoginEvent = procedure(ASender: TIdFTPServerContext) of object; + + TOnDirectoryEvent = procedure(ASender: TIdFTPServerContext; var VDirectory: TIdFTPFileName) of object; + TOnGetFileSizeEvent = procedure(ASender: TIdFTPServerContext; const AFilename: TIdFTPFileName; + var VFileSize: Int64) of object; + TOnGetFileDateEvent = procedure(ASender: TIdFTPServerContext; const AFilename: TIdFTPFileName; + var VFileDate: TDateTime) of object; + //note we have to use a switches parameter because LIST in practice can have both a path and some + //some switches such as -R for recursive. + TOnListDirectoryEvent = procedure(ASender: TIdFTPServerContext; const APath: TIdFTPFileName; + ADirectoryListing: TIdFTPListOutput; const ACmd : String; const ASwitches : String) of object; + TOnCustomListDirectoryEvent = procedure(ASender: TIdFTPServerContext; const APath: TIdFTPFileName; + ADirectoryListing: TStrings; const ACmd : String; const ASwitches : String) of object; + TOnFileEvent = procedure(ASender: TIdFTPServerContext; const APathName: TIdFTPFileName) of object; + TOnCheckFileEvent = procedure(ASender: TIdFTPServerContext; const APathName: TIdFTPFileName; var VExist : Boolean) of object; + TOnRenameFileEvent = procedure(ASender: TIdFTPServerContext; const ARenameFromFile, ARenameToFile: TIdFTPFileName) of object; + TOnRetrieveFileEvent = procedure(ASender: TIdFTPServerContext; const AFileName: TIdFTPFileName; + var VStream: TStream) of object; + TOnStoreFileEvent = procedure(ASender: TIdFTPServerContext; const AFileName: TIdFTPFileName; + AAppend: Boolean; var VStream: TStream) of object; + TOnCombineFiles = procedure(ASender: TIdFTPServerContext; const ATargetFileName: TIdFTPFileName; + AParts : TStrings) of object; + TOnCheckSumFile = procedure(ASender: TIdFTPServerContext; const AFileName : TIdFTPFileName; var VStream : TStream) of object; + TOnCacheChecksum = procedure(ASender: TIdFTPServerContext; const AFileName : TIdFTPFileName; var VCheckSum : String) of object; + TOnVerifyChecksum = procedure(ASender: TIdFTPServerContext; const AFileName : TIdFTPFileName; const ACheckSum : String) of object; + TOnSetFileDateEvent = procedure(ASender: TIdFTPServerContext; const AFileName : TIdFTPFileName; var AFileTime : TDateTime) of object; + TOnHostCheck = procedure(ASender:TIdFTPServerContext; const AHost : String; var VAccepted : Boolean) of object; + //This is just to be efficient with the SITE UTIME command and for setting the windows.lastaccesstime fact + TOnSiteUTIME = procedure(ASender: TIdFTPServerContext; const AFileName : TIdFTPFileName; + var VLastAccessTime, VLastModTime, VCreateDate : TDateTime; + var VAUth : Boolean) of object; + + EIdFTPServerException = class(EIdException); + EIdFTPServerNoOnListDirectory = class(EIdFTPServerException); + EIdFTPImplicitTLSRequiresSSL = class(EIdFTPServerException); + EIdFTPBoundPortMaxGreater = class(EIdFTPServerException); + EIdFTPBoundPortMinLess = class(EIdFTPServerException); + EIdFTPCannotBeNegative = class(EIdFTPServerException); + + //we don't parse CLNT parameters as they might be freeform for all we know + TIdOnClientID = procedure(ASender: TIdFTPServerContext; const AID : String) of object; + TIdOnFTPStatEvent = procedure(ASender: TIdFTPServerContext; AStatusInfo : TStrings) of object; + TIdOnBanner = procedure(ASender: TIdFTPServerContext; AGreeting : TIdReply) of object; + //This is for EPSV and PASV support - do not change the values unless you + //have an extremely compelling reason to do so. This even is ONLY for those compelling case. + TIdOnPASV = procedure(ASender: TIdFTPServerContext; var VIP : String; + var VPort : TIdPort; const AIPVer : TIdIPVersion) of object; + TIdOnPASVRange = procedure(ASender: TIdFTPServerContext; var VIP : String; + var VPortMin, VPortMax : TIdPort; const AIPVer : TIdIPVersion) of object; + TIdOnDirSizeInfo = procedure(ASender : TIdFTPServerContext; + const APathName : TIdFTPFileName; + var VIsAFile : Boolean; var VSpace : Int64) of object; + TIdFTPServer = class; + TIdFTPSecurityOptions = class(TPersistent) + protected + // RFC 2577 Recommends these + // Note that the current code already hides user ID's by + // only authenticating after the password + FPasswordAttempts : UInt32; + FInvalidPassDelay : UInt32; + // http://cr.yp.to/ftp/security.html Recommends these + FRequirePASVFromSameIP : Boolean; + FRequirePORTFromSameIP : Boolean; + FNoReservedRangePORT : Boolean; + FBlockAllPORTTransfers : Boolean; + FDisableSYSTCommand : Boolean; + FDisableSTATCommand : Boolean; + FPermitCCC : Boolean; + public + constructor Create; virtual; + procedure Assign(Source: TPersistent); override; + published + //limit login attempts - some hackers will try guessing passwords from a dictionary + property PasswordAttempts : UInt32 read FPasswordAttempts write FPasswordAttempts + default DEF_FTP_PASSWORDATTEMPTS; + //should slow-down a password guessing attack - note those dictionaries + property InvalidPassDelay : UInt32 read FInvalidPassDelay write FInvalidPassDelay + default DEF_FTP_INVALIDPASS_DELAY; + //client IP Address is the only one that we will accept a PASV + //transfer from + //http://cr.yp.to/ftp/security.html + property RequirePASVFromSameIP : Boolean read FRequirePASVFromSameIP write FRequirePASVFromSameIP + default DEF_FTP_PASV_SAME_IP; + //Accept port transfers from the same IP address as the client - + //should prevent bounce attacks + property RequirePORTFromSameIP : Boolean read FRequirePORTFromSameIP write FRequirePORTFromSameIP + default DEF_FTP_PORT_SAME_IP; + //Do not accept port requests to ports in the reserved range. That is dangerous on some systems + property NoReservedRangePORT : Boolean read FNoReservedRangePORT write FNoReservedRangePORT + default DEF_FTP_NO_RESERVED_PORTS; + //Do not accept any PORT transfers at all. This is a little extreme but reduces troubles further. + //This will break the the Win32 console clients and a number of other programs. + property BlockAllPORTTransfers : Boolean read FBlockAllPORTTransfers write FBlockAllPORTTransfers + default DEF_FTP_BLOCK_ALL_PORTS; + //Disable SYST command. SYST usually gives the system description. + //Disabling it may make it harder for a trouble maker to know about your computer + //but will not be a complete security solution. See http://www.sans.org/rr/infowar/fingerprint.php for details + //On the other hand, disabling it will break RFC 959 complience and may break some FTP programs. + property DisableSYSTCommand : Boolean read FDisableSYSTCommand write FDisableSYSTCommand + default DEF_FTP_DISABLE_SYST; + //Disable STAT command. STAT gives freeform information about the connection status. + // http://www.sans.org/rr/infowar/fingerprint.php advises administrators to disable this + //because servers tend to give distinct patterns of information and some trouble makers + //can figure out what type of server you are running simply with this. + property DisableSTATCommand : Boolean read FDisableSTATCommand write FDisableSTATCommand + default DEF_FTP_DISABLE_STAT; + //Permit CCC (Clear Command Connection) in TLS FTP + property PermitCCC : Boolean read FPermitCCC write FPermitCCC default DEF_FTP_PERMIT_CCC; + end; + + TIdDataChannel = class(TObject) + protected + FNegotiateTLS : Boolean; + FControlContext: TIdFTPServerContext; + FDataChannel: TIdTCPConnection; + FErrorReply: TIdReplyRFC; + FFtpOperation: TIdFTPOperation; + FOKReply: TIdReplyRFC; + FReply: TIdReplyRFC; + + FServer : TIdFTPServer; + FRequirePASVFromSameIP : Boolean; + FStopped : Boolean; + FData : TObject; + procedure SetErrorReply(const AValue: TIdReplyRFC); + procedure SetOKReply(const AValue: TIdReplyRFC); + function GetPeerIP: String; + function GetPeerPort: TIdPort; + function GetLocalIP: String; + function GetLocalPort: TIdPort; + public + constructor Create(APASV: Boolean; AControlContext: TIdFTPServerContext; const ARequirePASVFromSameIP : Boolean; AServer : TIdFTPServer); reintroduce; + destructor Destroy; override; + procedure InitOperation(const AConnectMode : Boolean = False); + property PeerIP : String read GetPeerIP; + property PeerPort : TIdPort read GetPeerPort; + property LocalIP : String read GetLocalIP; + property LocalPort : TIdPort read GetLocalPort; + property Stopped : Boolean read FStopped write FStopped; + property Data : TObject read FData write FData; + property Server : TIdFTPServer read FServer; + property OKReply: TIdReplyRFC read FOKReply write SetOKReply; + property ErrorReply: TIdReplyRFC read FErrorReply write SetErrorReply; + end; + + TIdFTPServerContext = class(TIdFTPServerContextBase) + protected + FXAUTKey : UInt32; + FRESTPos: Integer; + FDataChannel : TIdDataChannel; + FAuthMechanism : String; + FCCC : Boolean; //flag for CCC issuance + FDataType: TIdFTPTransferType; + FDataMode : TIdFTPTransferMode; + FDataPort: TIdPort; + FDataProtBufSize : UInt32; + FDataStruct: TIdFTPDataStructure; + + FPasswordAttempts : UInt32; + FPASV: Boolean; + + FEPSVAll: Boolean; + FDataPortDenied : Boolean; + FDataProtection : TIdFTPDataPortSecurity; + FDataPBSZCalled : Boolean; + FMLSOpts : TIdFTPFactOutputs; + + FSSCNOn : Boolean; + FServer : TIdFTPServer; + FUserSecurity : TIdFTPSecurityOptions; + FUMask : Integer; //for SITE UMASK command + //only used for Windows NT imitation + FMSDOSMode : Boolean; //False - off imitate Unix, //True - On imitate DOS + //This is a queued request to quite. + //if it's issued during a data transfer, we treat it as quit + //only after the request is completed. + FQuitReply : String; + //ZLib settings + FZLibCompressionLevel : Integer; //7 + FZLibWindowBits : Integer; //-15 + FZLibMemLevel : Integer; //8 + FZLibStratagy : Integer; //0 - default + // + procedure ResetZLibSettings; + procedure PortOnAfterBind(ASender : TObject); + procedure PortOnBeforeBind(ASender : TObject); + procedure SetUserSecurity(const Value: TIdFTPSecurityOptions); + procedure CreateDataChannel(APASV: Boolean = False); + function IsAuthenticated(ASender: TIdCommand): Boolean; + procedure ReInitialize; override; + + public + constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override; + destructor Destroy; override; + procedure KillDataChannel; + + property DataChannel : TIdDataChannel read FDataChannel; + property Server : TIdFTPServer read FServer write FServer; + + property UserSecurity : TIdFTPSecurityOptions read FUserSecurity write SetUserSecurity; + // + //This is for tracking what AUTH mechanism was specified and that + //we support. This may not matter as much now, but it could later on + //RFC 2228 + property AuthMechanism : String read FAuthMechanism write FAuthMechanism; + property DataType: TIdFTPTransferType read FDataType write FDataType; + property DataMode : TIdFTPTransferMode read FDataMode write FDataMode; + property DataPort: TIdPort read FDataPort; + //We do not use this much for now but if more AUTH mechanisms are added, + //we may need this property + property DataProtBufSize : UInt32 read FDataProtBufSize write FDataProtBufSize; + property DataPBSZCalled : Boolean read FDataPBSZCalled write FDataPBSZCalled; + property DataStruct: TIdFTPDataStructure read FDataStruct write FDataStruct; + //currently, only lear and

rivate are used. This could change + //later on + property DataProtection : TIdFTPDataPortSecurity read FDataProtection write FDataProtection; + property PasswordAttempts : UInt32 read FPasswordAttempts write FPasswordAttempts; + property PASV: Boolean read FPASV write FPASV; + property RESTPos: Integer read FRESTPos write FRESTPos; + property MLSOpts : TIdFTPFactOutputs read FMLSOpts write FMLSOpts; + //SSCN secure FTPX - http://www.raidenftpd.com/kb/kb000000037.htm + property SSCNOn : Boolean read FSSCNOn write FSSCNOn; + //SITE DIRSTYLE flag - true for MSDOS, false for Unix + property MSDOSMode : Boolean read FMSDOSMode write FMSDOSMode; + //SITE UMASK settings + property UMask : Integer read FUMask write FUMask; + //ZLib settings + property ZLibCompressionLevel : Integer read FZLibCompressionLevel write FZLibCompressionLevel; //7 + property ZLibWindowBits : Integer read FZLibWindowBits write FZLibWindowBits; //-15 + property ZLibMemLevel : Integer read FZLibMemLevel write FZLibMemLevel; //8 + property ZLibStratagy : Integer read FZLibStratagy write FZLibStratagy; //0 - default + end; + + TIdOnGetCustomListFormat = procedure(ASender: TIdFTPServer; AItem: TIdFTPListItem; + var VText: string) of object; + + { FTP Server } + TIdFTPServer = class(TIdExplicitTLSServer) + protected + FSupportXAUTH: Boolean; + FDirFormat : TIdFTPDirFormat; + FPathProcessing : TIdFTPPathProcessing; + FOnClientID : TIdOnClientID; + FDataChannelCommands: TIdCommandHandlers; + FSITECommands: TIdCommandHandlers; + FOPTSCommands: TIdCommandHandlers; + FMLSDFacts : TIdMLSDAttrs; + FAnonymousAccounts: TStrings; + FAllowAnonymousLogin: Boolean; + FAnonymousPassStrictCheck: Boolean; +// FEmulateSystem: TIdFTPSystems; + FPASVBoundPortMin : TIdPort; + FPASVBoundPortMax : TIdPort; + FSystemType: string; + FDefaultDataPort : TIdPort; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FUserAccounts: TIdCustomUserManager; + FOnUserAccount : TOnFTPUserAccountEvent; + FOnAfterUserLogin: TOnAfterUserLoginEvent; + FOnUserLogin: TOnFTPUserLoginEvent; + FOnChangeDirectory: TOnDirectoryEvent; + FOnGetFileSize: TOnGetFileSizeEvent; + FOnGetFileDate:TOnGetFileDateEvent; + FOnListDirectory: TOnListDirectoryEvent; + FOnCustomListDirectory : TOnCustomListDirectoryEvent; + FOnRenameFile: TOnRenameFileEvent; + FOnDeleteFile: TOnFileEvent; + FOnRetrieveFile: TOnRetrieveFileEvent; + FOnStoreFile: TOnStoreFileEvent; + FOnMakeDirectory: TOnDirectoryEvent; + FOnRemoveDirectory: TOnDirectoryEvent; + FOnStat : TIdOnFTPStatEvent; + FFTPSecurityOptions : TIdFTPSecurityOptions; + FOnCRCFile : TOnCheckSumFile; + FOnCombineFiles : TOnCombineFiles; + FOnSetModifiedTime : TOnSetFileDateEvent; + FOnFileExistCheck : TOnCheckFileEvent; //for MDTM variation to set the file time + FOnSetCreationTime : TOnSetFileDateEvent; + FOnMD5Cache : TOnCacheChecksum; + FOnMD5Verify : TOnVerifyChecksum; + FOnGreeting : TIdOnBanner; + FOnLoginSuccessBanner : TIdOnBanner; + FOnLoginFailureBanner : TIdOnBanner; + FOnQuitBanner : TIdOnBanner; + FOnSetATTRIB : TOnSetATTRIB; + FOnSiteUMASK : TOnSiteUMASK; + FOnSiteCHMOD : TOnSiteCHMOD; + FOnSiteCHOWN : TOnSiteCHOWN; + FOnSiteCHGRP : TOnSiteCHGRP; + FOnAvailDiskSpace : TIdOnDirSizeInfo; + FOnCompleteDirSize : TIdOnDirSizeInfo; + FOnRemoveDirectoryAll: TOnDirectoryEvent; + FOnCustomPathProcess : TOnCustomPathProcess; + + FOnDataPortBeforeBind : TOnDataPortBind; + FOnDataPortAfterBind : TOnDataPortBind; + FOnPASVBeforeBind : TIdOnPASVRange; + FOnPASVReply : TIdOnPASV; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FFTPFileSystem: TIdFTPBaseFileSystem; + FEndOfHelpLine : String; + FCustomSystID : String; + FReplyUnknownSITECommand : TIdReply; + FCompressor : TIdZLibCompressorBase; + FOnMLST : TIdOnMLST; + FOnSiteUTIME : TOnSiteUTIME; + FOnHostCheck : TOnHostCheck; + procedure SetOnUserAccount(AValue : TOnFTPUserAccountEvent); + procedure AuthenticateUser(ASender: TIdCommand); + function SupportTaDirSwitches(AContext : TIdFTPServerContext) : Boolean; + function IgnoreLastPathDelim(const APath : String) : String; + procedure DoOnPASVBeforeBind(ASender : TIdFTPServerContext; var VIP : String; + var VPortMin, VPortMax : TIdPort; const AIPVersion : TIdIPVersion); + procedure DoOnPASVReply(ASender : TIdFTPServerContext; var VIP : String; + var VPort : TIdPort; const AIPVersion : TIdIPVersion); + function InternalPASV(ASender: TIdCommand; var VIP : String; + var VPort: TIdPort; var VIPVersion : TIdIPVersion): Boolean; + function DoSysType(ASender : TIdFTPServerContext) : String; + function DoProcessPath(ASender : TIdFTPServerContext; const APath: TIdFTPFileName): TIdFTPFileName; + + function FTPNormalizePath(const APath: String) : String; + function MLSFEATLine(const AFactMask : TIdMLSDAttrs; const AFacts : TIdFTPFactOutputs) : String; + + function HelpText(Cmds : TStrings) : String; + function IsValidPermNumbers(const APermNos : String) : Boolean; + procedure SetRFCReplyFormat(AReply : TIdReply); + function CDUPDir(AContext : TIdFTPServerContext) : String; + procedure DisconUser(ASender: TIdCommand); + //command reply common code + procedure CmdNotImplemented(ASender : TIdCommand); + procedure CmdFileActionAborted(ASender : TIdCommand); + procedure CmdSyntaxError(AContext: TIdContext; ALine: string; const AReply : TIdReply = nil); overload; + procedure CmdSyntaxError(ASender : TIdCommand); overload; + procedure CmdInvalidParams(ASender: TIdCommand); + procedure CmdInvalidParamNum(ASender:TIdCommand); + //The http://www.potaroo.net/ietf/idref/draft-twine-ftpmd5/ + //draft didn't specify 550 as an error. It said use 504. + procedure CmdTwineFileActionAborted(ASender : TIdCommand); + //success reply codes can vary amoung commands + procedure CmdCommandSuccessful(ASender: TIdCommand; const AReplyCode : Integer = 250); + //Command replies + procedure CommandQUIT(ASender:TIdCommand); + procedure CommandUSER(ASender: TIdCommand); + procedure CommandPASS(ASender: TIdCommand); + procedure CommandACCT(ASender: TIdCommand); + procedure CommandXAUT(ASender : TIdCommand); + procedure CommandCWD(ASender: TIdCommand); + procedure CommandCDUP(ASender: TIdCommand); + procedure CommandREIN(ASender: TIdCommand); + procedure CommandPORT(ASender: TIdCommand); + procedure CommandPASV(ASender: TIdCommand); + procedure CommandTYPE(ASender: TIdCommand); + procedure CommandSTRU(ASender: TIdCommand); + procedure CommandMODE(ASender: TIdCommand); + procedure CommandRETR(ASender: TIdCommand); + procedure CommandSSAP(ASender: TIdCommand); + procedure CommandALLO(ASender: TIdCommand); + procedure CommandREST(ASender: TIdCommand); + procedure CommandRNFR(ASender: TIdCommand); + procedure CommandRNTO(ASender: TIdCommand); + procedure CommandABOR(ASender: TIdCommand); + //AVBL from Streamlined FTP Command Extensions + // draft-peterson-streamlined-ftp-command-extensions-01.txt + procedure CommandAVBL(ASender: TIdCommand); + procedure CommandDELE(ASender: TIdCommand); + + //DSIZ from Streamlined FTP Command Extensions + // draft-peterson-streamlined-ftp-command-extensions-01.txt + procedure CommandDSIZ(ASender : TIdCommand); + procedure CommandRMDA(ASender : TIdCommand); + + procedure CommandRMD(ASender: TIdCommand); + procedure CommandMKD(ASender: TIdCommand); + procedure CommandPWD(ASender: TIdCommand); + procedure CommandLIST(ASender: TIdCommand); + procedure CommandSYST(ASender: TIdCommand); + procedure CommandSTAT(ASender: TIdCommand); + procedure CommandSIZE(ASender: TIdCommand); + procedure CommandFEAT(ASender: TIdCommand); + procedure CommandOPTS(ASender: TIdCommand); + procedure CommandAUTH(ASender: TIdCommand); + procedure CommandCCC(ASender: TIdCommand); + // rfc 2428: + procedure CommandEPSV(ASender: TIdCommand); + procedure CommandEPRT(ASender: TIdCommand); + // + procedure CommandMDTM(ASender: TIdCommand); + procedure CommandMFF(ASender: TIdCommand); + // + procedure CommandMD5(ASender: TIdCommand); + procedure CommandMMD5(ASender: TIdCommand); + // + procedure CommandPROT(ASender: TIdCommand); + procedure CommandPBSZ(ASender: TIdCommand); + + procedure CommandMFMT(ASender: TIdCommand); + procedure CommandMFCT(ASender: TIdCommand); + + procedure CommandMLSD(ASender: TIdCommand); + procedure CommandMLST(ASender: TIdCommand); + + procedure CommandCheckSum(ASender: TIdCommand); + procedure CommandCOMB(ASender: TIdCommand); + + procedure CommandCLNT(ASender: TIdCommand); + //SSCN Secure FTPX - http://www.raidenftpd.com/kb/kb000000037.htm + procedure CommandSSCN(ASender: TIdCommand); + //Informal - like PASV accept SSL is in client mode - used by FlashXP + procedure CommandCPSV(ASender: TIdCommand); + //Informal - like PASV except that only the port is communicated. + // + procedure CommandSPSV(ASender: TIdCommand); + + procedure CommandHOST(ASender : TIdCommand); + procedure CommandSecRFC(ASender : TIdCommand); //stub for some commands in 2228 + procedure CommandSITE(ASender: TIdCommand); + procedure CommandSiteHELP(ASender : TIdCommand); + //site commands - Unix + procedure CommandSiteUMASK(ASender : TIdCommand); + procedure CommandSiteCHMOD(ASender : TIdCommand); + //SITE CHOWN - supported by some Unix servers + procedure CommandSiteCHOWN(ASender : TIdCommand); + //SITE CHGRP - supported by some Unix servers + procedure CommandSiteCHGRP(ASender : TIdCommand); + //site commans - MS IIS + procedure CommandSiteDIRSTYLE(ASender : TIdCommand); + //used by FTP Voyager + procedure CommandSiteZONE(ASender : TIdCommand); + //supported by RaidenFTP - http://www.raidenftpd.com/kb/kb000000049.htm + procedure CommandSiteATTRIB(ASender : TIdCommand); + //McFTP client uses this to set the time stamps for a file. + procedure CommandSiteUTIME(ASender : TIdCommand); + // end site commands + + procedure CommandOptsMLST(ASender : TIdCommand); + procedure CommandOptsMODEZ(ASender : TIdCommand); + procedure CommandOptsUTF8(ASender: TIdCommand); + procedure CommandHELP(ASender: TIdCommand); + // + procedure DoOnRenameFile(ASender: TIdFTPServerContext; const ARenameFromFile, ARenameToFile: string); + procedure DoOnDeleteFile(ASender: TIdFTPServerContext; const APathName: string); + procedure DoOnChangeDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); + procedure DoOnMakeDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); + procedure DoOnRemoveDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); + procedure DoOnGetFileSize(ASender: TIdFTPServerContext; const AFilename: string; var VFileSize: Int64); + procedure DoOnGetFileDate(ASender: TIdFTPServerContext; const AFilename: string; var VFileDate: TDateTime); + procedure DoOnSetModifiedTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); overload; + procedure DoOnSetModifiedTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTimeStr : String); overload; + procedure DoOnFileExistCheck(AContext: TIdFTPServerContext; const AFileName : String; var VExist : Boolean); + procedure DoOnSetModifiedTimeGMT(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); + procedure DoOnSetCreationTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); overload; + procedure DoOnSetCreationTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTimeStr : String); overload; + procedure DoOnSetCreationTimeGMT(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); + procedure DoOnCRCFile(ASender: TIdFTPServerContext; const AFileName : String; var VStream : TStream); + procedure DoOnMD5Verify(ASender: TIdFTPServerContext; const AFileName : String; const ACheckSum : String); + procedure DoOnMD5Cache(ASender: TIdFTPServerContext; const AFileName : String; var VCheckSum : String); + procedure DoOnCombineFiles(ASender: TIdFTPServerContext; const ATargetFileName: string; AParts : TStrings); + procedure DoOnSetATTRIB(ASender: TIdFTPServerContext; var VAttr : UInt32; const AFileName : String; var VAUth : Boolean); + procedure DoOnSiteUMASK(ASender: TIdFTPServerContext; var VUMASK : Integer; var VAUth : Boolean); + procedure DoOnSiteCHMOD(ASender: TIdFTPServerContext; var APermissions : Integer; const AFileName : String; var VAUth : Boolean); + procedure DoOnSiteCHOWN(ASender: TIdFTPServerContext; var AOwner, AGroup : String; const AFileName : String; var VAUth : Boolean); + procedure DoOnSiteCHGRP(ASender: TIdFTPServerContext; var AGroup : String; const AFileName : String; var VAUth : Boolean); + procedure SetUseTLS(AValue: TIdUseTLS); override; + procedure SetSupportXAUTH(AValue : Boolean); + procedure InitializeCommandHandlers; override; + procedure ListDirectory(ASender: TIdFTPServerContext; ADirectory: string; + ADirContents: TStrings; ADetails: Boolean; const ACmd : String = 'LIST'; + const ASwitches : String = ''); {do not localize} + {$IFNDEF USE_OBJECT_ARC} + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + {$ENDIF} + procedure SetAnonymousAccounts(const AValue: TStrings); + procedure SetUserAccounts(const AValue: TIdCustomUserManager); + procedure SetFTPSecurityOptions(const AValue: TIdFTPSecurityOptions); + procedure SetPASVBoundPortMax(const AValue: TIdPort); + procedure SetPASVBoundPortMin(const AValue: TIdPort); + procedure SetReplyUnknownSITECommand(AValue: TIdReply); + procedure SetSITECommands(AValue: TIdCommandHandlers); + procedure ThreadException(AThread: TIdThread; AException: Exception); + procedure SetFTPFileSystem(const AValue: TIdFTPBaseFileSystem); + function GetMD5Checksum(ASender : TIdFTPServerContext; const AFileName : String) : String; + //overrides from TIdTCPServer + procedure DoConnect(AContext:TIdContext); override; + procedure DoDisconnect(AContext:TIdContext); override; + procedure ContextCreated(AContext:TIdContext); override; + + procedure DoOnDataPortBeforeBind(ASender : TIdFTPServerContext); virtual; + procedure DoDataChannelOperation(ASender: TIdCommand; const AConnectMode : Boolean = False);virtual; + procedure DoOnDataPortAfterBind(ASender : TIdFTPServerContext); virtual; + procedure DoOnCustomListDirectory(ASender: TIdFTPServerContext; const APath: string; + ADirectoryListing: TStrings; const ACmd : String; const ASwitches : String); + function GetReplyClass: TIdReplyClass; override; + function GetRepliesClass: TIdRepliesClass; override; + procedure InitComponent; override; + procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); override; + // overriden so we can close active transfers during a shutdown + procedure DoTerminateContext(AContext: TIdContext); override; + //overriden so we can handle telnet sequences + function ReadCommandLine(AContext: TIdContext): string; override; + public + destructor Destroy; override; + property SupportXAUTH : Boolean read FSupportXAUTH write SetSupportXAUTH; + published + {This is an object that can compress and decompress HTTP Deflate encoding} + property Compressor : TIdZLibCompressorBase read FCompressor write FCompressor; + property CustomSystID : String read FCustomSystID write FCustomSystID; + property DirFormat : TIdFTPDirFormat read FDirFormat write FDirFormat default DEF_DIRFORMAT; + property PathProcessing : TIdFTPPathProcessing read FPathProcessing write FPathProcessing default DEF_PATHPROCESSING; + property UseTLS; + property DefaultPort default IDPORT_FTP; + property AllowAnonymousLogin: Boolean read FAllowAnonymousLogin write FAllowAnonymousLogin default Id_DEF_AllowAnon; + property AnonymousAccounts: TStrings read FAnonymousAccounts write SetAnonymousAccounts; + property AnonymousPassStrictCheck: Boolean read FAnonymousPassStrictCheck + write FAnonymousPassStrictCheck default Id_DEF_PassStrictCheck; + property DefaultDataPort : TIdPort read FDefaultDataPort write FDefaultDataPort default IdPORT_FTP_DATA; + property FTPFileSystem:TIdFTPBaseFileSystem read FFTPFileSystem write SetFTPFileSystem; + property FTPSecurityOptions : TIdFTPSecurityOptions read FFTPSecurityOptions write SetFTPSecurityOptions; + property EndOfHelpLine : String read FEndOfHelpLine write FEndOfHelpLine; + property PASVBoundPortMin : TIdPort read FPASVBoundPortMin write SetPASVBoundPortMin default DEF_PASV_BOUND_MIN; + property PASVBoundPortMax : TIdPort read FPASVBoundPortMax write SetPASVBoundPortMax default DEF_PASV_BOUND_MAX; + property UserAccounts: TIdCustomUserManager read FUserAccounts write SetUserAccounts; + property SystemType: string read FSystemType write FSystemType; + property OnGreeting : TIdOnBanner read FOnGreeting write FOnGreeting; + property OnLoginSuccessBanner : TIdOnBanner read FOnLoginSuccessBanner write FOnLoginSuccessBanner; + property OnLoginFailureBanner : TIdOnBanner read FOnLoginFailureBanner write FOnLoginFailureBanner; + //for retreiving MD5 Checksums from a cache + property OnMD5Cache : TOnCacheChecksum read FOnMD5Cache write FOnMD5Cache; + property OnMD5Verify : TOnVerifyChecksum read FOnMD5Verify write FOnMD5Verify; + property OnQuitBanner : TIdOnBanner read FOnQuitBanner write FOnQuitBanner; + property OnCustomListDirectory : TOnCustomListDirectoryEvent read FOnCustomListDirectory write FOnCustomListDirectory; + property OnCustomPathProcess : TOnCustomPathProcess read FOnCustomPathProcess write FOnCustomPathProcess; + property OnAfterUserLogin: TOnAfterUserLoginEvent read FOnAfterUserLogin write FOnAfterUserLogin; + property OnChangeDirectory: TOnDirectoryEvent read FOnChangeDirectory write FOnChangeDirectory; + property OnGetFileSize: TOnGetFileSizeEvent read FOnGetFileSize write FOnGetFileSize; + property OnGetFileDate: TOnGetFileDateEvent read FOnGetFileDate write FOnGetFileDate; + property OnUserLogin: TOnFTPUserLoginEvent read FOnUserLogin write FOnUserLogin; + property OnUserAccount : TOnFTPUserAccountEvent read FOnUserAccount write SetOnUserAccount; + property OnListDirectory: TOnListDirectoryEvent read FOnListDirectory write FOnListDirectory; + property OnDataPortBeforeBind : TOnDataPortBind read FOnDataPortBeforeBind write FOnDataPortBeforeBind; + property OnDataPortAfterBind : TOnDataPortBind read FOnDataPortAfterBind write FOnDataPortAfterBind; + property OnRenameFile: TOnRenameFileEvent read FOnRenameFile write FOnRenameFile; + property OnDeleteFile: TOnFileEvent read FOnDeleteFile write FOnDeleteFile; + property OnRetrieveFile: TOnRetrieveFileEvent read FOnRetrieveFile write FOnRetrieveFile; + property OnStoreFile: TOnStoreFileEvent read FOnStoreFile write FOnStoreFile; + property OnMakeDirectory: TOnDirectoryEvent read FOnMakeDirectory write FOnMakeDirectory; + property OnRemoveDirectory: TOnDirectoryEvent read FOnRemoveDirectory write FOnRemoveDirectory; + property OnStat : TIdOnFTPStatEvent read FOnStat write FOnStat; + property OnCombineFiles : TOnCombineFiles read FOnCombineFiles write FOnCombineFiles; + property OnCRCFile : TOnCheckSumFile read FOnCRCFile write FOnCRCFile; + property OnSetCreationTime : TOnSetFileDateEvent read FOnSetCreationTime write FOnSetCreationTime; + property OnSetModifiedTime : TOnSetFileDateEvent read FOnSetModifiedTime write FOnSetModifiedTime; + property OnFileExistCheck : TOnCheckFileEvent read FOnFileExistCheck write FOnFileExistCheck; + property OnHostCheck : TOnHostCheck read FOnHostCheck write FOnHostCheck; + property OnSetATTRIB : TOnSetATTRIB read FOnSetATTRIB write FOnSetATTRIB; + property OnSiteUMASK : TOnSiteUMASK read FOnSiteUMASK write FOnSiteUMASK; + property OnSiteCHMOD : TOnSiteCHMOD read FOnSiteCHMOD write FOnSiteCHMOD; + property OnSiteCHOWN : TOnSiteCHOWN read FOnSiteCHOWN write FOnSiteCHOWN; + property OnSiteCHGRP : TOnSiteCHGRP read FOnSiteCHGRP write FOnSiteCHGRP; + { + READ THIS!!! + + Do not change values in the OnPASV event unless you have a compelling reason to do so. + + In SPSV, the PORT is the only thing that can work because that's all which is + given as a reply. The server IP is the same one that the client connects to. + + In EPSV, the PORT is the only thing that can work because that's all which is + given as a reply. The server IP is the same one that the client connects to. + + } + property OnPASVBeforeBind : TIdOnPASVRange read FOnPASVBeforeBind write FOnPASVBeforeBind; + property OnPASVReply : TIdOnPASV read FOnPASVReply write FOnPASVReply; + property OnMLST : TIdOnMLST read FOnMLST write FOnMLST; + property OnSiteUTIME : TOnSiteUTIME read FOnSiteUTIME write FOnSiteUTIME; + property OnAvailDiskSpace : TIdOnDirSizeInfo read FOnAvailDiskSpace write FOnAvailDiskSpace; + property OnCompleteDirSize : TIdOnDirSizeInfo read FOnCompleteDirSize write FOnCompleteDirSize; + + property SITECommands: TIdCommandHandlers read FSITECommands write SetSITECommands; + property MLSDFacts : TIdMLSDAttrs read FMLSDFacts write FMLSDFacts; + property OnClientID : TIdOnClientID read FOnClientID write FOnClientID; + property ReplyUnknownSITCommand: TIdReply read FReplyUnknownSITECommand write SetReplyUnknownSITECommand; + end; + + {This is used internally for some Telnet sequence parsing} +type + TIdFTPTelnetState = (tsData, tsCheckCR, tsIAC, tsWill, tsDo, tsWont, tsDont, + tsNegotiate, tsNegotiateData, tsNegotiateIAC, tsInterrupt, tsInterruptIAC); + +implementation + +uses + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + IdFIPS, + IdHash, IdHashCRC, IdHashMessageDigest, IdHashSHA, IdIOHandlerSocket, + IdResourceStringsProtocols, IdGlobalProtocols, IdSimpleServer, IdSSL, + IdIOHandlerStack, IdSocketHandle, IdStrings, IdTCPClient, IdEMailAddress, + IdStack, IdFTPListTypes; + +const + //THese commands need some special treatment in the Indy 10 FTP Server help system + //as they will not always work + HELP_SPEC_CMDS : array [0..25] of string = + ('SIZE','MDTM', {do not localize} + 'AUTH','PBSZ','PROT','CCC','MIC','CONF','ENC', 'SSCN','CPSV', {do not localize} + 'MFMT','MFF', + 'MD5','MMD5','XCRC','XMD5','XSHA1','XSHA256','XSHA512', {do not localize} + 'COMB','AVBL','DSIZ','RMDA','HOST','XAUT'); {do not localize} + + //These commands must always be present even if not implemented + //alt help topics and superscripts should be used sometimes. + //These are mandated by RFC 1123 + HELP_ALT_MD_CMD : array [0..17] of string = + ('RETR', {do not localize} + 'STOR','STOU', {do not localize} + 'APPE', {do not localize} + 'RNFR', 'RNTO', {do not localize} + 'DELE', {do not localize} + 'LIST','NLST', {do not localize} + 'CWD','XCWD', {do not localize} + 'CDUP','XCUP', {do not localize} + 'RMD','XRMD', {do not localize} + 'MKD', 'XMKD', {do not localize} + 'SYST'); {do not localize} + + HELP_ALT_MD_TP : array [0..17] of string = + ('RETR (retrieve); unimplemented.', {do not localize} + 'STOR (store); unimplemented.', {do not localize} + 'STOU (store unique); unimplemented.', {do not localize} + 'APPE (append); unimplemented.', {do not localize} + 'RNFR (rename from); unimplemented.', {do not localize} + 'RNTO (rename to); unimplemented.', {do not localize} + 'DELE (delete); unimplemented.', {do not localize} + 'LIST (list); unimplemented.', {do not localize} + 'NLIST (name-list); unimplemented.', {do not localize} + 'CWD (change working directory); unimplemented.', {do not localize} + 'XCWD (change working directory); unimplemented.', {do not localize} + 'CDUP (change to parent directory); unimplemented.', {do not localize} + 'XCDUP (change to parent directory); unimplemented.', {do not localize} + 'RMD (remove Directory); unimplemented.', {do not localize} + 'XRMD (remove Directory); unimplemented.', {do not localize} + 'MKD (make Directory); unimplemented.', {do not localize} + 'XMKD (make Directory); unimplemented.', {do not localize} + 'SYST (system); unimplemented.' {do not localize} + ); + + //SSCN, OPTS MODE Z EXTRA, and OPTS UTF8 states + OnOffStates : array [0..1] of string = + ('ON', {do not localize} + 'OFF' {do not localize} + ); + +const + //%s = host + //%n = xauth key + XAUTHBANNER = '%s X2 WS_FTP Server Compatible(%d)'; + ACCT_HELP_DISABLED = 'ACCT (specify account); unimplemented.'; {do not localize} + ACCT_HELP_ENABLED = 'Syntax: ACCT '; + +const + NLSTEncType: array[Boolean] of IdTextEncodingType = (encASCII, encUTF8); + +function CalculateCheckSum(AHashClass: TIdHashClass; AStrm: TStream; ABeginPos, AEndPos: TIdStreamSize): String; + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + LHash: TIdHash; +begin + LHash := AHashClass.Create; + try + Result := LHash.HashStreamAsHex(AStrm, ABeginPos, AEndPos-ABeginPos); + finally + LHash.Free; + end; +end; + +procedure XAutGreeting(AContext: TIdContext; AGreeting : TIdReply; const AHostName : String); + {$IFDEF USE_INLINE} inline; {$ENDIF} +var + s : String; +begin +//for XAUT to work with WS-FTP Pro, you need a banner mentioning "WS_FTP Server" +//and that banner can only be one line in length. + s := IndyFormat(XAUTHBANNER, + [ GStack.HostName, (AContext as TIdFTPServerContext).FXAUTKey]) + ' '+AGreeting.Text.Text; + s := Fetch(s,CR); + s := Fetch(s,LF); + AGreeting.Text.Text := s; + +end; + +{ TIdFTPServer } + +constructor TIdFTPServerContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; + AList: TIdContextThreadList = nil); +begin + inherited Create(AConnection, AYarn, AList); + FUserSecurity := TIdFTPSecurityOptions.Create; + //we don't initialize FCCC flag here because that shouldn't be cleared with implicit SSL + FCCC := False; + FDataMode := dmStream; + FMLSOpts := [ItemType, Modify, Size]; + //no write permissions for group and others + FUMask := 22; + ResetZLibSettings; + ReInitialize; +end; + +procedure TIdFTPServerContext.SetUserSecurity(const Value: TIdFTPSecurityOptions); +begin + FUserSecurity.Assign( Value); +end; + +destructor TIdFTPServerContext.Destroy; +begin + KillDataChannel; + FreeAndNil(FUserSecurity); + inherited Destroy; +end; + +procedure TIdFTPServerContext.CreateDataChannel(APASV: Boolean = False); +begin + KillDataChannel; //let the old one terminate + FDataChannel := TIdDataChannel.Create(APASV, Self, UserSecurity.RequirePASVFromSameIP, Server); +end; + +procedure TIdFTPServerContext.KillDataChannel; +begin + if Assigned(FDataChannel) then begin + if not FDataChannel.Stopped then begin + FDataChannel.Stopped := True; + FDataChannel.FDataChannel.Disconnect(False); + end; + FreeAndNil(FDataChannel); + end; +end; + +procedure TIdFTPServerContext.ReInitialize; +begin + inherited; + FDataType := ftASCII; + // FDataMode := dmStream; + FDataPort := 0; + FDataStruct := dsFile; + FPASV := False; + FEPSVAll := False; + FDataProtection := ftpdpsClear; + DataPBSZCalled := False; + FDataProtBufSize := 0; +end; + +function TIdFTPServerContext.IsAuthenticated(ASender: TIdCommand): Boolean; +begin + Result := FAuthenticated; + if not Result then begin + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); + end; +end; + +{ TIdFTPServer } + +procedure TIdFTPServer.InitComponent; +begin + inherited InitComponent; + HelpReply.Code := ''; //we will handle the help ourselves + FDataChannelCommands := TIdCommandHandlers.Create(Self, FReplyClass, ReplyTexts, ExceptionReply); + FSITECommands := TIdCommandHandlers.Create(Self, FReplyClass, ReplyTexts, ExceptionReply); + FOPTSCommands := TIdCommandHandlers.Create(Self, FReplyClass, ReplyTexts, ExceptionReply); + //inherited from TLS classes + FRegularProtPort := IdPORT_FTP; + FImplicitTLSProtPort := IdPORT_ftps; + // + FAnonymousAccounts := TStringList.Create; + // By default these user names will be treated as anonymous. + FAnonymousAccounts.Add('anonymous'); { do not localize } + FAnonymousAccounts.Add('ftp'); { do not localize } + FAnonymousAccounts.Add('guest'); { do not localize } + FAllowAnonymousLogin := Id_DEF_AllowAnon; + FAnonymousPassStrictCheck := Id_DEF_PassStrictCheck; + DefaultPort := IDPORT_FTP; + DefaultDataPort := IdPORT_FTP_DATA; +// FEmulateSystem := Id_DEF_SystemType; + Greeting.SetReply(220, RSFTPDefaultGreeting); + + FContextClass := TIdFTPServerContext; + ReplyUnknownCommand.SetReply(500, 'Unknown Command'); {do not localize} + + FReplyUnknownSITECommand := FReplyClass.Create(nil); + FReplyUnknownSITECommand.SetReply(500, 'Invalid SITE command.'); {do not localize} + + FFTPSecurityOptions := TIdFTPSecurityOptions.Create; + FPASVBoundPortMin := DEF_PASV_BOUND_MIN; + FPASVBoundPortMax := DEF_PASV_BOUND_MAX; + FPathProcessing := DEF_PATHPROCESSING; + FDirFormat := DEF_DIRFORMAT; +end; + +function TIdFTPServer.GetReplyClass: TIdReplyClass; +begin + Result := TIdReplyFTP; +end; + +function TIdFTPServer.GetRepliesClass: TIdRepliesClass; +begin + Result := TIdRepliesFTP; +end; + +procedure TIdFTPServer.CommandHELP(ASender: TIdCommand); +var + s : String; + LCmds : TStringList; + i : Integer; + LExp : String; + + function ShouldShowCommand(const ACommand : String) : Boolean; + begin + Result := False; + case PosInStrArray(ACommand, HELP_SPEC_CMDS, False) of + -1 : + Result := True; + 0 : //'SIZE' + if Assigned(FOnGetFileSize) then begin + Result := True; + end; + 1 :// 'MDTM', + if Assigned(FOnGetFileDate) or Assigned(FTPFileSystem) then begin + Result := True; + end; + 2 : // 'AUTH' + if (FUseTLS in ExplicitTLSVals) then begin + Result := True; + end; + 3,4,5,6,7,8,9,10 : //'PBSZ','PROT', 'CCC','MIC','CONF','ENC','SSCN','CPSV', + if (FUseTLS <> utNoTLSSupport) then begin + Result := True; + end; + 11,12 : // 'MFMT','MFF', + if Assigned(FOnSetModifiedTime) or Assigned(FTPFileSystem) then begin + Result := True; + end; + 13,14, 15,16 : //'MD5','MMD5','XCRC','XMD5', + begin + Result := False; + if not GetFIPSMode then begin + if Assigned(FOnCRCFile) or Assigned(FTPFileSystem) then begin + Result := True; + end; + end; + end; + + 17 : // 'XSHA1', + if Assigned(FOnCRCFile) or Assigned(FTPFileSystem) then begin + Result := True; + end; + 18 : //'XSHA256' + if (Assigned(FOnCRCFile) or Assigned(FTPFileSystem)) + and TIdHashSHA256.IsAvailable then begin + Result := True; + end; + 19 : //'XSHA512' + if (Assigned(FOnCRCFile) or Assigned(FTPFileSystem)) and + TIdHashSHA512.IsAvailable then begin + Result := True; + end; + 20 : // 'COMB'); + if Assigned(OnCombineFiles) or Assigned(FTPFileSystem) then begin + Result := True; + end; + 21 : // AVBL + if Assigned(FOnAvailDiskSpace) then begin + Result := True; + end; + 22 : // DSIZ + if Assigned(FOnCompleteDirSize) then begin + Result := True; + end; + 23 : // RMDA + if Assigned(FOnRemoveDirectoryAll) then begin + Result := True; + end; + 24 : // HOST + if Assigned( FOnHostCheck ) then begin + Result := True; + end; + 25 : // XAUT + if (not GetFIPSMode) and Self.FSupportXAUTH then begin + Result := True; + end; + end; + end; + + function IsNotImplemented(const ACommand : String; var VHelp : String) : Boolean; + var + idx : Integer; + begin + Result := False; //presume that the command is implemented + idx := PosInStrArray(ACommand, HELP_ALT_MD_CMD, False); + if idx = -1 then begin + Exit; + end; + case idx of + 0 : // 'RETR' + begin + if (not Assigned(FOnRetrieveFile)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 1,2,3 : //'STOR','STOU', 'APPE', + begin + if (not Assigned(FOnStoreFile)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 4,5 : // 'RNFR', 'RNTO', + begin + if (not Assigned(FOnRenameFile)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 6 : // 'DELE', + begin + if (not Assigned(FOnDeleteFile)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 7,8 :// 'LIST','NLST', + begin + if (not Assigned(FOnListDirectory)) or + ((FDirFormat = ftpdfCustom) and (not Assigned(OnCustomListDirectory))) then begin + Result := True; + end; + end; + 9, 10, //'CWD','XCWD', + 11, 12 : // 'CDUP','XCUP', + begin + if (not Assigned(FOnChangeDirectory)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 13, 14 : //'RMD','XRMD', + begin + if (not Assigned(FOnRemoveDirectory)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 15,16 : //'MKD', 'XMKD', + begin + if (not Assigned(FOnMakeDirectory)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + 17 :// 'SYST', + begin + if (not Assigned(FOnMakeDirectory)) and (not Assigned(FFTPFileSystem)) then begin + Result := True; + end; + end; + end; + if Result then begin + LExp := HELP_ALT_MD_TP[idx]; + end; + end; + +begin + if ASender.Params.Count > 0 then begin + for i := 0 to CommandHandlers.Count-1 do begin + if TextIsSame(ASender.Params[0], CommandHandlers.Items[i].Command) then begin + if CommandHandlers.Items[i].HelpVisible and ShouldShowCommand(ASender.Params[0]) then begin + if IsNotImplemented(CommandHandlers.Items[i].Command, LExp) then begin + ASender.Reply.SetReply(214, LExp); + end else begin + ASender.Reply.SetReply(214, CommandHandlers.Items[i].Description.Text); + end; + end else begin + ASender.Reply.SetReply(502, IndyFormat(RSFTPCmdHelpNotKnown, [UpperCase(ASender.Params[0])])); + end; + Exit; + end; + end; + ASender.Reply.SetReply(502, IndyFormat(RSFTPCmdHelpNotKnown, [UpperCase(ASender.Params[0])])); + end else begin + s := RSFTPHelpBegining + EOL; + LCmds := TStringList.Create; + try + // + for i := 0 to CommandHandlers.Count -1 do begin + if CommandHandlers.Items[i].HelpVisible and ShouldShowCommand(CommandHandlers.Items[i].Command) then begin + if IsNotImplemented(CommandHandlers.Items[i].Command, LExp) then begin + LCmds.Add(CommandHandlers.Items[i].Command + '*'); {do not localize} + end else begin + LCmds.Add(CommandHandlers.Items[i].Command + CommandHandlers.Items[i].HelpSuperScript); + end; + end; + end; + LCmds.Sort; + s := s + HelpText(LCmds) + FEndOfHelpLine; + if FEndOfHelpLine = '' then begin + s := s + EOL; //prevent ugliness if last row out of alignment with the rest + end; + ASender.Reply.SetReply(214, s); + finally + FreeAndNil(LCmds); + end; + end; +end; + +procedure TIdFTPServer.CommandHOST(ASender: TIdCommand); +var LTmp : String; + LValid : Boolean; + LContext : TIdFTPServerContext; +begin + LContext := TIdFTPServerContext(ASender.Context); + if Assigned(OnHostCheck) then begin + if LContext.Username <> '' then begin + ASender.Reply.SetReply(530, RSFTPNotAfterAuthentication ); + Exit; + end; + if (ASender.Params.Count > 0) then begin + LTmp := ASender.Params[0]; + if Copy(LTmp,1,1)='[' then begin + Delete(LTmp,1,1); + end; + LTmp := Fetch(LTmp,']'); + LValid := False; + FOnHostCheck(LContext,LTmp,LValid); + if LValid then begin + LContext.Host := LTmp; + if Assigned(OnGreeting) then begin + OnGreeting(LContext,ASender.Reply); + end; + if ASender.Reply.NumericCode = 421 then begin + ASender.Disconnect := True; + end else begin + if not GetFIPSMode then begin + //setting the reply code number directly causes the text to be cleared + if FSupportXAUTH and (ASender.Reply.NumericCode = 220) then begin + XAutGreeting(LContext,ASender.Reply, LTmp); + end; + end; + ASender.Reply.SetReply(220,ASender.Reply.Text.Text); + end; + ASender.SendReply; + end else begin + ASender.Reply.SetReply(530,RSFTPHostNotFound); + end; + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.InitializeCommandHandlers; +var + LCmd : TIdCommandHandler; +begin + inherited InitializeCommandHandlers; + + //ACCESS CONTROL COMMANDS + //USER + LCmd := CommandHandlers.Add; + LCmd.Command := 'USER'; {Do not Localize} + LCmd.OnCommand := CommandUSER; + LCmd.Description.Text := 'Syntax: USER username'; {do not localize} + + //PASS + LCmd := CommandHandlers.Add; + LCmd.Command := 'PASS'; {Do not Localize} + LCmd.OnCommand := CommandPASS; + LCmd.Description.Text := 'Syntax: PASS password'; {do not localize} + + //ACCT + LCmd := CommandHandlers.Add; + LCmd.Command := 'ACCT'; {Do not Localize} + // LCMd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['ACCT'])); {do not localize} + LCmd.OnCommand := CommandACCT; + if Assigned(Self.FOnUserAccount) then begin + LCmd.HelpSuperScript := ''; //not supported + LCmd.Description.Text := ACCT_HELP_ENABLED; + end else begin + LCmd.HelpSuperScript := '*'; //not supported + LCmd.Description.Text := ACCT_HELP_DISABLED; + end; +// 'ACCT (specify account); unimplemented.'; {do not localize} + + { + LCmd.NormalReply.SetReply(502, Format(RSFTPCmdNotImplemented, ['ACCT'])); {Do not Localize} + + //CWD + LCmd := CommandHandlers.Add; + LCmd.Command := 'CWD'; {Do not Localize} + LCmd.OnCommand := CommandCWD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: CWD [ directory-name ]'; {do not localize} + + //CDUP + LCmd := CommandHandlers.Add; + LCmd.Command := 'CDUP'; {Do not Localize} + LCmd.OnCommand := CommandCDUP; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: CDUP (change to parent directory)'; {do not localize} + + //SMNT + LCmd := CommandHandlers.Add; + LCmd.Command := 'SMNT'; {Do not Localize} + LCmd.NormalReply.SetReply(502, RSFTPFileActionCompleted);//250 for success + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'SMNT (structure mount); unimplemented.'; {do not localize} + + //QUIT + LCmd := CommandHandlers.Add; + LCmd.Command := 'QUIT'; {Do not Localize} + LCmd.OnCommand := CommandQUIT; + LCmd.Disconnect := True; + LCmd.NormalReply.SetReply(221, RSFTPQuitGoodby); {Do not Localize} + LCmd.Description.Text := 'Syntax: QUIT (terminate service)'; {do not localize} + + //REIN + LCmd := CommandHandlers.Add; + LCmd.Command := 'REIN'; {Do not Localize} + LCmd.OnCommand := CommandREIN; + LCmd.Description.Text := 'Syntax: REIN (reinitialize server state)'; {do not localize} + + //PORT + LCmd := CommandHandlers.Add; + LCmd.Command := 'PORT'; {Do not Localize} + LCmd.OnCommand := CommandPORT; + LCmd.Description.Text := 'Syntax: PORT b0, b1, b2, b3, b4'; {do not localize} + + //PASV + LCmd := CommandHandlers.Add; + LCmd.Command := 'PASV'; {Do not Localize} + LCmd.OnCommand := CommandPASV; + LCmd.Description.Text := 'Syntax: PASV (set server in passive mode)'; {do not localize} + + //P@SW + //This is for some routers that replace a PASV with a P@SW + //as part of a misguided attempt to add a feature. + //A router would do a replacement so a client would think that + //PASV wasn't supported and then the client would do a PORT command + //instead. That doesn't happen so this just caused the client not to work. + //See: http://www.gbnetwork.co.uk/smcftpd/ + LCmd := CommandHandlers.Add; + LCmd.Command := 'P@SW'; {Do not Localize} + LCmd.OnCommand := CommandPASV; + LCmd.HelpVisible := False; //this is just a workaround + + //TYPE + LCmd := CommandHandlers.Add; + LCmd.Command := 'TYPE'; {Do not Localize} + LCmd.OnCommand := CommandTYPE; + LCmd.Description.Text := 'Syntax: TYPE [ A | E | I | L ]'; {do not localize} + + //STRU + LCmd := CommandHandlers.Add; + LCmd.Command := 'STRU'; {Do not Localize} + LCmd.OnCommand := CommandSTRU; + LCmd.Description.Text := 'Syntax: STRU (specify file structure)'; {do not localize} + + //MODE + LCmd := CommandHandlers.Add; + LCmd.Command := 'MODE'; {Do not Localize} + LCmd.OnCommand := CommandMODE; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: MODE (specify transfer mode)'; {do not localize} + + //FTP SERVICE COMMANDS + //RETR + LCmd := CommandHandlers.Add; + LCmd.Command := 'RETR'; {Do not Localize} + LCmd.OnCommand := CommandRETR; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: RETR file-name'; {do not localize} + + //STOR + LCmd := CommandHandlers.Add; + LCmd.Command := 'STOR'; {Do not Localize} + LCmd.OnCommand := CommandSSAP; + LCmd.ExceptionReply.NumericCode := 551; + LCmd.Description.Text := 'Syntax: STOR file-name'; {do not localize} + + //STOU + LCmd := CommandHandlers.Add; + LCmd.Command := 'STOU'; {Do not Localize} + LCmd.OnCommand := CommandSSAP; + LCmd.ExceptionReply.NumericCode := 551; + LCmd.Description.Text := 'Syntax: STOU file-name'; {do not localize} + + //APPE + LCmd := CommandHandlers.Add; + LCmd.Command := 'APPE'; {Do not Localize} + LCmd.OnCommand := CommandSSAP; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: APPE file-name'; {do not localize} + + //ALLO + // [ R ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'ALLO'; {Do not Localize} + LCmd.OnCommand := CommandALLO; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: ALLO allocate storage (vacuously)'; {do not localize} + + //REST + LCmd := CommandHandlers.Add; + LCmd.Command := 'REST'; {Do not Localize} + LCmd.OnCommand := CommandREST; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: REST (restart command)'; {do not localize} + + //RNFR + LCmd := CommandHandlers.Add; + LCmd.Command := 'RNFR'; {Do not Localize} + LCmd.OnCommand := CommandRNFR; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: RNFR file-name'; {do not localize} + + //RNTO + LCmd := CommandHandlers.Add; + LCmd.Command := 'RNTO'; {Do not Localize} + LCmd.OnCommand := CommandRNTO; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: RNTO file-name'; {do not localize} + + //ABOR + LCmd := CommandHandlers.Add; + LCmd.Command := 'ABOR'; {Do not Localize} + LCmd.OnCommand := CommandABOR; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: ABOR (abort operation)'; {do not localize} + + //DELE + LCmd := CommandHandlers.Add; + LCmd.Command := 'DELE'; {Do not Localize} + LCmd.OnCommand := CommandDELE; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: DELE file-name'; {do not localize} + +// 'SMNT (structure mount); unimplemented.'; + + //RMD + LCmd := CommandHandlers.Add; + LCmd.Command := 'RMD'; {Do not Localize} + LCmd.OnCommand := CommandRMD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: RMD path-name'; {do not localize} + + //MKD + LCmd := CommandHandlers.Add; + LCmd.Command := 'MKD'; {Do not Localize} + LCmd.OnCommand := CommandMKD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MKD path-name'; {do not localize} + + //PWD + LCmd := CommandHandlers.Add; + LCmd.Command := 'PWD'; {Do not Localize} + LCmd.OnCommand := CommandPWD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: PWD (return current directory)'; {do not localize} + + //LIST [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'LIST'; {Do not Localize} + LCmd.OnCommand := CommandLIST; + LCmd.ExceptionReply.NumericCode := 450; + LCmd.Description.Text := 'Syntax: LIST [ path-name ]'; {do not localize} + + //NLST [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'NLST'; {Do not Localize} + LCmd.OnCommand := CommandLIST; + LCmd.ExceptionReply.NumericCode := 450; + LCmd.Description.Text := 'Syntax: NLST [ path-name ]'; {do not localize} + + //SITE + LCmd := CommandHandlers.Add; + LCmd.Command := 'SITE'; {Do not Localize} + LCmd.OnCommand := CommandSITE; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE (site-specific commands)'; + + //SYST + LCmd := CommandHandlers.Add; + LCmd.Command := 'SYST'; {Do not Localize} + LCmd.OnCommand := CommandSYST; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SYST (get type of operating system)'; {do not localize} + + //STAT [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'STAT'; {Do not Localize} + LCmd.OnCommand := CommandSTAT; + LCmd.ExceptionReply.NumericCode := 450; + LCmd.Description.Text := 'Syntax: CWD [ directory-name ]'; {do not localize} + + //NOOP + LCmd := CommandHandlers.Add; + LCmd.Command := 'NOOP'; {Do not Localize} + LCmd.NormalReply.SetReply(200, IndyFormat(RSFTPCmdSuccessful, ['NOOP'])); {Do not Localize} + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: NOOP'; {do not localize} + + //RFC 775 + LCmd := CommandHandlers.Add; + LCmd.Command := 'XMKD'; {Do not Localize} + LCmd.OnCommand := CommandMKD; + LCmd.ExceptionReply.NumericCode := 551; //use the ones in parathensies + LCmd.Description.Text := 'Syntax: XMKD path-name'; {do not localize} + + //XCWD + LCmd := CommandHandlers.Add; + LCmd.Command := 'XCWD'; {Do not Localize} + LCmd.OnCommand := CommandCWD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: XCWD [ directory-name ]'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'XRMD'; {Do not Localize} + LCmd.OnCommand := CommandRMD; + LCmd.ExceptionReply.NumericCode := 551; //use the ones in parathensies + LCmd.Description.Text := 'Syntax: XRMD path-name'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'XPWD'; {Do not Localize} + LCmd.OnCommand := CommandPWD; + LCmd.ExceptionReply.NumericCode := 502; + LCmd.Description.Text := 'Syntax: PWD (return current directory)'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'XCUP'; {Do not Localize} + LCmd.OnCommand := CommandCDUP; + LCmd.ExceptionReply.NumericCode := 551; //use the ones in parathensies + LCmd.Description.Text := 'Syntax: XCUP (change to parent directory)'; {do not localize} + + //RFC 2389 + LCmd := CommandHandlers.Add; + LCmd.Command := 'FEAT'; {Do not Localize} + LCmd.OnCommand := CommandFEAT; + SetRFCReplyFormat(LCmd.NormalReply); + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: FEAT (returns feature list)'; {do not localize} + + //RFC 2389 + LCmd := CommandHandlers.Add; + LCmd.Command := 'OPTS'; {Do not Localize} + LCmd.OnCommand := CommandOPTS; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: OPTS command [ options]'; {do not localize} + + //SIZE [] CRLF + LCmd := CommandHandlers.Add; + LCmd.Command := 'SIZE'; {Do not Localize} + LCmd.OnCommand := CommandSIZE; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: SIZE path-name'; {do not localize} + + //EPSV [protocol] + LCmd := CommandHandlers.Add; + LCmd.Command := 'EPSV'; {Do not Localize} + LCmd.OnCommand := CommandEPSV; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: EPSV (returns port |||port|)'; {do not localize} + + //EPRT [address/port string] + LCmd := CommandHandlers.Add; + LCmd.Command := 'EPRT'; {Do not Localize} + LCmd.OnCommand := CommandEPRT; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: EPRT |proto|addr|port|'; {do not localize} + + //MDTM [] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MDTM'; {Do not Localize} + LCmd.OnCommand := CommandMDTM; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MDTM path-name'; {do not localize} + + //RFC 2228 + //AUTH [Mechanism] + LCmd := CommandHandlers.Add; + LCmd.Command := 'AUTH'; {Do not translate} + LCmd.OnCommand := CommandAUTH; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: AUTH mechanism-name'; {do not localize} + + //PBSZ [Protection Buffer Size] + LCmd := CommandHandlers.Add; + LCmd.Command := 'PBSZ'; {Do not translate} + LCmd.OnCommand := CommandPBSZ; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: PBSZ protection buffer size'; {do not localize} + + //PROT Protection Type + LCmd := CommandHandlers.Add; + LCmd.Command := 'PROT'; {Do not translate} + LCmd.OnCommand := CommandPROT; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: PROT protection code'; {do not localize} + + //CCC Clear Command Channel + LCmd := CommandHandlers.Add; + LCmd.Command := 'CCC'; {Do not translate} + LCmd.OnCommand := CommandCCC; + LCmd.Description.Text := 'Syntax: CCC (clear command channel)'; {do not localize} + + //MIC Integrity Protected Command + LCmd := CommandHandlers.Add; + LCmd.Command := 'MIC'; {Do not translate} + LCmd.OnCommand := CommandSecRFC; + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MIC (integrity protected command); unimplemented.'; {do not localize} + + //CONF Confidentiality protected command + LCmd := CommandHandlers.Add; + LCmd.Command := 'CONF'; {Do not translate} + LCmd.OnCommand := CommandSecRFC; + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'CONF (confidentiality protected command); unimplemented.'; {do not localize} + + //ENC Privacy Protected command + LCmd := CommandHandlers.Add; + LCmd.Command := 'ENC'; {Do not translate} + LCmd.OnCommand := CommandSecRFC; + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'ENC (privacy protected command); unimplemented.'; {do not localize} + + //These are from IETF Draft "Extensions to FTP" + //MLSD [Pathname] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MLSD'; {Do not translate} + LCmd.OnCommand := CommandMLSD; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MLSD [ path-name ]'; {do not localize} + + //MLST [Pathname] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MLST'; {Do not translate} + LCmd.OnCommand := CommandMLST; + SetRFCReplyFormat(LCmd.NormalReply); + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MLST [ path-name ]'; {do not localize} + + //Defined in http://www.trevezel.com/downloads/draft-somers-ftp-mfxx-00.html + //Modify File Modification Time + //MFMT [ATime] [Path-name] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MFMT'; {Do not translate} + LCmd.OnCommand := CommandMFMT; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MFMT [ATime] [Path-name]'; {do not localize} + + //Defined in http://www.trevezel.com/downloads/draft-somers-ftp-mfxx-00.html + //Modify File Creation Time + //MFMT [ATime] [Pathname] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MFCT'; {Do not translate} + LCmd.OnCommand := CommandMFCT; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MFCT [ATime] [Path-name]'; {do not localize} + + //params are the same format as the MLS output + LCmd := CommandHandlers.Add; + LCmd.Command := 'MFF'; {Do not translate} + LCmd.OnCommand := CommandMFF; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: MFF [ mff-facts ] SP path-name'; {do not localize} + + //From http://www.ietf.org/internet-drafts/draft-twine-ftpmd5-00.txt + //MD5 [Pathname] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MD5'; {Do not translate} + LCmd.OnCommand := CommandMD5; + LCmd.ExceptionReply.NumericCode := 504; + LCmd.Description.Text := 'Syntax: MD5 [Pathname]'; {do not localize} + + //MMD5 [Filepath1], [Filepath2] [...] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MMD5'; {Do not translate} + LCmd.OnCommand := CommandMMD5; + LCmd.ExceptionReply.NumericCode := 504; + LCmd.Description.Text := 'Syntax: MMD5 [Filepath1], [Filepath2] [...]'; {do not localize} + + //These two commands are not in RFC's or drafts + // but are documented in: + // GlobalSCAPE Secure FTP Server Users Guide + //XCRC "[filename]" [start] [finish] + LCmd := CommandHandlers.Add; + LCmd.Command := 'XCRC'; {Do not translate} + LCmd.OnCommand := CommandCheckSum; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: XCRC "[file-name]" [start] [finish]'; {do not localize} + + //COMB "[filename]" [start] [finish] + LCmd := CommandHandlers.Add; + LCmd.Command := 'COMB'; {Do not translate} + LCmd.OnCommand := CommandCOMB; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: COMB "[file-name]" [start] [finish]'; {do not localize} + + //informal but we might want to support this anyway + //SSCN - specified by: + //http://www.raidenftpd.com/kb/kb000000037.htm + LCmd := CommandHandlers.Add; + LCmd.Command := 'SSCN'; {Do not translate} + LCmd.OnCommand := CommandSSCN; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.NormalReply.NumericCode := 200; + LCmd.Description.Text := 'Syntax: SSCN [ON|OFF]'; {do not localize} + + //CPSV + LCmd := CommandHandlers.Add; + LCmd.Command := 'CPSV'; {Do not Localize} + LCmd.OnCommand := CommandCPSV; + LCmd.Description.Text := 'Syntax: CPSV (set server in passive mode with SSL Connect)'; {do not localize} + + //Seen in RaidenFTPD documentation + //XCRC "[filename]" [start] [finish] + LCmd := CommandHandlers.Add; + LCmd.Command := 'XMD5'; {Do not translate} + LCmd.OnCommand := CommandCheckSum; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: XMD5 "[filename]" [start] [finish]'; {do not localize} + + //Seen in RaidenFTPD documentation + //XCRC "[filename]" [start] [finish] + LCmd := CommandHandlers.Add; + LCmd.Command := 'XSHA1'; {Do not translate} + LCmd.OnCommand := CommandCheckSum; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: XSHA1 "[filename]" [start] [finish]'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'XSHA256'; {Do not translate} + LCmd.OnCommand := CommandCheckSum; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'Syntax: XSHA256 "[filename]" [start] [finish]'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'XSHA512'; {Do not translate} + LCmd.OnCommand := CommandCheckSum; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.HelpVisible := True; + LCmd.Description.Text := 'Syntax: XSHA512 "[filename]" [start] [finish]'; {do not localize} + +//commands from +// draft-peterson-streamlined-ftp-command-extensions-01.txt +//http://tools.ietf.org/html/draft-peterson-streamlined-ftp-command-extensions-01#section-2.4 + LCmd := CommandHandlers.Add; + LCmd.Command := 'AVBL'; {Do not localize} + LCmd.OnCommand := CommandAVBL; + LCmd.ExceptionReply.NumericCode := 500; + LCmd.Description.Text := 'Syntax: AVBL [ dirpath] (returns the number of '+ + 'bytes available for uploading in the directory or current working directory)'; + + LCmd := CommandHandlers.Add; + LCmd.Command := 'DSIZ'; {Do not localize} + LCmd.OnCommand := CommandDSIZ; + LCmd.ExceptionReply.NumericCode := 500; + LCmd.Description.Text := 'DSIZ [ dirpath] (returns the number of bytes '+ + 'in the directory or current working directory, including sub directories)'; + + LCmd := CommandHandlers.Add; + LCmd.Command := 'RMDA'; + LCmd.OnCommand := CommandRMDA; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.Description.Text := 'RMDA pathname (deletes (removes) the '+ + 'specified directory and it s contents)'; + + //informal but we might want to support this anyway + //CLNT + LCmd := CommandHandlers.Add; + LCmd.Command := 'CLNT'; {do not localize} + LCmd.OnCommand := CommandCLNT; + LCmd.ExceptionReply.NumericCode := 550; + LCmd.NormalReply.SetReply(200, RSFTPClntNoted); {Do not Localize} + LCmd.Description.Text := 'Syntax: CLNT'; {do not localize} + + //Informal - an old proposed solution to IPv6 support in FTP. + //Mentioned at: http://cr.yp.to/ftp/retr.html + //and supported by PureFTPD. + LCmd := CommandHandlers.Add; + LCmd.Command := 'SPSV'; {do not localize} + LCmd.OnCommand := CommandSPSV; + LCmd.Description.Text := 'Syntax: SPSV (set server in passive mode)'; {do not localize} + + LCmd := CommandHandlers.Add; + LCmd.Command := 'HOST'; {Do not localize} + LCmd.OnCommand := CommandHOST; + LCmd.ExceptionReply.NumericCode := 504; + LCmd.Description.Text := 'Syntax: HOST domain (select a domain prior to logging in)'; {Do not localize} + + //Note that these commands are mentioned in old RFC's + //and we will not support them at all. The commands + //were there because FTP was a predisessor of SMTP + //These are from RFC 765 + //MLFL [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MLFL'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MLFL'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MLFL (mail file); unimplemented.'; {do not localize} + + //MAIL [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MAIL'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MAIL'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MAIL (mail to user); unimplemented.'; {do not localize} + + // MSND [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MSND'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MSND'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MSND (mail send to terminal); unimplemented.'; {do not localize} + + // MSOM [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MSOM'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MSOM'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MSOM (mail send to terminal or mailbox); unimplemented.'; {do not localize} + + // MSAM [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MSAM'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MSAM'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MSAM (mail send to terminal and mailbox); unimplemented.'; {do not localize} + + // MRSQ [ ] + LCmd := CommandHandlers.Add; + LCmd.Command := 'MRSQ'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MRSQ'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MRSQ (mail recipient scheme question); unimplemented.'; {do not localize} + + // MRCP + LCmd := CommandHandlers.Add; + LCmd.Command := 'MRCP'; {Do not Localize} + LCmd.NormalReply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['MRCP'])); {Do not Localize} + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := 'MRCP (mail recipient); unimplemented.'; {do not localize} + // + LCmd := CommandHandlers.Add; + LCmd.Command := 'HELP'; {Do not Localize} + LCmd.OnCommand := COmmandHELP; + LCmd.NormalReply.NumericCode := 214; + LCmd.Description.Text := 'Syntax: HELP [ ]'; {do not localize} + +//We use a separate command handler collection for some things which are +//valid durring the data connection. + //ABOR + LCmd := FDataChannelCommands.Add; + LCmd.Command := 'ABOR'; {Do not Localize} + LCmd.OnCommand := CommandABOR; + LCmd.ExceptionReply.NumericCode := 550; + + //STAT [ ] + LCmd := FDataChannelCommands.Add; + LCmd.Command := 'STAT'; {Do not Localize} + LCmd.OnCommand := CommandSTAT; + LCmd.ExceptionReply.NumericCode := 450; + +//This is for SITE commands to make it easy for the user to add their own site commands +//as they wish + //These are Unix site commands + LCmd := FSITECommands.Add; + LCmd.Command := 'HELP'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.OnCommand := CommandSiteHELP; + LCmd.Description.Text := 'Syntax: SITE HELP [ ]'; {do not localize} + + //SITE ATTRIBAttribsFileName + LCmd := FSITECommands.Add; + LCmd.Command := 'ATTRIB'; {Do not Localize} + LCmd.OnCommand := CommandSiteATTRIB; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE ATTRIBAttribsFilename'; {do not localize} + + //SITE UMASK[mask] + LCmd := FSITECommands.Add; + LCmd.Command := 'UMASK'; {Do not Localize} + LCmd.OnCommand := CommandSiteUMASK; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE UMASK'; {do not localize} + //SITE CHMODPermission numbersFilename + LCmd := FSITECommands.Add; + LCmd.Command := 'CHMOD'; {Do not Localize} + LCmd.OnCommand := CommandSiteCHMOD; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE CHMODPermission numbersFilename'; {do not localize} + + //additional Unix server commands that aren't supported but should be supported, IMAO + //SITE CHOWNOwner[:Group]Filename + LCmd := FSITECommands.Add; + LCmd.Command := 'CHOWN'; {Do not Localize} + LCmd.OnCommand := CommandSiteCHOWN; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE CHOWNOwner[:Group]Filename'; {do not localize} + + //SITE CHGRPGroupFilename + LCmd := FSITECommands.Add; + LCmd.Command := 'CHGRP'; {Do not Localize} + LCmd.OnCommand := CommandSiteCHGRP; + LCmd.ExceptionReply.NumericCode := 501; + LCmd.Description.Text := 'Syntax: SITE CHGRPGroupFilename'; {do not localize} + + //Microsoft IIS SITE commands + //SITE DIRSTYLE + LCmd := FSITECommands.Add; + LCmd.Command := 'DIRSTYLE'; {Do not Localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.OnCommand := CommandSiteDIRSTYLE; + LCmd.Description.Text := 'Syntax: SITE DIRSTYLE (toggle directory format)'; {do not localize} + + //SITE ZONE + LCmd := FSITECommands.Add; + LCmd.Command := 'ZONE'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 530; + LCmd.OnCommand := CommandSiteZONE; + LCmd.Description.Text := 'Syntax: SITE ZONE (returns the server offset from GMT)'; {do not localize} + + //SITE UTIME + LCmd := FSITECommands.Add; + LCmd.Command := 'UTIME'; {Do not localize} + LCmd.NormalReply.NumericCode := 200; + LCmd.NormalReply.Text.Text := 'Date/time changed okay.'; + LCmd.ExceptionReply.NumericCode := 530; + LCmd.OnCommand := CommandSiteUTIME; + LCmd.Description.Text := + 'Syntax: SITE UTIME '+CR+LF+ {do not localize} + ' Each timestamp must be in the format YYYYMMDDhhmmss'; {do not localize} + + //OPTS MLST + LCmd := FOPTSCommands.Add; + LCmd.Command := 'MLST'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.OnCommand := CommandOptsMLST; + + //OPTS MODE Z + LCmd := FOPTSCommands.Add; + LCmd.Command := 'MODE Z'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.OnCommand := CommandOptsMODEZ; + + // OPTS UTF-8 + LCmd := FOPTSCommands.Add; + LCmd.Command := 'UTF-8'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.NormalReply.NumericCode := 200; + LCmd.OnCommand := CommandOptsUTF8; + + // OPTS UTF8 + LCmd := FOPTSCommands.Add; + LCmd.Command := 'UTF8'; {Do not localize} + LCmd.ExceptionReply.NumericCode := 501; + LCmd.NormalReply.NumericCode := 200; + LCmd.OnCommand := CommandOptsUTF8; + + //XAUT + LCmd := CommandHandlers.Add; + LCmd.Command := 'XAUT'; {Do not Localize} + LCmd.OnCommand := CommandXAUT; + LCmd.Description.Text := 'Syntax: XAUT 2 '; {do not localize} +end; + +procedure TIdFTPServer.ContextCreated(AContext: TIdContext); +var + LContext : TIdFTPServerContext; +begin + LContext := (AContext as TIdFTPServerContext); + LContext.Server := Self; + //from Before run method + LContext.FDataPort := 0; + LContext.FPasswordAttempts := 0; + LContext.FDataPortDenied := False; + LContext.FUserSecurity.Assign(FTPSecurityOptions); + if (DirFormat = ftpdfOSDependent) and (GOSType = otWindows) then begin + LContext.MSDOSMode := True; + end; + // + if mlsdUnixModes in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [UnixMODE]; + end; + if mlsdUnixOwner in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [UnixOwner]; + end; + if mlsdUnixGroup in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [UnixGroup]; + end; + if mlsdFileCreationTime in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [CreateTime]; + end; + if mlsdPerms in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [Perm]; + end; + if mlsdUniqueID in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [Unique]; + end; + if mlsdFileLastAccessTime in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [LastAccessTime]; + end; + if mlsdWin32Attributes in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [WinAttribs]; + end; + if mlsdWin32DriveType in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [WinDriveType]; + end; + if mlstWin32DriveLabel in FMLSDFacts then begin + LContext.MLSOpts := LContext.MLSOpts + [WinDriveLabel]; + end; + //MS-DOS mode on for MS-DOS + if FDirFormat = ftpdfDOS then begin + LContext.FMSDOSMode := True; + end; +end; + +destructor TIdFTPServer.Destroy; +begin + FreeAndNil(FAnonymousAccounts); + FreeAndNil(FFTPSecurityOptions); + FreeAndNil(FOPTSCommands); + FreeAndNil(FDataChannelCommands); + FreeAndNil(FSITECommands); + FreeAndNil(FReplyUnknownSITECommand); + inherited Destroy; +end; + +procedure TIdFTPServer.ListDirectory(ASender: TIdFTPServerContext; ADirectory: string; + ADirContents: TStrings; ADetails: Boolean; const ACmd : String = 'LIST'; + const ASwitches : String = ''); {do not localize} +var + LDirectoryList: TIdFTPListOutput; + LPathSep: string; + LIsMLST: Boolean; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFilesystem; +begin + LIsMLST := PosInStrArray(ACmd, ['MLSD', 'MLST']) <> -1; {do not localize} + if (FDirFormat = ftpdfCustom) and (not LIsMLST) then begin + DoOnCustomListDirectory(ASender, ADirectory, ADirContents, ACmd, ASwitches); + Exit; + end; + LFileSystem := FFTPFileSystem; + if Assigned(FOnListDirectory) or Assigned(LFileSystem) then begin + LDirectoryList := TIdFTPListOutput.Create; + try + case FDirFormat of + ftpdfEPLF : + LDirectoryList.DirFormat := doEPLF; + ftpdfDOS : + if ASender.FMSDOSMode then begin + LDirectoryList.DirFormat := DoWin32; + end else begin + LDirectoryList.DirFormat := DoUnix; + end; + ftpdfOSDependent : + if (GOSType = otWindows) and (ASender.FMSDOSMode) then begin + LDirectoryList.DirFormat := DoWin32; + end else begin + LDirectoryList.DirFormat := DoUnix; + end; + else + LDirectoryList.DirFormat := DoUnix; + end; + //someone might be using the STAT -l to get a dir without a data channel + if IndyPos('l', ASwitches) > 0 then begin + LDirectoryList.Switches := LDirectoryList.Switches + 'l'; + end; + //we do things this way because the 'a' and 'T' swithces only make sense + //when listing Unix dirs. + if SupportTaDirSwitches(ASender) then begin + if IndyPos('a', ASwitches) > 0 then begin + LDirectoryList.Switches := LDirectoryList.Switches + 'a'; + end; + if IndyPos('T', ASwitches) > 0 then begin + LDirectoryList.Switches := LDirectoryList.Switches + 'T'; + end; + end; + LDirectoryList.ExportTotalLine := True; + LPathSep := '/'; {Do not Localize} + if not TextEndsWith(ADirectory, LPathSep) then begin + ADirectory := ADirectory + LPathSep; + end; + if Assigned(LFileSystem) then begin + LFileSystem.ListDirectory(ASender, ADirectory, LDirectoryList, ACmd, ASwitches); + end else begin + FOnListDirectory(ASender, ADirectory, LDirectoryList, ACmd, ASwitches); // Event + end; + if LIsMLST then begin {Do not translate} + LDirectoryList.MLISTOutputDir(ADirContents, ASender.MLSOpts); + end + else if ADetails then begin + LDirectoryList.LISTOutputDir(ADirContents); + end else begin + LDirectoryList.NLISTOutputDir(ADirContents); + end; + finally + FreeAndNil(LDirectoryList); + end; + end else begin + raise EIdFTPServerNoOnListDirectory.Create(RSFTPNoOnDirEvent); {Do not Localize} + end; +end; + +procedure TIdFTPServer.SetUserAccounts(const AValue: TIdCustomUserManager); +var + // under ARC, convert a weak reference to a strong reference before working with it + LUserAccounts: TIdCustomUserManager; +begin + LUserAccounts := FUserAccounts; + + if LUserAccounts <> AValue then begin + // under ARC, all weak references to a freed object get nil'ed automatically + + {$IFNDEF USE_OBJECT_ARC} + if Assigned(LUserAccounts) then begin + LUserAccounts.RemoveFreeNotification(Self); + end; + {$ENDIF} + + FUserAccounts := AValue; + + if Assigned(AValue) then begin + {$IFNDEF USE_OBJECT_ARC} + AValue.FreeNotification(Self); + {$ENDIF} + FOnUserAccount := nil; + //XAUT can not work with an account manager that sends + //a challange because that command is a USER/PASS rolled into + //one command. + if AValue.SendsChallange then begin + FSupportXAUTH := False; + end; + end; + end; +end; + +procedure TIdFTPServer.SetFTPFileSystem(const AValue: TIdFTPBaseFileSystem); +begin + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FFTPFileSystem := AValue; + {$ELSE} + if FFTPFileSystem <> AValue then begin + if Assigned(FFTPFileSystem) then begin + FFTPFileSystem.RemoveFreeNotification(Self); + end; + FFTPFileSystem := AValue; + if Assigned(AValue) then begin + AValue.FreeNotification(Self); + end; + end; + {$ENDIF} +end; + +procedure TIdFTPServer.SetReplyUnknownSITECommand(AValue: TIdReply); +begin + FReplyUnknownSITECommand.Assign(AValue); +end; + +procedure TIdFTPServer.SetSITECommands(AValue: TIdCommandHandlers); +begin + FSITECommands.Assign(AValue); +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +{$IFNDEF USE_OBJECT_ARC} +procedure TIdFTPServer.Notification(AComponent: TComponent; Operation: TOperation); +begin + if Operation = opRemove then begin + if AComponent = FUserAccounts then begin + FUserAccounts := nil; + end + else if AComponent = FFTPFileSystem then begin + FFTPFileSystem := nil; + end; + end; + inherited Notification(AComponent, Operation); +end; +{$ENDIF} + +procedure TIdFTPServer.SetAnonymousAccounts(const AValue: TStrings); +begin + if Assigned(AValue) then begin + FAnonymousAccounts.Assign(AValue); + end; +end; + +procedure TIdFTPServer.SetSupportXAUTH(AValue : Boolean); +var + // under ARC, convert a weak reference to a strong reference before working with it + LUserAccounts: TIdCustomUserManager; +begin + if FSupportXAUTH <> AValue then begin + LUserAccounts := FUserAccounts; + if Assigned(LUserAccounts) then begin + if LUserAccounts.SendsChallange then begin + Exit; + end; + end; + FSupportXAUTH := AValue; + end; +end; + +procedure TIdFTPServer.ThreadException(AThread: TIdThread; AException: Exception); +begin + //we do not want to show an exception in a dialog-box +end; + +//Command Replies/Handling +procedure TIdFTPServer.CommandUSER(ASender: TIdCommand); +var + LSafe: Boolean; + LChallenge: String; + LContext: TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LUserAccounts: TIdCustomUserManager; +begin + LChallenge := ''; + LContext := ASender.Context as TIdFTPServerContext; + if (FUseTLS = utUseRequireTLS) and (LContext.AuthMechanism <> 'TLS') then begin {do not localize} + DisconUser(ASender); + Exit; + end; + LContext.Authenticated := False; + if (FAnonymousAccounts.IndexOf(LowerCase(ASender.UnparsedParams)) >= 0) and AllowAnonymousLogin then begin + LContext.UserType := utAnonymousUser; + LContext.Username := ASender.UnparsedParams; + ASender.Reply.SetReply(331, RSFTPAnonymousUserOkay); + end else begin + LContext.UserType := utNormalUser; + if Length(ASender.UnparsedParams) > 0 then begin + LContext.Username := ASender.UnparsedParams; + LUserAccounts := FUserAccounts; + if Assigned(LUserAccounts) then begin + LChallenge := LUserAccounts.ChallengeUser(LSafe, LContext.Username); + {$IFDEF USE_OBJECT_ARC}LUserAccounts := nil;{$ENDIF} + if not LSafe then begin + //we do this to prevent a potential race attack + DisconUser(ASender); + Exit; + end; + end; + if LChallenge = '' then begin + ASender.Reply.SetReply(331, RSFTPUserOkay); + end else begin + ASender.Reply.SetReply(331, LChallenge); + end; + end else begin + ASender.Reply.SetReply(332, RSFTPNeedAccountForLogin); + end; + end; +end; + +procedure TIdFTPServer.AuthenticateUser(ASender: TIdCommand); +var + LValidated: Boolean; + LContext: TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LUserAccounts: TIdCustomUserManager; +begin + LContext:= ASender.Context as TIdFTPServerContext; + try + LContext.FAuthenticated := False; + case LContext.FUserType of + utAnonymousUser: + begin + LValidated := Length(LContext.Password ) > 0; + if FAnonymousPassStrictCheck and LValidated then begin + LValidated := False; + if FindFirstOf('@.', LContext.Password) > 0 then begin {Do not Localize} + LValidated := True; + end; + end; + if LValidated then begin + LContext.FAuthenticated := True; + ASender.Reply.SetReply(230, RSFTPAnonymousUserLogged); + if Assigned(OnLoginSuccessBanner) then begin + OnLoginSuccessBanner(LContext, ASender.Reply); + ASender.Reply.SetReply(230, ASender.Reply.Text.Text); + end; + LContext.FPasswordAttempts := 0; + end else begin + LContext.FUserType := utNone; + LContext.FAuthenticated := False; + LContext.FPassword := ''; {Do not Localize} + Inc(LContext.FPasswordAttempts); + if LContext.UserSecurity.InvalidPassDelay > 0 then begin + //Delay our error response to slow down a dictionary attack + IndySleep(FFTPSecurityOptions.InvalidPassDelay); + end; + if (LContext.UserSecurity.PasswordAttempts > 0) and + (LContext.FPasswordAttempts >= LContext.UserSecurity.PasswordAttempts) then begin + DisconUser(ASender); + Exit; + end; + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); + end; + end; + utNormalUser: + begin + LUserAccounts := FUserAccounts; + if Assigned(LUserAccounts) then begin + LContext.FAuthenticated := LUserAccounts.AuthenticateUser(LContext.FUsername, ASender.UnparsedParams); + {$IFDEF USE_OBJECT_ARC}LUserAccounts := nil;{$ENDIF} + if LContext.FAuthenticated then begin + LContext.FPasswordAttempts := 0; + ASender.Reply.SetReply(230, RSFTPUserLogged); + end else begin + LContext.FPassword := ''; {Do not Localize} + Inc(LContext.FPasswordAttempts); + if LContext.UserSecurity.InvalidPassDelay > 0 then begin + //Delay our error response to slow down a dictionary attack + IndySleep(LContext.UserSecurity.InvalidPassDelay); + end; + if (LContext.UserSecurity.PasswordAttempts > 0) and + (LContext.FPasswordAttempts >= LContext.UserSecurity.PasswordAttempts) then + begin + //Max login attempts exceeded, close the connection + DisconUser(ASender); + Exit; + end; + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); + end; + end + else if Assigned(FOnUserLogin) then begin + LValidated := False; + FOnUserLogin(LContext, LContext.FUsername, LContext.Password, LValidated); + LContext.FAuthenticated := LValidated; + if LValidated then begin + if (LContext.AccountNeeded = True) and Assigned(FOnUserAccount) then begin + LContext.FAuthenticated := False; + ASender.Reply.SetReply(332,'Need account for login.'); + Exit; + end else begin + LContext.FAuthenticated := LValidated; + ASender.Reply.SetReply(230, RSFTPUserLogged); + if Assigned(OnLoginSuccessBanner) then begin + OnLoginSuccessBanner(LContext, ASender.Reply); + ASender.Reply.SetReply(230, ASender.Reply.Text.Text); + end; + LContext.FPasswordAttempts := 0; + end; + end else begin + LContext.FPassword := ''; {Do not Localize} + Inc(LContext.FPasswordAttempts); + if (LContext.UserSecurity.PasswordAttempts > 0) and + (LContext.FPasswordAttempts >= LContext.UserSecurity.PasswordAttempts) then begin + //Max login attempts exceeded, close the connection + DisconUser(ASender); + Exit; + end; + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); + end; + end else begin + //APR 020423 + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); // user manager not found + end; + end; + else + ASender.Reply.SetReply(503, RSFTPNeedLoginWithUser); + end;//case + except + on E : Exception do begin + ASender.Reply.SetReply(503, E.Message); + end; + end; + //After login + if LContext.FAuthenticated and Assigned(FOnAfterUserLogin) then begin + FOnAfterUserLogin(LContext); + end; +end; + +procedure TIdFTPServer.CommandPASS(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; +begin + LContext:= ASender.Context as TIdFTPServerContext; + if (FUseTLS = utUseRequireTLS) and (LContext.AuthMechanism <> 'TLS') then begin {do not localize} + DisconUser(ASender); + Exit; + end; + LContext.FAuthenticated := False; + LContext.FPassword := ASender.UnparsedParams; + AuthenticateUser(ASender); +end; + +procedure TIdFTPServer.CommandXAUT(ASender : TIdCommand); +var + LContext : TIdFTPServerContext; + s : String; + LPos : Integer; +begin + LContext := ASender.Context as TIdFTPServerContext; + if (FUseTLS = utUseRequireTLS) and (LContext.AuthMechanism <> 'TLS') then begin {do not localize} + DisconUser(ASender); + Exit; + end; + LContext := ASender.Context as TIdFTPServerContext; + s := ASender.UnparsedParams; + s := IdFTPCommon.ExtractAutInfoFromXAUT(s, LContext.FXAUTKey ); + LPos := RPos(':',s); + if LPos > 1 then begin + LContext.Username := Copy(s,1,LPos - 1); + s := Copy(s,LPos + 1,$FF); + //for some reason, WS-FTP Pro likes to add the string "^vta4r2" to + //the authentication information if you aren't using anonymous login. + //I'm not sure what the significance of "^vta4r2" really is. + // 1234567 + if TextEndsWith(s,'^vta4r2') then begin + LContext.Password := Copy(s,1,Length(s)-7); + end; + end else begin + LContext.Username := s; + LContext.Password := ''; + end; + LContext.Authenticated := False; + if (FAnonymousAccounts.IndexOf(LowerCase(ASender.UnparsedParams)) >= 0) and AllowAnonymousLogin then begin + LContext.UserType := utAnonymousUser; + end else begin + LContext.UserType := utNormalUser; + end; + AuthenticateUser(ASender); +end; + +procedure TIdFTPServer.CommandACCT(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LValidated : Boolean; +begin + LValidated := False; + if Assigned(FOnUserAccount) then begin + LContext := ASender.Context as TIdFTPServerContext; + LContext.Account := ASender.UnparsedParams; + FOnUserAccount(LContext,LContext.Username, LContext.Password, LContext.Account, LValidated); + LContext.Authenticated := LValidated; + if LValidated then begin + LContext.AccountNeeded := False; + ASender.Reply.SetReply(230, RSFTPUserLogged); + if Assigned(OnLoginSuccessBanner) then begin + OnLoginSuccessBanner(LContext, ASender.Reply); + ASender.Reply.SetReply(230, ASender.Reply.Text.Text); + LContext.PasswordAttempts := 0; + end; + end else begin + LContext.FPassword := ''; {Do not Localize} + Inc(LContext.FPasswordAttempts); + if (LContext.UserSecurity.PasswordAttempts > 0) and + (LContext.FPasswordAttempts >= LContext.UserSecurity.PasswordAttempts) then begin + //Max login attempts exceeded, close the connection + DisconUser(ASender); + Exit; + end; + ASender.Reply.SetReply(530, RSFTPUserNotLoggedIn); + end; + end else begin + ASender.Reply.SetReply(502, IndyFormat(RSFTPCmdNotImplemented, ['ACCT'])); {do not localize} + end; +end; + +procedure TIdFTPServer.CommandCWD(ASender: TIdCommand); +var + s: TIdFTPFileName; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFilesystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + s := ASender.UnparsedParams; + if LContext.IsAuthenticated(ASender) then begin + s := IgnoreLastPathDelim(s); + LFileSystem := FFTPFileSystem; + if Assigned(OnChangeDirectory) or Assigned(LFileSystem) then begin + if s = '..' then begin {do not localize} + s := CDUPDir(LContext); + end + else if s = '.' then begin {do not localize} + s := LContext.CurrentDir; + end else begin + s := DoProcessPath(LContext, s); + end; + s := RemoveDuplicatePathSyms(s); + DoOnChangeDirectory(LContext, s); + LContext.CurrentDir := s; + CmdCommandSuccessful(ASender); + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandCDUP(ASender: TIdCommand); +var + s: TIdFTPFileName; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := CDUPDir(LContext); + s := DoProcessPath(LContext, s); + LFileSystem := FFTPFileSystem; + if Assigned(FOnChangeDirectory) or Assigned(LFileSystem) then begin + DoOnChangeDirectory(LContext, s); + LContext.FCurrentDir := s; + ASender.Reply.SetReply(250, IndyFormat(RSFTPCurrentDirectoryIs, [LContext.FCurrentDir])); + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandREIN(ASender: TIdCommand); +var + LIO : TIdSSLIOHandlerSocketBase; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LContext.ReInitialize; + LContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; + ASender.Reply.SetReply(220, RSFTPServiceOpen); + if (FUseTLS in ExplicitTLSVals) then begin + LIO := ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase; + if not LIO.PassThrough then begin + LIO.Passthrough := True; + end; + LContext.FCCC := False; + end; + end; +end; + +procedure TIdFTPServer.CommandPORT(ASender: TIdCommand); +var + LLo, LHi : Integer; + LPort: TIdPort; + LParm, LIP : string; + LContext : TIdFTPServerContext; + LDataChannel: TIdTCPClient; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if LContext.FEPSVAll then begin + ASender.Reply.SetReply(501, IndyFormat(RSFTPNotAllowedAfterEPSVAll, [ASender.CommandHandler.Command])); + Exit; + end; + if LContext.UserSecurity.BlockAllPORTTransfers then + begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(502, RSFTPPORTDisabled); + Exit; + end; + LContext.FPASV := False; + LParm := ASender.UnparsedParams; + LIP := ''; {Do not Localize} + { h1 } + LIP := LIP + Fetch(LParm, ',') + '.'; {Do not Localize} + { h2 } + LIP := LIP + Fetch(LParm, ',') + '.'; {Do not Localize} + { h3 } + LIP := LIP + Fetch(LParm, ',') + '.'; {Do not Localize} + { h4 } + LIP := LIP + Fetch(LParm, ','); {Do not Localize} + { p1 } + LLo := IndyStrToInt(Fetch(LParm, ',')); {Do not Localize} + { p2 } + LHi := IndyStrToInt(LParm); + LPort := TIdPort((LLo * 256) + LHi); + if LContext.UserSecurity.NoReservedRangePORT and + ((LPort > 0) and (LPort <= 1024)) then begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(504, RSFTPPORTRange); + Exit; + end; + {//BGO} + if LContext.UserSecurity.FRequirePORTFromSameIP and + (LIP <> LContext.Binding.PeerIP) then begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(504, RSFTPSameIPAddress); + Exit; + end; + {//BGO} + LContext.CreateDataChannel(False); + LDataChannel := TIdTCPClient(LContext.FDataChannel.FDataChannel); + LDataChannel.Host := LIP; + LDataChannel.Port := LPort; + LDataChannel.IPVersion := Id_IPv4; + LContext.FDataPort := LPort; + LContext.FDataPortDenied := False; + CmdCommandSuccessful(ASender, 200); + end; +end; + +procedure TIdFTPServer.CommandPASV(ASender: TIdCommand); +var + LParam: string; + LBPort: Word; + LIPVersion : TIdIPVersion; +begin + //InternalPASV does all of the checking + if InternalPASV(ASender, LParam, LBPort, LIPVersion) then begin + DoOnPASVReply(TIdFTPServerContext(ASender.Context), LParam, LBPort, LIPVersion); + LParam := ReplaceAll(LParam, '.', ','); {Do not Localize} + LParam := LParam + ',' + IntToStr(LBPort div 256) + ',' + IntToStr(LBPort mod 256); {Do not Localize} + ASender.Reply.SetReply(227, IndyFormat(RSFTPPassiveMode, [LParam])); + end; +end; + +procedure TIdFTPServer.CommandTYPE(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + s: string; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + s := UpperCase(Fetch(s)); + if Length(s) = 1 then begin + //Default data type is ASCII + case s[1] of + 'A': LContext.FDataType := ftASCII; {Do not Localize} + 'I': LContext.FDataType := ftBinary; {Do not Localize} + else Exit; + end; + ASender.Reply.SetReply(200, IndyFormat(RSFTPTYPEChanged, [s])); + end; + end; +end; + +procedure TIdFTPServer.CommandSTRU(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + s: String; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + s := UpperCase(Fetch(s)); + if Length(s) = 1 then begin + //Default structure is file + case s[1] of + 'F': LContext.FDataStruct := dsFile; {Do not Localize} + 'R': LContext.FDataStruct := dsRecord; {Do not Localize} + 'P': LContext.FDataStruct := dsPage; {Do not Localize} + else Exit; + end; + ASender.Reply.SetReply(200, IndyFormat(RSFTPSTRUChanged, [s])); + end; + end; +end; + +procedure TIdFTPServer.CommandMODE(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + s: String; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + s := UpperCase(Fetch(s)); + if Length(s) = 1 then begin + //Default data mode is stream + case s[1] of + 'S' : //stream mode + begin + LContext.DataMode := dmStream; + ASender.Reply.SetReply(200, IndyFormat(RSFTPMODEChanged, [s])); + Exit; + end; + 'Z' : //deflate + begin + if Assigned(FCompressor) then begin + LContext.DataMode := dmDeflate; + ASender.Reply.SetReply(200, IndyFormat(RSFTPMODEChanged, [s])); + Exit; + end; + end; + end; + ASender.Reply.SetReply(504, RSFTPMODENotSupported); + end; + end; +end; + +procedure TIdFTPServer.CommandRETR(ASender: TIdCommand); +var + s: string; + LStream: TStream; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if (not Assigned(LContext.FDataChannel)) or LContext.FDataPortDenied then begin + ASender.Reply.SetReply(425, RSFTPCantOpenData); + Exit; + end; + //TODO: Fix reference to / + s := DoProcessPath(LContext, ASender.UnparsedParams); + LFileSystem := FFTPFileSystem; + if Assigned(FOnRetrieveFile) or Assigned(LFileSystem) then begin + LStream := nil; + try + //some file stream creations can fail with an exception so + //we need to handle this gracefully. + if Assigned(LFileSystem) then begin + LFileSystem.RetrieveFile(LContext, s, LStream) + end else begin + FOnRetrieveFile(LContext, s, LStream); + end; + except + on E : Exception do begin + LContext.KillDataChannel; + ASender.Reply.SetReply(550, E.Message); + Exit; + end; + end; + if Assigned(LStream) then begin + try + LStream.Position := LContext.FRESTPos; + LContext.FRESTPos := 0; + //it should be safe to assume that the FDataChannel object exists because + //we checked it earlier + LContext.FDataChannel.FFtpOperation := ftpRetr; + LContext.FDataChannel.FData := LStream; + LContext.FDataChannel.OKReply.SetReply(226, RSFTPDataConnClosed); + LContext.FDataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally); + ASender.Reply.SetReply(150, RSFTPDataConnToOpen); + ASender.SendReply; + DoDataChannelOperation(ASender, LContext.SSCNOn); + finally + LStream.Free; + end; + end else begin + //make sure the data connection is closed + LContext.KillDataChannel; + CmdFileActionAborted(ASender); + end; + end else begin + //make sure the data connection is closed + LContext.KillDataChannel; + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandSSAP(ASender: TIdCommand); +var + LStream: TStream; + LTmp1: string; + LAppend: Boolean; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if (not Assigned(LContext.FDataChannel)) or LContext.FDataPortDenied then begin + ASender.Reply.SetReply(425, RSFTPCantOpenData); + Exit; + end; + if TextIsSame(ASender.CommandHandler.Command, 'STOU') then begin {Do not Localize} + LTmp1 := GetUniqueFileName('', 'Temp', ''); {Do not localize} + //This is a standardized format + ASender.Reply.SetReply(150, IndyFormat('FILE: %s', [LTmp1])); {Do not translate} + end else begin + LTmp1 := ASender.UnparsedParams; + ASender.Reply.SetReply(150, RSFTPDataConnToOpen); + end; + LTmp1 := DoProcessPath(LContext, LTmp1); + LAppend := TextIsSame(ASender.CommandHandler.Command, 'APPE'); {Do not Localize} + + LFileSystem := FFTPFileSystem; + if Assigned(FOnStoreFile) or Assigned(LFileSystem) then begin + LStream := nil; + try + if Assigned(LFileSystem) then begin + LFileSystem.StoreFile(LContext, LTmp1, LAppend, LStream); + {$IFDEF USE_OBJECT_ARC}LFileSystem := nil;{$ENDIF} + end else begin + FOnStoreFile(LContext, LTmp1, LAppend, LStream); + end; + except + on E : Exception do + begin + ASender.Reply.SetReply(550, E.Message); + LContext.KillDataChannel; + Exit; + end; + end; + if Assigned(LStream) then begin + try + //Issued previously by ALLO cmd + if LContext.ALLOSize > 0 then begin + LStream.Size := LContext.FALLOSize; + end; + if LAppend then begin + LStream.Position := LStream.Size; + end else begin + LStream.Position := LContext.FRESTPos; + LContext.FRESTPos := 0; + end; + { Data transfer } + //it should be safe to assume that the FDataChannel object exists because + //we checked it earlier + LContext.FDataChannel.FFtpOperation := ftpStor; + LContext.FDataChannel.Data := LStream; + LContext.FDataChannel.OKReply.SetReply(226, RSFTPDataConnClosed); + LContext.FDataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally); + ASender.SendReply; + DoDataChannelOperation(ASender, LContext.SSCNOn); + finally + LStream.Free; + end; + end else begin + //make sure the data connection is closed + LContext.KillDataChannel; + CmdFileActionAborted(ASender); + end; + end else begin + //make sure the data connection is closed + LContext.KillDataChannel; + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandALLO(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; + LALLOSize, s: string; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + LALLOSize := ''; + if Length(ASender.UnparsedParams) > 0 then begin + if TextStartsWith(ASender.UnparsedParams, 'R ') then begin {Do not localize} + LALLOSize := TrimLeft(Copy(s, 3, MaxInt)); + end else begin + LALLOSize := TrimLeft(ASender.UnparsedParams); + end; + LALLOSize := Fetch(LALLOSize); + end; + if LALLOSize <> '' then begin + LContext.FALLOSize := IndyStrToInt(LALLOSize, 0); + CmdCommandSuccessful(ASender, 200); + end else begin + ASender.Reply.SetReply(504, RSFTPInvalidForParam); + end; + end; +end; + +procedure TIdFTPServer.CommandREST(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + LContext.FRESTPos := IndyStrToInt(ASender.UnparsedParams, 0); + ASender.Reply.SetReply(350, RSFTPFileActionPending); + end; +end; + +procedure TIdFTPServer.CommandRNFR(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; + s: string; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + if Assigned(FOnRenameFile) or Assigned(FTPFileSystem) then begin + ASender.Reply.SetReply(350, RSFTPFileActionPending); + LContext.FRNFR := DoProcessPath(TIdFTPServerContext(LContext), s); + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandRNTO(ASender: TIdCommand); +var + s: string; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + LFileSystem := FFTPFileSystem; + if Assigned(LFileSystem) or Assigned(FOnRenameFile) then begin + DoOnRenameFile(LContext, LContext.FRNFR, DoProcessPath(LContext, s)); + ASender.Reply.NumericCode := 250; + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandABOR(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + if Assigned(LContext.FDataChannel) then begin + if not LContext.FDataChannel.Stopped then begin + LContext.FDataChannel.OkReply.SetReply(426, RSFTPDataConnClosedAbnormally); + LContext.FDataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally); + LContext.KillDataChannel; + ASender.Reply.SetReply(226, RSFTPDataConnClosed); + Exit; + end; + end; + CmdCommandSuccessful(ASender, 226); + end; +end; + +procedure TIdFTPServer.CommandDELE(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +(* +DELE + 250 Requested file action okay, completed. + 450 Requested file action not taken. - File is busy + 550 Requested action not taken. - File unavailable, no access permitted, etc + 500 Syntax error, command unrecognized. + 501 Syntax error in parameters or arguments. + 502 Command not implemented. + 421 Service not available, closing control connection. - During server shutdown, etc + 530 Not logged in. +*) +//TODO: Need to set replies when not authenticated and set NormalReply to 250 +// do for all procs, list valid replies in comments. Or maybe default is 550 +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FTPFileSystem; + if Assigned(FOnDeleteFile) or Assigned(LFileSystem) then begin + DoOnDeleteFile(LContext, DoProcessPath(LContext, ASender.UnparsedParams)); + ASender.Reply.SetReply(250, RSFTPFileActionCompleted); + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.SetReply(550, RSFTPFileActionNotTaken); + end; +end; + +procedure TIdFTPServer.CommandRMD(ASender: TIdCommand); +var + s: TIdFTPFileName; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + S := IgnoreLastPathDelim(S); + s := DoProcessPath(LContext, ASender.UnparsedParams); + LFileSystem := FFTPFileSystem; + if Assigned(LFileSystem) or Assigned(FOnRemoveDirectory) then begin + DoOnRemoveDirectory(LContext, s); + ASender.Reply.SetReply(250, RSFTPFileActionCompleted); + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandMKD(ASender: TIdCommand); +var + S: TIdFTPFileName; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + S := IgnoreLastPathDelim(S); + S := DoProcessPath(LContext, ASender.UnparsedParams); + DoOnMakeDirectory(LContext, s); + ASender.Reply.SetReply(257, RSFTPFileActionCompleted); + end; +end; + +procedure TIdFTPServer.CommandPWD(ASender: TIdCommand); +var + LContext: TIdFTPServerContext; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + ASender.Reply.SetReply(257, IndyFormat(RSFTPCurrentDirectoryIs, [LContext.FCurrentDir])); + end; +end; + +procedure TIdFTPServer.CommandLIST(ASender: TIdCommand); +var + LStream: TStringList; + LSendData : Boolean; + LPath, LSwitches : String; + LContext : TIdFTPServerContext; + + function DeletRSwitch(const AString : String): String; + var + i : Integer; + begin + Result := ''; + for i := 1 to Length(AString) do begin + if AString[i] <> 'R' then begin + Result := Result + AString[i]; + end; + end; + end; + +begin + LSendData := False; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if (not Assigned(LContext.FDataChannel)) or LContext.FDataPortDenied then begin + ASender.Reply.SetReply(425, RSFTPCantOpenData); + Exit; + end; + if (not Assigned(FOnListDirectory)) and + ((FDirFormat = ftpdfCustom) and (not Assigned(FOnCustomListDirectory))) then begin + LContext.KillDataChannel; + CmdNotImplemented(ASender); + Exit; + end; + LStream := TStringList.Create; + try + LSwitches := ''; + LPath := ASender.UnparsedParams; + if TextStartsWith(LPath, '-') then begin {Do not Localize} + LSwitches := Fetch(LPath); + end; + //we can't support recursive lists with EPLF + if DirFormat = ftpdfEPLF then begin + LSwitches := DeletRSwitch(LSwitches); + end; + ListDirectory(LContext, DoProcessPath(LContext, LPath), LStream, + TextIsSame(ASender.CommandHandler.Command, 'LIST'), ASender.CommandHandler.Command, + LSwitches); + LSendData := True; + finally + try + if LSendData then begin + //it should be safe to assume that the FDataChannel object exists because + //we checked it earlier + LContext.FDataChannel.Data := LStream; + LContext.FDataChannel.FFtpOperation := ftpRetr; + LContext.FDataChannel.OKReply.SetReply(226, RSFTPDataConnClosed); + LContext.FDataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally); + if FDirFormat = ftpdfEPLF then begin + ASender.Reply.SetReply(125, RSFTPDataConnToOpen); + LContext.FDataChannel.OKReply.SetReply(226, RSFTPDataConnClosed); + end + else if TextIsSame(ASender.CommandHandler.Command, 'LIST') or (LSwitches <> '') then begin {do not localize} + ASender.Reply.SetReply(125, RSFTPDataConnList); + end else begin + ASender.Reply.SetReply(125, RSFTPDataConnNList); + end; + ASender.SendReply; + DoDataChannelOperation(ASender); + end else begin + LContext.KillDataChannel; + ASender.Reply.SetReply(426, RSFTPDataConnClosedAbnormally); + end; + finally + LStream.Free; + end; + end; + end; +end; + +procedure TIdFTPServer.DoDataChannelOperation(ASender: TIdCommand; const AConnectMode : Boolean = False); +const + DEF_BLOCKSIZE = 10*10240; + {CH DEF_CHECKCMD_WAIT = 1; } +var + LContext : TIdFTPServerContext; + LCmdQueue : TStringList; + LLine : String; + LStrm : TStream; + + procedure CheckControlConnection(AContext : TIdFTPServerContext; ACmdQueue : TStrings); + var + LLocalLine : String; + begin + LLocalLine := ReadCommandLine(AContext); + if LLocalLine <> '' then begin + if not FDataChannelCommands.HandleCommand(AContext, LLocalLine) then begin + ACmdQueue.Add(LLocalLine); + end; + end; + end; + + procedure ReadFromStream(AContext : TIdFTPServerContext; ACmdQueue : TStrings; ADestStream : TStream); + var + LM : TStream; + begin + if AContext.DataMode = dmDeflate then begin + LM := TMemoryStream.Create; + end else begin + LM := ADestStream; + end; + try + repeat + AContext.FDataChannel.FDataChannel.IOHandler.CheckForDisconnect(False); + AContext.FDataChannel.FDataChannel.IOHandler.ReadStream(LM, DEF_BLOCKSIZE, True); + CheckControlConnection(AContext, ACmdQueue); + until not AContext.FDataChannel.FDataChannel.IOHandler.Connected; + if AContext.DataMode = dmDeflate then begin + LM.Position := 0; + FCompressor.DecompressFTPDeflate(LM, ADestStream, AContext.ZLibWindowBits); + end; + finally + if AContext.DataMode = dmDeflate then begin + FreeAndNil(LM); + end; + end; + end; + + procedure WriteToStream(AContext : TIdFTPServerContext; ACmdQueue : TStrings; + ASrcStream : TStream; const AIgnoreCompression : Boolean = False); + var + LBufSize : TIdStreamSize; + LOutStream : TStream; + begin + if AContext.DataMode = dmDeflate then begin + LOutStream := TMemoryStream.Create; + end else begin + LOutStream := ASrcStream; + end; + try + if AContext.DataMode = dmDeflate then begin + FCompressor.CompressFTPDeflate(ASrcStream, LOutStream, + AContext.ZLibCompressionLevel, AContext.ZLibWindowBits, + AContext.ZLibMemLevel, AContext.ZLibStratagy); + LOutStream.Position := 0; + end; + repeat + LBufSize := LOutStream.Size - LOutStream.Position; + if LBufSize > DEF_BLOCKSIZE then begin + LBufSize := DEF_BLOCKSIZE; + end; + if LBufSize > 0 then begin + AContext.FDataChannel.FDataChannel.IOHandler.Write(LOutStream, LBufSize, False); + if LOutStream.Position < LOutStream.Size then begin + CheckControlConnection(AContext, ACmdQueue); + end; + end; + until (LBufSize = 0) or (not AContext.FDataChannel.FDataChannel.IOHandler.Connected); + finally + if AContext.DataMode = dmDeflate then begin + FreeAndNil(LOutStream); + end; + end; + end; + + procedure WriteStrings(AContext : TIdFTPServerContext; ACmdQueue : TStrings; ASrcStrings : TStrings); + var + i : Integer; + LM : TStream; + LEncoding: IIdTextEncoding; + begin + //for loops will execute at least once triggering an out of range error. + //write nothing if AStrings is empty. + if ASrcStrings.Count < 1 then begin + Exit; + end; + { + IMPORTANT!!! + + If LIST data is sent as 8bit, you have a FTP list that is unparsable by + some FTP clients. If UTF8 OPTS OFF, you should send the data as 7bit + for the LIST and NLST commands. That way, unprintable charactors are + returned as ?. While the file name is not valid, at least, there some + thing that looks better than binary junk. + } + if PosInStrArray(ASender.CommandHandler.Command, ['LIST', 'NLST', 'MLSD'], False) > -1 then begin + LEncoding := IndyTextEncoding(NLSTEncType[AContext.NLSTUtf8]); + end else begin + LEncoding := IndyTextEncoding_8Bit; + end; + + if AContext.DataMode = dmDeflate then begin + LM := TMemoryStream.Create; + try + for i := 0 to ASrcStrings.Count-1 do begin + WriteStringToStream(LM, ASrcStrings[i] + EOL, LEncoding); + end; + LM.Position := 0; + WriteToStream(AContext, ACmdQueue, LM, True); + finally + FreeAndNil(LM); + end; + Exit; + end; + for i := 0 to ASrcStrings.Count-1 do begin + if AContext.FDataChannel.FDataChannel.IOHandler.Connected then begin + AContext.FDataChannel.FDataChannel.IOHandler.WriteLn(ASrcStrings[i], LEncoding); + if ((i mod 10) = 0) and (i <> (ASrcStrings.Count-1)) then begin + if AContext.FDataChannel.FDataChannel.IOHandler.Connected then begin + CheckControlConnection(AContext, ACmdQueue); + end else begin + Break; + end; + end; + end else begin + Break; + end; + end; + end; + +begin + if not Assigned(ASender) then begin + Exit; + end; + if not Assigned(ASender.Context) then begin + Exit; + end; + LContext := ASender.Context as TIdFTPServerContext; + if not Assigned(LContext.FDataChannel) then begin + Exit; + end; + try + LCmdQueue := TStringList.Create; + try + LContext.FDataChannel.InitOperation(AConnectMode); + try + try + try + if LContext.FDataChannel.Data is TStream then begin + LStrm := TStream(LContext.FDataChannel.Data); + case LContext.FDataChannel.FFtpOperation of + ftpRetr: + WriteToStream(LContext, LCmdQueue, LStrm); + ftpStor: + ReadFromStream(LContext, LCmdQueue, LStrm); + end; + end else begin + case LContext.FDataChannel.FFtpOperation of + ftpRetr: + if Assigned(LContext.FDataChannel.Data) then begin + WriteStrings(LContext, LCmdQueue, LContext.FDataChannel.Data as TStrings); + end; + ftpStor: + if Assigned(LContext.FDataChannel.Data) then begin + LStrm := TMemoryStream.Create; + try + ReadFromStream(LContext, LCmdQueue, LStrm); + //TODO; + // SplitLines(TMemoryStream(LStrm).Memory, LMemStream.Size, LContext.FDataChannel.FData as TStrings); + finally + LStrm.Free; + end; + end;//ftpStor + end;//case + end; + finally + if Assigned(LContext.FDataChannel.FDataChannel) then begin + LContext.FDataChannel.FDataChannel.Disconnect(False); + end; + end; + LContext.FDataChannel.FReply.Assign(LContext.FDataChannel.FOKReply); //226 + except + on E: Exception do begin + if not (E is EIdSilentException) then begin + LContext.FDataChannel.FReply.Assign(LContext.FDataChannel.FErrorReply); //426 + end; + end; + end; + finally + ASender.Reply.Assign(LContext.FDataChannel.FReply); + ASender.SendReply; + //now we have to handle the FIFO queue we had made + while LCmdQueue.Count > 0 do begin + LLine := LCmdQueue[0]; + if not FCommandHandlers.HandleCommand(ASender.Context, LLine) then begin + DoReplyUnknownCommand(ASender.Context, LLine); + end; + if Assigned(ASender.Context.Connection) then begin + if not ASender.Context.Connection.Connected then begin + Break; + end; + end else begin + Break; + end; + LCmdQueue.Delete(0); + end; + end; + finally + FreeAndNil(LCmdQueue); + end; + finally + FreeAndNil(LContext.FDataChannel); + end; +end; + +procedure TIdFTPServer.CommandSYST(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.UserSecurity.DisableSYSTCommand then begin + CmdNotImplemented(ASender); + Exit; + end; + //this should keep CuteFTP Pro 3.0 from stopping there's no custom ID and + //the Dir format is custonm. + if (FDirFormat = ftpdfCustom) and (Trim(FCustomSystID) = '') then begin + CmdNotImplemented(ASender); + Exit; + end; + if LContext.IsAuthenticated(ASender) then begin + ASender.Reply.SetReply(215, DoSysType(LContext)); + end; +end; + +procedure TIdFTPServer.CommandSTAT(ASender: TIdCommand); +var + LStream: TStringList; + LActAsList: boolean; + LSwitches, LPath : String; + i : Integer; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + LActAsList := (ASender.Params.Count > 0); + if not LActAsList then begin + if LContext.UserSecurity.DisableSTATCommand then begin + if ASender.UnparsedParams = '' then begin + CmdNotImplemented(ASender); + Exit; + end; + end; + end; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(LContext.DataChannel) then begin + if not LContext.DataChannel.Stopped then begin + LActAsList := False; + end; + end; + if not LActAsList then begin + ASender.Reply.NumericCode := 211; + ASender.Reply.Text.Clear; + if Assigned(FOnStat) then begin + LStream := TStringList.Create; + try + SetRFCReplyFormat(ASender.Reply); + FOnStat(LContext, LStream); + for i := 0 to LStream.Count - 1 do begin + ASender.Reply.Text.Add(' ' + TrimLeft(LStream[i])); {Do not Localize} + end; + finally + FreeAndNil(LStream); + end; + end; + ASender.Reply.Text.Insert(0,RSFTPCmdStartOfStat); + ASender.Reply.Text.Add(RSFTPCmdEndOfStat); + end else begin //else act as LIST command without a data channel + LStream := TStringList.Create; + try + LSwitches := ''; + LPath := ASender.UnparsedParams; + if TextStartsWith(LPath, '-') then begin + LSwitches := Fetch(LPath); + end; + ListDirectory(LContext, DoProcessPath(LContext, LPath), LStream, True, LSwitches); + //we use IOHandler.WriteLn here because we need better control over what + //we send than what Reply.SendReply offers. This is important as the dir + //is written using WriteStrings and I found that with Reply.SetReply, a stat + //reply could throw off a FTP client. + LContext.Connection.IOHandler.WriteLn(IndyFormat('213-%s', [RSFTPDataConnToOpen])); {Do not Localize} + LContext.Connection.IOHandler.Write(LStream, False, IndyTextEncoding(NLSTEncType[LContext.NLSTUtf8])); + ASender.PerformReply := True; + ASender.Reply.SetReply(213, RSFTPCmdEndOfStat); + finally + FreeAndNil(LStream); + end; + end; + end; +end; + +procedure TIdFTPServer.CommandFEAT(ASender: TIdCommand); +const + MFFPREFIX = 'MFF '; {Do not Localize} +var + LTmp : String; + LContext: TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := TIdFTPServerContext(ASender.Context); + LFileSystem := FTPFileSystem; + ASender.Reply.Clear; + SetRFCReplyFormat(ASender.Reply); + ASender.Reply.NumericCode := 211; + ASender.Reply.Text.Add(RSFTPCmdExtsSupportedStart); {Do not translate} + //AUTH + if IOHandler is TIdServerIOHandlerSSLBase then begin + if (FUseTLS <> utUseImplicitTLS) then begin + ASender.Reply.Text.Add('AUTH TLS;AUTH TLS-C;SSL;TLS-P;'); {Do not translate} + end; + end; + //AVBL + if Assigned(FOnAvailDiskSpace) then begin + ASender.Reply.Text.Add('AVBL'); + end; + //CCC + if (FUseTLS <> utNoTLSSupport) then begin + ASender.Reply.Text.Add('CCC'); {Do not translate} + end; + //CLNT + if Assigned(FOnClientID) then begin + ASender.Reply.Text.Add('CLNT'); {Do not translate} + end; + //COMB + if Assigned(FOnCombineFiles) or Assigned(LFileSystem) then begin + ASender.Reply.Text.Add('COMB target;source_list'); {Do not translate} + end; + //CPSV + //CPSV is not supported in IPv6 - same problem as PASV + if (UseTLS <> utNoTLSSupport) and (LContext.Binding.IPVersion = Id_IPv4) then begin + ASender.Reply.Text.Add('CPSV'); {Do not translate} + end; + //DSIZ + if Assigned(OnCompleteDirSize) then begin + ASender.Reply.Text.Add('DSIZ'); {Do not localize} + end; + //EPRT + ASender.Reply.Text.Add('EPRT'); {Do not translate} + //EPSV + ASender.Reply.Text.Add('EPSV'); {Do not translate} + //Host + if Assigned(FOnHostCheck) then begin + ASender.Reply.Text.Add('HOST domain'); {Do not localize} + end; + // + //This is not proper but FTP Voyager uses it to determine if the -T parameter + //will work. + if Assigned(FOnListDirectory) then begin + //we do things this way because the 'a' and 'T' swithces only make sense + //when listing Unix dirs. + LTmp := 'LIST -l'; {Do not translate} + if SupportTaDirSwitches(LContext) then begin + LTmp := LTmp + 'aT'; {Do not translate} + end; + ASender.Reply.Text.Add(LTmp); {do not localize} + end; + //MDTM + if Assigned(FOnGetFileDate) or Assigned(LFileSystem) then begin + ASender.Reply.Text.Add('MDTM'); {Do not translate} + //MDTM YYYYMMDDHHMMSS filename + if Assigned(FOnSetModifiedTime) then begin + // ASender.Reply.Text.Add('MDTM YYYYMMDDHHMMSS[+-TZ];filename'); + //Indicate that we wish to use FTP Voyager's old MDTM variation for seting time. + //time is returned as local (relative to server's timezone. We do this for compatibility + ASender.Reply.Text.Add('MDTM YYYYMMDDHHMMSS filename'); {Do not translate} + end; + end; + //MFCT + if Assigned(FOnSetCreationTime) then begin + ASender.Reply.Text.Add('MFCT'); {Do not Localize} + //TODO: The logic for the MMF entry may need to change if we + //support modifying more facts + end; + //MFF + LTmp := MFFPREFIX; {Do not localize} + if Assigned(FOnSetCreationTime) and (mlsdFileLastAccessTime in FMLSDFacts) then begin + LTmp := LTmp + 'Create;'; {Do not Localize} + end; + if Assigned(FOnSetModifiedTime) or Assigned(LFileSystem) then begin + LTmp := LTmp + 'Modify;'; {Do not Localize} + end; + if Assigned(FOnSiteCHMOD) then begin + LTmp := LTmp + 'Unix.mode;'; + end; + if Assigned(FOnSiteCHOWN) then begin + LTmp := LTmp + 'Unix.owner;'; + end; + if Assigned(FOnSiteCHGRP) then begin + LTmp := LTmp + 'Unix.group;'; + end; + if Assigned(FOnSiteUTIME) and (mlsdFileLastAccessTime in FMLSDFacts) then begin + LTmp := LTmp + 'Windows.lastaccesstime;'; + end; + if Assigned(FOnSetATTRIB) then begin + LTmp := LTmp + 'Win32.ea;'; + end; + if LTmp <> MFFPREFIX then begin + ASender.Reply.Text.Add(LTmp); + end; + //MFMT + if Assigned(FOnSetModifiedTime) or Assigned(LFileSystem) then begin + ASender.Reply.Text.Add('MFMT'); {Do not Localize} + end; + //MLST + if Assigned(FOnListDirectory) then begin + ASender.Reply.Text.Add('MLSD'); {Do not translate} + ASender.Reply.Text.Add(MLSFEATLine(FMLSDFacts, LContext.MLSOpts)); {Do not translate} + end; + //MODE Z + if Assigned(FCompressor) then begin + ASender.Reply.Text.Add('MODE Z'); {do not localize} + end; + //OPTS + LTmp := 'OPTS '; + if Assigned(FOnListDirectory) then begin + LTmp := LTmp + 'MLST;'; + end; + if Assigned(FCompressor) then begin + LTmp := LTmp + 'MODE;'; + end; + LTmp := LTmp + 'UTF8'; + ASender.Reply.Text.Add(LTmp); + //PBSZ + if (FUseTLS <> utNoTLSSupport) then begin + ASender.Reply.Text.Add('PBSZ'); {Do not translate} + end; + //PROT + if (FUseTLS <> utNoTLSSupport) then begin + ASender.Reply.Text.Add('PROT'); {Do not translate} + end; + //REST STREAM + ASender.Reply.Text.Add('REST STREAM'); {Do not translate} + //RMDA + if Assigned(FOnRemoveDirectoryAll) then begin + ASender.Reply.Text.Add('RMDA directoryname'); {Do not localize} + end; + //SITE ZONE + //Listing a SITE command in feature negotiation is unusual and + //may be a little off-spec. FTP Voyager scans this looking for + //SITE ZONE and if it's present, it will use the SITE ZONE + //to help it convert the time to the user's local time zone. + //The only other way that FTP Voyager would know is if the initial + //FTP greeting banner started with "Serv-U FTP-Server v2.5f" which + //is more problematic because Serve-U is a trademark and we would then + //then be stuck with a situation where everyone has to use it down the road. + //This would amount to the same mess we had with "Mozilla" in the HTTP + //User-Agent header field. + //also list other supported site commands; + LTmp := 'SITE ZONE'; + if Assigned(FOnSetATTRIB) then begin + LTmp := LTmp + ';ATTRIB'; + end; + if Assigned(FOnSiteUMASK) then begin + LTmp := LTmp + 'UMASK'; + end; + if Assigned(FOnSiteCHMOD) then begin + LTmp := LTmp + ';CHMOD'; + end; + if (FDirFormat = ftpdfDOS) or + ((FDirFormat = ftpdfOSDependent) and (GOSType = otWindows)) then begin + LTmp := LTmp + ';DIRSTYLE'; + end; + if Assigned(OnSiteUTIME) or Assigned(OnSetModifiedTime) then begin + LTmp := LTmp + ';UTIME'; + end; + if Assigned(OnSiteCHOWN) then begin + LTmp := LTmp + ';CHOWN'; + end; + if Assigned(OnSiteCHGRP) then begin + LTmp := LTmp + ';CHGRP'; + end; + ASender.Reply.Text.Add(LTmp); {do not localize} + //SIZE + if Assigned(FOnGetFileSize) or Assigned(LFileSystem) then begin + ASender.Reply.Text.Add('SIZE'); {do not localize} + end; + //SPSV + ASender.Reply.Text.Add('SPSV'); {do not localize} + //SSCN + if UseTLS <> utNoTLSSupport then begin + ASender.Reply.Text.Add('SSCN'); {do not localize} + end; + //STAT -l + //Some servers such as Microsoft FTP Service, RaidenFTPD, and a few others, + //treat a STAT -l as a LIST command, only it's sent on the control connection. + //Some versions of Flash FXP can also use this as an option to improve efficiency. + if Assigned(FOnListDirectory) then begin + //we do things this way because the 'a' and 'T' swithces only make sense + //when listing Unix dirs. + LTmp := 'STAT -l'; {Do not translate} + if SupportTaDirSwitches(LContext) then begin + LTmp := LTmp + 'aT'; {Do not translate} + end; + ASender.Reply.Text.Add(LTmp); {do not localize} + end; + //TVFS + if FPathProcessing <> ftppCustom then begin + //TVFS should not be indicated for custom parsing because + //we don't know what a person will do. + ASender.Reply.Text.Add('TVFS'); {Do not localize} + end; + // UTF-8 + // RFC 2640 says that "Servers MUST support the UTF-8 feature in response to the FEAT command [RFC2389]." + // TODO: finish actually implementing UTF-8 support + ASender.Reply.Text.Add('UTF8'); {Do not localize} + //XCRC + if Assigned(FOnCRCFile) or Assigned(LFileSystem) then begin + if not GetFIPSMode then begin + ASender.Reply.Text.Add('XCRC "filename" SP EP');//filename;start;end'); {Do not Localize} + ASender.Reply.Text.Add('XMD5 "filename" SP EP');//filename;start;end'); {Do not Localize} + end; + ASender.Reply.Text.Add('XSHA1 "filename" SP EP');//filename;start;end'); {Do not Localize} + + if TIdHashSHA256.IsAvailable then begin + ASender.Reply.Text.Add('XSHA256 "filename" SP EP'); //file;start/end + end; + if TIdHashSHA512.IsAvailable then begin + ASender.Reply.Text.Add('XSHA512 "filename" SP EP'); //file;start/end + end; + end; + //I'm doing things this way with complience level to match the current + //version of NcFTPD + LTmp := 'RFC 959 2389 '; + if LContext.UserSecurity.FInvalidPassDelay <> 0 then begin + LTmp := LTmp + '2577 '; + end; + LTmp := LTmp + '3659 '; {Do not Localize} + if IOHandler is TIdServerIOHandlerSSLBase then begin + if (FUseTLS <> utUseImplicitTLS) then begin + LTmp := LTmp + '4217 '; {Do not localize} + end; + end; + ASender.Reply.Text.Add(Trim(LTmp)); {Do not Localize} + ASender.Reply.Text.Add(RSFTPCmdExtsSupportedEnd); +end; + +procedure TIdFTPServer.CommandOPTS(ASender: TIdCommand); +var + LCmd : String; +begin + LCmd := ASender.UnparsedParams; + ASender.Reply.Clear; + if TextIsSame(Fetch(LCmd, ' ', False), 'MLST') then begin {do not localize} + //just in case the user doesn't create a ListDirectory event. + if not Assigned(FOnListDirectory) then begin + ASender.Reply.SetReply(501, RSFTPOptNotRecog); + Exit; + end; + end; + if not FOPTSCommands.HandleCommand(ASender.Context, LCmd) then begin + ASender.Reply.SetReply(501, RSFTPOptNotRecog); + end else begin + //we don't want an extra 200 reply. + ASender.PerformReply := False; + end; +end; + +procedure TIdFTPServer.CommandSIZE(ASender: TIdCommand); +var + s: string; + LSize: Int64; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FFTPFileSystem; + if Assigned(FOnGetFileSize) or Assigned(LFileSystem) then begin + LSize := -1; + s := DoProcessPath(LContext, ASender.UnparsedParams); + DoOnGetFileSize(LContext, s, LSize); + if LSize > -1 then begin + ASender.Reply.SetReply(213, IntToStr(LSize)); + end else begin + CmdFileActionAborted(ASender); + end; + end else begin + CmdSyntaxError(ASender); + end; + end; +end; + +procedure TIdFTPServer.DoOnChangeDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FFTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.ChangeDir(AContext, VDirectory); + end else if Assigned(FOnChangeDirectory) then begin + FOnChangeDirectory(AContext, VDirectory); + end; +end; + +procedure TIdFTPServer.DoOnRemoveDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FFTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.RemoveDirectory(AContext, VDirectory); + end else if Assigned(FOnRemoveDirectory) then begin + FOnRemoveDirectory(AContext, VDirectory); + end; +end; + +procedure TIdFTPServer.DoOnMakeDirectory(AContext: TIdFTPServerContext; var VDirectory: TIdFTPFileName); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FFTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.MakeDirectory(AContext, VDirectory); + end else if Assigned(FOnMakeDirectory) then begin + FOnMakeDirectory(AContext, VDirectory); + end; +end; + +procedure TIdFTPServer.CommandEPRT(ASender: TIdCommand); +var + LParm, LIP: string; + LDelim: char; + LReqIPVersion: TIdIPVersion; + LContext : TIdFTPServerContext; + LDataChannel: TIdTCPClient; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LContext.FPASV := False; + LParm := ASender.UnparsedParams; + if Length(LParm) = 0 then begin + LContext.FDataPortDenied := True; + CmdInvalidParamNum(ASender); + Exit; + end; + if FFTPSecurityOptions.BlockAllPORTTransfers then begin + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(502, RSFTPPORTDisabled); + Exit; + end; + LDelim := LParm[1]; + Fetch(LParm, LDelim); + case IndyStrToInt(Fetch(LParm, LDelim), -1) of + 1: LReqIPVersion := Id_IPv4; + 2: if GStack.SupportsIPv6 then begin + LReqIPVersion := Id_IPv6; + end else begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(522, IndyFormat(RSFTPNetProtNotSup, ['1'])); {Do not translate} + Exit; + end; + else + begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(522, IndyFormat(RSFTPNetProtNotSup, [iif(GStack.SupportsIPv6, '1,2', '1')])); {Do not translate} + Exit; + end; + end; + LIP := Fetch(LParm, LDelim); + if Length(LIP) = 0 then begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(500, RSFTPInvalidIP); + Exit; + end; + LContext.FDataPort := TIdPort(IndyStrToInt(Fetch(LParm, LDelim), 0)); + if LContext.FDataPort = 0 then begin + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(500, RSFTPInvalidPort); + Exit; + end; + if FFTPSecurityOptions.NoReservedRangePORT and + ((LContext.FDataPort > 0) and (LContext.FDataPort <= 1024)) then begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(504, RSFTPPORTRange); + Exit; + end; + if FFTPSecurityOptions.FRequirePORTFromSameIP then begin + case LReqIPVersion of + Id_IPv4: LIP := MakeCanonicalIPv4Address(LIP); + Id_IPv6: LIP := MakeCanonicalIPv6Address(LIP); + end; + if LIP <> LContext.Binding.PeerIP then begin + LContext.FDataPort := 0; + LContext.FDataPortDenied := True; + ASender.Reply.SetReply(504, RSFTPSameIPAddress); + Exit; + end; + end; + LContext.CreateDataChannel(False); + LDataChannel := TIdTCPClient(LContext.FDataChannel.FDataChannel); + LDataChannel.Host := LIP; + LDataChannel.Port := LContext.FDataPort; + LDataChannel.IPVersion := LReqIPVersion; + LContext.FDataPortDenied := False; + CmdCommandSuccessful(ASender, 200); + end; +end; + +procedure TIdFTPServer.CommandEPSV(ASender: TIdCommand); +var + LParam: string; + LBPortMin, LBPortMax: Word; + LIP : String; + LIPVersion: TIdIPVersion; + LReqIPVersion: TIdIPVersion; + LContext : TIdFTPServerContext; + LDataChannel: TIdSimpleServer; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LIPVersion := LContext.Binding.IPVersion; + LReqIPVersion := LIPVersion; + LParam := ASender.UnparsedParams; + if Length(LParam) > 0 then begin + case IndyStrToInt(LParam, -1) of + 1: LReqIPVersion := Id_IPv4; + 2: if GStack.SupportsIPv6 then begin + LReqIPVersion := Id_IPv6; + end else begin + ASender.Reply.SetReply(522, IndyFormat(RSFTPNetProtNotSup, ['1'])); {do not localize} + Exit; + end; + else + begin + if TextIsSame(LParam, 'ALL') then begin { do not localize } + LContext.FEPSVAll := True; + ASender.Reply.SetReply(200, RSFTPEPSVAllEntered); + end else begin + ASender.Reply.SetReply(522, IndyFormat(RSFTPNetProtNotSup, [iif(GStack.SupportsIPv6, '1,2', '1')])); {do not localize} + end; + Exit; + end; + end; + end; + if LReqIPVersion = LIPVersion then begin + LIP := LContext.Binding.IP; + end; + if (FPASVBoundPortMin <> 0) and (FPASVBoundPortMax <> 0) then begin + LBPortMin := FPASVBoundPortMin; + LBPortMax := FPASVBoundPortMax; + end else begin + LBPortMin := FDefaultDataPort; + LBPortMax := LBPortMin; + end; + DoOnPASVBeforeBind(LContext, LIP, LBPortMin, LBPortMax, LReqIPVersion); + + LContext.CreateDataChannel(True); + LDataChannel := TIdSimpleServer(LContext.FDataChannel.FDataChannel); + LDataChannel.BoundIP := LIP; + if LBPortMin = LBPortMax then begin + LDataChannel.BoundPort := LBPortMin; + LDataChannel.BoundPortMin := 0; + LDataChannel.BoundPortMax := 0; + end else begin + LDataChannel.BoundPort := 0; + LDataChannel.BoundPortMin := LBPortMin; + LDataChannel.BoundPortMax := LBPortMax; + end; + LDataChannel.IPVersion := LReqIPVersion; + LDataChannel.BeginListen; + LIP := LDataChannel.Binding.IP; + LBPortMin := LDataChannel.Binding.Port; + + //Note that only one Port can work with EPSV + DoOnPASVReply(LContext, LIP, LBPortMin, LReqIPVersion); + LParam := '|||' + IntToStr(LBPortMin) + '|'; {Do not localize} + ASender.Reply.SetReply(229, IndyFormat(RSFTPEnteringEPSV, [LParam])); + ASender.SendReply; + LContext.FPASV := True; + end; +end; + +procedure TIdFTPServer.CommandMDTM(ASender: TIdCommand); +var + s: string; + LDate: TDateTime; + LContext : TIdFTPServerContext; + LSDate : String; + LExists : Boolean; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +{ +I know that this code and design are a mess. + +There are actually two forms of MDTM and they mean different things. + +The formal spec indicates that anything after the space in MDTM +is the filename. + +FTP Voyager and some other clients abuse the MDTM command by using it to specify +a timestamp for the "Modified Time" on a file. The format is like this: + + MDTM YYYYMMDDHHMMSS filename + + Thus, there's an ambiguity. + + Does MDTM 20031229152022 ESBAdDemo.exe mean + 1) Set the date time on ESBAdDemo.exe to 12/29/2003 3:20:22 PM + + or + + 2) Get the time for a file named 20031229152022 ESBAdDemo.exe + + To resolve this ambiguity, we check specifically for a valid date, and then see + if a file, 20031229152022 ESBAdDemo.exe really does exist. If not, we interpret + MDTM as a set date command. Otherwise, we will traat it as a request for the timestamp + of a file, 20031229152022 ESBAdDemo.exe. + + Note also that the time is sometimes given as either relative to the local server + or an offset is provided. + + Note from: + http://www.ftpvoyager.com/releasenotes.asp + ==== + Added support for RFC change and the MDTM. MDTM requires sending the server + GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with + Serv-U automatically by checking the Serv-U version number and by checking the + response to the FEAT command for MDTM. Servers returning "MDTM" or + "MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers + returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a + and time is GMT (UTC). + === + We will use the old form for compatiability with some older FTP Voyager clients + and because a few servers support the old form as well. I do this even though, + this is really inconsistant with what MDTM returns for a query request. I might + consider some type of support for the new form but I do not feel that such + things are just abuse of the MDTM command. That's why I prefer a separate command for + modifying file modification dates (MFMT). +} +begin + LFileSystem := FFTPFileSystem; + if Assigned(FOnGetFileDate) or Assigned(LFileSystem) then + begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := ASender.UnparsedParams; + LSDate := Fetch(s); + if IsMDTMDate(LSDate) then begin + s := DoProcessPath(LContext, ASender.UnparsedParams ); + DoOnFileExistCheck(LContext, s, LExists); + if not LExists then begin + s := ASender.UnparsedParams; + Fetch(s); + s := DoProcessPath(LContext, s); + LDate := FTPMDTMToGMTDateTime(LSDate); + DoOnSetModifiedTime(LContext, s, LDate); + // Self.DoOnSetModifiedTime(LF,s, LSDate); + ASender.Reply.SetReply(253, 'Date/time changed okay.'); {do not localize} + Exit; + end; + end; + + s := DoProcessPath(LContext, ASender.UnparsedParams); + LDate := 0; + DoOnGetFileDate(LContext, s, LDate); + if LDate > 0 then begin + ASender.Reply.SetReply(213, FTPGMTDateTimeToMLS(LDate)); + end else begin + CmdFileActionAborted(ASender); + end; + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.SetFTPSecurityOptions(const AValue: TIdFTPSecurityOptions); +begin + FFTPSecurityOptions.Assign(AValue); +end; + +procedure TIdFTPServer.SetOnUserAccount(AValue: TOnFTPUserAccountEvent); +var + LCmd : TIdCommandHandler; + i : Integer; +begin + if FUserAccounts = nil then begin + FOnUserAccount := AValue; + for i := 0 to CommandHandlers.Count - 1 do begin + LCmd := CommandHandlers.Items[i]; + if LCmd.Command = 'ACCT' then begin + if Assigned(AValue) then begin + LCmd.HelpSuperScript := ''; + LCmd.Description.Text := ACCT_HELP_ENABLED; + end else begin + LCmd.HelpSuperScript := '*'; + LCmd.Description.Text := ACCT_HELP_DISABLED; + end; + end; + end; + end; +end; + +procedure TIdFTPServer.CommandAUTH(ASender: TIdCommand); +var + LIO : TIdSSLIOHandlerSocketBase; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if (PosInStrArray(ASender.UnparsedParams, TLS_AUTH_NAMES) > -1) and (IOHandler is TIdServerIOHandlerSSLBase) + and (FUseTLS in ExplicitTLSVals) then begin + ASender.Reply.SetReply(234,RSFTPAuthSSL); + ASender.SendReply; + LIO := ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase; + LIO.Passthrough := False; + { + This is from: + + http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad + + and we implement things this way for historical reasons so + we don't break older and newer clients. + } + case PosInStrArray(ASender.UnparsedParams, TLS_AUTH_NAMES) of + 0,2 : LContext.DataProtection := ftpdpsClear; //AUTH TLS, AUTH TLS-C + 1,3 : LContext.DataProtection := ftpdpsPrivate; //AUTH SSL, AUTH TLS-P + end; + LContext.AuthMechanism := 'TLS'; {Do not localize} + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.CommandAVBL(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LIsFile : Boolean; + LSize : Int64; + LPath : String; +begin + LIsFile := True; + LSize := 0; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(FOnAvailDiskSpace) then begin + LPath := DoProcessPath(LContext, ASender.UnparsedParams); + FOnAvailDiskSpace(LContext, LPath, LIsFile, LSize); + if LIsFile then begin + ASender.Reply.SetReply(550, IndyFormat(RSFTPIsAFile,[LPath])); + end else begin + ASender.Reply.SetReply(213, IntToStr(LSize)); + end; + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.SetReply(550, RSFTPFileActionNotTaken); + end; +end; + + //FOnCompleteDirSize +procedure TIdFTPServer.CommandDSIZ(ASender : TIdCommand); +var + LContext : TIdFTPServerContext; + LIsFile : Boolean; + LSize : Int64; + LPath : String; +begin + LIsFile := True; + LSize := 0; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(FOnCompleteDirSize) then begin + LPath := DoProcessPath(LContext, ASender.UnparsedParams); + FOnCompleteDirSize(LContext, LPath, LIsFile, LSize); + if LIsFile then begin + ASender.Reply.SetReply(550, IndyFormat(RSFTPIsAFile,[LPath])); + end else begin + ASender.Reply.SetReply(213, IntToStr(LSize)); + end; + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.SetReply(550, RSFTPFileActionNotTaken); + end; +end; + +procedure TIdFTPServer.CommandRMDA(ASender : TIdCommand); +var + LContext : TIdFTPServerContext; + LPath : TIdFTPFileName; +begin + //FOnRemoveDirectoryAll: TOnDirectoryEvent; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(FOnRemoveDirectoryAll) then begin + LPath := DoProcessPath(LContext, ASender.UnparsedParams); + FOnRemoveDirectoryAll(LContext, LPath); + ASender.Reply.SetReply(250, RSFTPFileActionCompleted); + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.SetReply(550, RSFTPFileActionNotTaken); + end; +end; + +procedure TIdFTPServer.CommandCCC(ASender: TIdCommand); +var + LIO : TIdSSLIOHandlerSocketBase; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if FUseTLS <> utNoTLSSupport then begin + //Not sure if it's proper to require authentication before a CCC + //but it is a good idea anyway because you definately want to + //prevent eavesdropping + if LContext.IsAuthenticated(ASender) then begin + if LContext.FUserSecurity.PermitCCC then begin + ASender.Reply.SetReply(200, RSFTPClearCommandConnection); + ASender.SendReply; + LIO := ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase; + LIO.Passthrough := True; + LContext.FCCC := True; + end else begin + ASender.Reply.SetReply(534, RSFTPClearCommandNotPermitted); + end; + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.CommandPBSZ(ASender: TIdCommand); +{Note that this may have to be expanded and reworked for other AUTH mechanisms} +var + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if IOHandler is TIdServerIOHandlerSSLBase then begin + if ASender.UnparsedParams = '' then begin + CmdInvalidParamNum(ASender); + Exit; + end; + if (LContext.AuthMechanism = '') and (FUseTLS <> utUseImplicitTLS) then begin + ASender.Reply.SetReply(503, RSFTPPBSZAuthDataRequired); + Exit; + end; + if LContext.FCCC then begin + ASender.Reply.SetReply(503, RSFTPPBSZNotAfterCCC); + Exit; + end; + if (LContext.AuthMechanism = 'TLS') or (FUseTLS = utUseImplicitTLS) then begin {Do not localize} + ASender.Reply.SetReply(200,RSFTPDataProtBuffer0); + LContext.DataPBSZCalled := True; + end + else if IsNumeric(ASender.UnparsedParams) then begin + ASender.Reply.SetReply(200,'PBSZ=0'); {Do not translate} + LContext.DataPBSZCalled := True; + end else begin + CmdInvalidParams(ASender); + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.CommandPROT(ASender: TIdCommand); +const + LValidParams : array [0..3] of string = ('C','S','E','P'); {Do not translate} +{Note that this may have to be expanded and reworked for other AUTH mechanisms} +var + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if IOHandler is TIdServerIOHandlerSSLBase then begin + if LContext.FCCC then begin + ASender.Reply.SetReply(503, RSFTPPBSZNotAfterCCC); + Exit; + end; + if not LContext.DataPBSZCalled then begin + ASender.Reply.SetReply(503, RSFTPPROTProtBufRequired); + Exit; + end; + case PosInStrArray(ASender.UnparsedParams, LValidParams) of + 0 : begin + LContext.FDataProtection := ftpdpsClear; + ASender.Reply.SetReply(200, RSFTPProtTypeClear); + end; + 1, 2 : ASender.Reply.SetReply(536, RSFTPInvalidProtTypeForMechanism); + 3 : begin + LContext.FDataProtection := ftpdpsPrivate; + ASender.Reply.SetReply(200, RSFTPProtTypePrivate); + end; + else + ASender.Reply.SetReply(504, RSFTPInvalidForParam); + end; + end else begin + CmdNotImplemented(ASender); + end; +end; + +procedure TIdFTPServer.CommandCOMB(ASender: TIdCommand); +var + LFileParts : TStringList; + LBuf : String; + LTargetFileName : String; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + LFileSystem := FTPFileSystem; + if Assigned(FOnCombineFiles) or Assigned(LFileSystem) then begin + if LContext.IsAuthenticated(ASender) then begin + if ASender.UnparsedParams = '' then begin + CmdInvalidParamNum(ASender); + Exit; + end; + if Pos('"', ASender.UnparsedParams) > 0 then begin + LBuf := ASender.UnparsedParams; + Fetch(LBuf,'"'); + LTargetFileName := Fetch(LBuf, '"'); + LTargetFileName := DoProcessPath(LContext, LTargetFileName); + LBuf := Trim(LBuf); + LFileParts := TStringList.Create; + try + while LBuf <> '' do begin + Fetch(LBuf,'"'); + LFileParts.Add(DoProcessPath(LContext, Fetch(LBuf,'"'))); + end; + DoOnCombineFiles(LContext, LTargetFileName, LFileParts); + ASender.Reply.SetReply(250, RSFTPFileOpSuccess); + finally + FreeAndNil(LFileParts); + end; + end else begin + CmdInvalidParams(ASender); + end; + end; + end else begin + CmdNotImplemented(ASender); + end; +end; + +procedure TIdFTPServer.DoConnect(AContext: TIdContext); +var + LGreeting : TIdReplyRFC; +begin + AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; + if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin + if FUseTLS = utUseImplicitTLS then begin + TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False; + end; + end; + (AContext as TIdFTPServerContext).FXAUTKey := MakeXAUTKey; + if Assigned(OnGreeting) then begin + LGreeting := TIdReplyRFC.Create(nil); + try + LGreeting.Assign(Greeting); + OnGreeting(TIdFTPServerContext(AContext), LGreeting); + ReplyTexts.UpdateText(LGreeting); + if (not GetFIPSMode) and FSupportXAUTH and (LGreeting.NumericCode = 220) then begin + (AContext as TIdFTPServerContext).FXAUTKey := IdFTPCommon.MakeXAUTKey; + XAutGreeting(AContext,LGreeting, GStack.HostName); + end; + AContext.Connection.IOHandler.Write(LGreeting.FormattedReply); + if Assigned(OnConnect) then begin + OnConnect(AContext); + end; + if LGreeting.NumericCode = 421 then begin + AContext.Connection.Disconnect(False); + end; + finally + FreeAndNil(LGreeting); + end; + end else begin + if (not GetFIPSMode) and FSupportXAUTH and (Greeting.NumericCode = 220) then begin + LGreeting := TIdReplyRFC.Create(nil); + try + LGreeting.Assign(Greeting); + XAutGreeting(AContext,LGreeting, GStack.HostName); + AContext.Connection.IOHandler.Write(LGreeting.FormattedReply); + if Assigned(OnConnect) then begin + OnConnect(AContext); + end; + if LGreeting.NumericCode = 421 then begin + AContext.Connection.Disconnect(False); + end; + finally + FreeAndNil(LGreeting); + end; + end else begin + inherited DoConnect(AContext); + end; + end; +end; + +procedure TIdFTPServer.CommandQUIT(ASender: TIdCommand); +begin + if Assigned(FOnQuitBanner) then begin + FOnQuitBanner(TIdFTPServerContext(ASender.Context), ASender.Reply); + ASender.Disconnect := True; + end else begin + ASender.Reply.Assign(ASender.CommandHandler.NormalReply); + end; + ASender.Reply.SetReply(221, ASender.Reply.Text.Text); +end; + +procedure TIdFTPServer.CommandMLSD(ASender: TIdCommand); +var + LStream: TStringList; + LSendData : Boolean; + LContext : TIdFTPServerContext; +begin + if not Assigned(OnListDirectory) then begin + CmdSyntaxError(ASender); + Exit; + end; + LContext := ASender.Context as TIdFTPServerContext; + LSendData := False; + if LContext.IsAuthenticated(ASender) then begin + if (not Assigned(LContext.FDataChannel)) or LContext.FDataPortDenied then begin + ASender.Reply.SetReply(425, RSFTPCantOpenData); + Exit; + end; + LStream := TStringList.Create; + try + ListDirectory(LContext, DoProcessPath(LContext, ASender.UnparsedParams), + LStream, TextIsSame(ASender.CommandHandler.Command, 'LIST'), 'MLSD'); {Do not translate} + LSendData := True; + finally + try + if LSendData then begin + //it should be safe to assume that the FDataChannel object exists because + //we checked it earlier + LContext.FDataChannel.Data := LStream; + LContext.FDataChannel.OKReply.SetReply(226, RSFTPDataConnClosed); + LContext.FDataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally); + LContext.FDataChannel.FFtpOperation := ftpRetr; + ASender.Reply.SetReply(125, RSFTPDataConnToOpen); + ASender.SendReply; + DoDataChannelOperation(ASender); + end else begin + LContext.KillDataChannel; + ASender.Reply.SetReply(426, RSFTPDataConnClosedAbnormally); + end; + finally + LStream.Free; + end; + end; + end; +end; + +procedure TIdFTPServer.CommandMLST(ASender: TIdCommand); +var + LStream : TStringList; + i : Integer; + LContext : TIdFTPServerContext; + LPath : String; + LDir : TIdFTPListOutput; +begin + if Assigned(OnListDirectory) or Assigned(FOnMLST) then begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LStream := TStringList.Create; + try + LPath := DoProcessPath(LContext, ASender.UnparsedParams); + if Assigned(FOnMLST) then begin + LDir := TIdFTPListOutput.Create; + try + FOnMLST(LContext, LPath, LDir); + LDir.MLISTOutputDir(LStream, LContext.MLSOpts); + finally + FreeAndNil(LDir); + end; + end else begin + //this part is kept just for backwards compatibility + ListDirectory(LContext, LPath, LStream, True, 'MLST'); {Do not translate} + end; + ASender.Reply.Clear; + SetRFCReplyFormat(ASender.Reply); + ASender.Reply.NumericCode := 250; + ASender.Reply.Text.Add('Begin'); {do not localize} + for i := 0 to LStream.Count -1 do begin + ASender.Reply.Text.Add(' ' + LStream[i]); + end; + ASender.Reply.Text.Add('End'); {do not localize} + ASender.SendReply; + finally + FreeAndNil(LStream); + end; + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.DoOnSetModifiedTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.SetModifiedFileDate(AContext, AFileName, VDateTime); + end else if Assigned(FOnSetModifiedTime) then begin + FOnSetModifiedTime(AContext, AFileName, VDateTime); + end; +end; + +procedure TIdFTPServer.DoOnSetModifiedTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTimeStr : String); +var + LTime : TDateTime; +begin + LTime := FTPMLSToGMTDateTime(VDateTimeStr); + DoOnSetModifiedTime(AContext, AFileName, LTime); + VDateTimeStr := FTPGMTDateTimeToMLS(LTime); +end; + +procedure TIdFTPServer.DoOnSetCreationTime(AContext: TIdFTPServerContext; const AFileName : String; var VDateTime: TDateTime); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + //LFileSystem.SetCreationFileDate(AContext,AFileName,VDateTime); + end else if Assigned(FOnSetCreationTime) then begin + FOnSetCreationTime(AContext, AFileName, VDateTime); + end; +end; + +procedure TIdFTPServer.DoOnSetCreationTimeGMT(AContext: TIdFTPServerContext; + const AFileName: String; var VDateTime: TDateTime); +begin + if Assigned(FOnSetCreationTime) then begin + FOnSetCreationTime(AContext, AFileName, VDateTime); + end; +end; + +procedure TIdFTPServer.DoOnSetModifiedTimeGMT(AContext: TIdFTPServerContext; + const AFileName: String; var VDateTime: TDateTime); +begin + if Assigned(FOnSetModifiedTime) then begin + FOnSetModifiedTime(AContext, AFileName, VDateTime); + end; +end; + +procedure TIdFTPServer.DoOnSetCreationTime(AContext: TIdFTPServerContext; + const AFileName : String; var VDateTimeStr : String); +var + LTime : TDateTime; +begin + LTime := FTPMLSToLocalDateTime(VDateTimeStr); + DoOnSetCreationTime(AContext, AFileName, LTime); + VDateTimeStr := FTPLocalDateTimeToMLS(LTime); +end; + +procedure TIdFTPServer.CommandMFMT(ASender: TIdCommand); +var + LTimeStr, LFileName : String; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LFilesystem := FTPFileSystem; + if Assigned(FOnSetModifiedTime) or Assigned(LFileSystem) then begin + LFileName := ASender.UnparsedParams; + LTimeStr := Fetch(LFileName); + LFileName := DoProcessPath(LContext, LFileName); + DoOnSetModifiedTime(LContext, LFileName, LTimeStr); + ASender.Reply.SetReply(213, IndyFormat('Modify=%s %s', [LTimeStr, LFileName])); {Do not translate} + end else begin + CmdSyntaxError(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandMFCT(ASender: TIdCommand); +var + LTimeStr, LFileName : String; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FTPFileSystem; + if Assigned(FOnSetCreationTime) or Assigned(LFileSystem) then begin + LFileName := ASender.UnparsedParams; + LTimeStr := Fetch(LFileName); + LFileName := DoProcessPath(LContext, LFileName); + DoOnSetCreationTime(LContext, LFileName, LTimeStr); + ASender.Reply.SetReply(213, IndyFormat('CreateTime=%s %s', [LTimeStr, LFileName])); {Do not translate} + end else begin + CmdSyntaxError(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandMFF(ASender: TIdCommand); +var + LFacts : TStringList; + LFileName : String; + LValue : String; + s : String; + LContext : TIdFTPServerContext; + LAttrib : UInt32; + LAuth : Boolean; + LDummyDate1, LDummyDate2 : TDateTime; + LDate : TDateTime; + LCHMOD : Integer; + LDummy : String; +begin + LAuth := True; + LDummy := ''; //empty value for passing a var in case we need to do that + LContext := TIdFTPServerContext(ASender.Context); + //this may need to change if we make more facts to modify + if not Assigned(FOnSetModifiedTime) and not Assigned(FOnSetCreationTime) then begin + CmdSyntaxError(ASender); + Exit; + end; + s := ''; + if ASender.UnparsedParams = '' then begin + CmdInvalidParamNum(ASender); + Exit; + end; + if LContext.IsAuthenticated(ASender) then begin + LFacts := TStringList.Create; + try + LFileName := ParseFacts(ASender.UnparsedParams, LFacts); + LFileName := DoProcessPath(LContext, LFileName); + if LFacts.Values['Modify'] <> '' then begin {Do not translate} + if Assigned(FOnSetModifiedTime) then begin + LValue := LFacts.Values['Modify']; {Do not translate} + DoOnSetModifiedTime(LContext, LFileName, LValue); + s := s + IndyFormat('Modify=%s;', [LValue]); {Do not translate} + end; + end; + if LFacts.Values['Create'] <> '' then begin {Do not translate} + if Assigned(FOnSetCreationTime) then begin + LValue := LFacts.Values['Create']; {Do not translate} + DoOnSetCreationTime(LContext, LFileName, LValue); + s := s + IndyFormat('Create=%s;', [LValue]); {Do not translate} + end; + end; + if LFacts.Values['Win32.ea'] <> '' then begin + if Assigned(FOnSetATTRIB) then begin + LValue := LFacts.Values['Win32.ea']; {Do not localize} + LAttrib := IndyStrToInt(LValue); + DoOnSetAttrib(LContext, LAttrib, LFileName, LAuth); + LValue := '0x' + IntToHex(LAttrib, 8); + s := s + IndyFormat('Win32.ea=%s;', [LValue]); {Do not translate} + end; + end; + if LFacts.Values['Unix.mode'] <> '' then begin + LValue := LFacts.Values['Unix.mode']; {Do not localize} + if Assigned(FOnSiteCHMOD) then begin + If IsValidPermNumbers(LValue) then begin + LCHMOD := IndyStrToInt(LValue); + DoOnSiteCHMOD(LContext, LCHMOD, LFileName, LAuth); + LValue := IndyFormat('%.4d', [LCHMOD]); + s := s + IndyFormat('Unix.mode=%s;', [LValue]); {Do not translate} + end; + end; + end; + if LFacts.Values['Unix.owner'] <> '' then begin {Do not localize} + LValue := LFacts.Values['Unix.owner']; {Do not localize} + if Assigned(FOnSiteCHOWN) then begin + DoOnSiteCHOWN(LContext, LValue, LDummy, LFileName, LAuth); + s := s + IndyFormat('Unix.owner=%s;', [LValue]); {Do not localize} + end; + end; + if LFacts.Values['Unix.group'] <> '' then begin {Do not localize} + LValue := LFacts.Values['Unix.group']; {Do not localize} + if Assigned(FOnSiteCHGRP) then begin + DoOnSiteCHGRP(LContext, LValue, LFileName, LAuth); + s := s + IndyFormat('Unix.group=%s;', [LValue]); {Do not localize} + end; + end; + if LFacts.Values['Windows.lastaccesstime'] <> '' then begin + LValue := LFacts.Values['Windows.lastaccesstime']; + if Assigned(FOnSiteUTIME) and (mlsdFileLastAccessTime in FMLSDFacts) then begin + LDate := FTPMLSToGMTDateTime(LValue); + LDummyDate1 := 0; + LDummyDate2 := 0; + FOnSiteUTIME(LContext, LFileName, LDate, LDummyDate1, LDummyDate2, LAuth); + LValue := FTPGMTDateTimeToMLS(LDate); + s := s + IndyFormat('Windows.lastaccesstime=%s;', [LValue]); + end; + end; + if s <> '' then begin + ASender.Reply.SetReply(213, s + ' ' + LFileName); + end else begin + ASender.Reply.SetReply(504, IndyFormat(RSFTPParamError, ['MFF'])); {Do not translate} + end; + finally + FreeAndNil(LFacts); + end; + end; +end; + +function TIdFTPServer.GetMD5Checksum(ASender : TIdFTPServerContext; const AFileName : String ) : String; +var + LCalcStream : TStream; +begin + Result := ''; + DoOnMD5Cache(ASender, AFileName, Result); + if Result = '' then begin + LCalcStream := nil; + DoOnCRCFile(ASender, AFileName, LCalcStream); + if Assigned(LCalcStream) then try + LCalcStream.Position := 0; + Result := CalculateCheckSum(TIdHashMessageDigest5, LCalcStream, 0, LCalcStream.Size); + DoOnMD5Verify(ASender, AFileName, Result); + finally + FreeAndNil(LCalcStream); + end; + end; +end; + +procedure TIdFTPServer.CommandMMD5(ASender: TIdCommand); +var + LChecksum : String; + LRes : String; + LFiles : TStringList; + LError : Boolean; + i : Integer; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if GetFIPSMode then begin + CmdSyntaxError(ASender); + Exit; + end; + LError := False; + LChecksum := ''; + LRes := ''; + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FTPFileSystem; + if Assigned(FOnCRCFile) or Assigned(FOnMD5Cache) or Assigned(LFileSystem) then begin + LFiles := TStringList.Create; + try + ParseQuotedArgs(ASender.UnparsedParams, LFiles); + for i := 0 to LFiles.Count -1 do begin + LChecksum := GetMD5Checksum(LContext, DoProcessPath(LContext, UnquotedStr(LFiles[i]))); + if LChecksum = '' then begin + LError := True; + Break; + end; + LRes := LRes + ',' + LFiles[i] + ' '+ LChecksum; + end; + IdDelete(LRes,1,1); + finally + FreeAndNil(LFiles); + end; + if LError then begin + //The http://www.potaroo.net/ietf/idref/draft-twine-ftpmd5/ + //draft didn't specify 550 as an error. + CmdTwineFileActionAborted(ASender); + end else begin + ASender.Reply.SetReply(252, LRes); + end; + end else begin + CmdSyntaxError(ASender); + end; + end; +end; + +procedure TIdFTPServer.CommandMD5(ASender: TIdCommand); +var + LChecksum : String; + LContext : TIdFTPServerContext; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := TIdFTPServerContext(ASender.Context); + if GetFIPSMode then begin + CmdSyntaxError(ASender); + Exit; + end; + LChecksum := ''; + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FTPFileSystem; + if Assigned(FOnCRCFile) or Assigned(LFileSystem) then begin + LChecksum := GetMD5Checksum(LContext, DoProcessPath(LContext, ASender.UnparsedParams)); + if LChecksum = '' then begin + CmdTwineFileActionAborted(ASender); + end else begin + ASender.Reply.SetReply(251, LChecksum); + end; + end else begin + CmdSyntaxError(ASender); + end; + end; +end; + +procedure TIdFTPServer.DoOnMD5Verify(ASender: TIdFTPServerContext; + const AFileName, ACheckSum: String); +begin + if Assigned(OnMD5Verify) then begin + OnMD5Verify(ASender, AFileName, AChecksum); + end; +end; + +procedure TIdFTPServer.DoOnMD5Cache(ASender: TIdFTPServerContext; + const AFileName: String; var VCheckSum: String); +begin + if Assigned(OnMD5Cache) then begin + OnMD5Cache(ASender, AFileName, VCheckSum); + end; +end; + +procedure TIdFTPServer.DoDisconnect(AContext: TIdContext); +var + // under ARC, convert a weak reference to a strong reference before working with it + LUserAccounts: TIdCustomUserManager; +begin + LUserAccounts := FUserAccounts; + if Assigned(LUserAccounts) then begin + LUserAccounts.UserDisconnected(TIdFTPServerContext(AContext).UserName); + {$IFDEF USE_OBJECT_ARC}LUserAccounts := nil;{$ENDIF} + end; + inherited DoDisconnect(AContext); +end; + +procedure TIdFTPServer.DoOnCRCFile(ASender: TIdFTPServerContext; + const AFileName: String; var VStream: TStream); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.GetCRCCalcStream(ASender, AFileName, VStream); + end else if Assigned(FOnCRCFile) then begin + FOnCRCFile(ASender, AFileName, VStream); + end; +end; + +procedure TIdFTPServer.DoOnCombineFiles(ASender: TIdFTPServerContext; + const ATargetFileName: string; AParts: TStrings); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.CombineFiles(ASender, ATargetFileName, AParts); + end else if Assigned(FOnCombineFiles) then begin + FOnCombineFiles(ASender, ATargetFileName, AParts); + end; +end; + +procedure TIdFTPServer.DoOnRenameFile(ASender: TIdFTPServerContext; + const ARenameFromFile, ARenameToFile: string); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.RenameFile(ASender, ARenameToFile); + end else if Assigned(FOnRenameFile) then begin + FOnRenameFile(ASender, ARenameFromFile, ARenameToFile); + end; +end; + +procedure TIdFTPServer.DoOnGetFileDate(ASender: TIdFTPServerContext; + const AFilename: string; var VFileDate: TDateTime); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.GetFileDate(ASender, AFileName, VFileDate); + VFileDate := VFileDate - OffsetFromUTC; + end else if Assigned(FOnGetFileDate) then begin + FOnGetFileDate(ASender, AFileName, VFileDate); + end; +end; + +procedure TIdFTPServer.DoOnGetFileSize(ASender: TIdFTPServerContext; + const AFilename: string; var VFileSize: Int64); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.GetFileSize(ASender, AFileName, VFileSize); + end else if Assigned(FOnGetFileSize) then begin + FOnGetFileSize(ASender, AFileName, VFileSize); + end; +end; + +procedure TIdFTPServer.DoOnDeleteFile(ASender: TIdFTPServerContext; + const APathName: string); +var + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LFileSystem := FTPFileSystem; + if Assigned(LFileSystem) then begin + LFileSystem.DeleteFile(ASender, APathName); + end else if Assigned(FOnDeleteFile) then begin + FOnDeleteFile(ASender, APathName); + end; +end; + +procedure TIdFTPServer.SetUseTLS(AValue: TIdUseTLS); +begin + inherited SetUseTLS(AValue); + if AValue = utUseImplicitTLS then + begin + if DefaultDataPort = IdPORT_FTP_DATA then begin + DefaultDataPort := IdPORT_ftps_data; + end; + end + else if DefaultDataPort = IdPORT_ftps_data then begin + DefaultDataPort := IdPORT_FTP_DATA; + end; +end; + +procedure TIdFTPServer.DisconUser(ASender: TIdCommand); +begin + ASender.Disconnect := True; + ASender.Reply.SetReply(421, RSFTPClosingConnection); + if Assigned(OnLoginFailureBanner) then begin + OnLoginFailureBanner(TIdFTPServerContext(ASender.Context), ASender.Reply); + ASender.Reply.SetReply(421, ASender.Reply.Text.Text); + end; +end; + +procedure TIdFTPServer.SetRFCReplyFormat(AReply: TIdReply); +begin + if AReply is TIdReplyFTP then begin + TIdReplyFTP(AReply).ReplyFormat := rfIndentMidLines; + end; +end; + +procedure TIdFTPServer.CommandSiteATTRIB(ASender : TIdCommand); +var + LContext : TIdFTPServerContext; + LFileName, + LAttrs : String; + LAttrVal : UInt32; + LPermitted : Boolean; + + function ValidAttribStr(const AAttrib : String) : Boolean; + var i : Integer; + begin + Result := TextStartsWith(AAttrib, '+'); + if Result then begin + Result := Length(AAttrib)>1; + if result then begin + if AAttrib = '+N' then begin + Exit; + end; + for i := 2 to Length(AAttrib) do begin + if not CharIsInSet(AAttrib,i,'RASH') then begin + Result := False; + break; + end; + end; + end; + end; + end; + +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(OnSetAttrib) then begin + LFileName := ASender.UnparsedParams; + LAttrs := Fetch(LFileName); + LPermitted := True; + LAttrs := UpperCase(LAttrs); + if TextStartsWith(LAttrs, '+') then begin + if ValidAttribStr(LAttrs) then begin + LAttrVal := 0; + ASender.Reply.Clear; + ASender.Reply.SetReply(220,''); + if IndyPos('R', LATTRS) > 0 then begin + LAttrVal := LAttrVal or IdFILE_ATTRIBUTE_READONLY; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg+' : +FILE_ATTRIBUTE_READONLY'); {Do not localize} + end; + if IndyPos('A', LATTRS) > 0 then begin + LAttrVal := LAttrVal or IdFILE_ATTRIBUTE_ARCHIVE; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg+' : +FILE_ATTRIBUTE_ARCHIVE'); {Do not localize} + end; + if IndyPos('S', LATTRS) > 0 then begin + LAttrVal := LAttrVal or IdFILE_ATTRIBUTE_SYSTEM; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg+' : +FILE_ATTRIBUTE_SYSTEM'); {Do not localize} + end; + if IndyPos('H', LATTRS) > 0 then begin + LAttrVal := LAttrVal or IdFILE_ATTRIBUTE_HIDDEN; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg+' : +FILE_ATTRIBUTE_HIDDEN'); {Do not localize} + end; + if IndyPos('N', LATTRS) > 0 then begin + LAttrVal := LAttrVal or IdFILE_ATTRIBUTE_NORMAL; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg+' : +FILE_ATTRIBUTE_NORMAL'); {Do not localize} + end; + ASender.Reply.Text.Add(RSFTPSiteATTRIBMsg + IndyFormat(RSFTPSiteATTRIBDone, [IntToStr(Length(LAttrs)-1)])); + LFileName := DoProcessPath(LContext, LFileName); + DoOnSetATTRIB(LContext, LAttrVal, LFileName, LPermitted); + end else begin + ASender.Reply.SetReply(550,RSFTPSiteATTRIBInvalid); + Exit; + end; + if not LPermitted then begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end else begin + ASender.Reply.SetReply(550,RSFTPSiteATTRIBInvalid); + Exit; + end; + end else begin + ASender.Reply.Assign(FReplyUnknownSITECommand); + end; + end; +end; + +procedure TIdFTPServer.CommandSiteUTIME(ASender: TIdCommand); + + procedure TryNewFTPSyntax(AContext: TIdFTPServerContext; ALSender: TIdCommand); + var + LgMTime : TDateTime; + LgPermitted : Boolean; + LFileName : String; + LDummy1, LDummy2 : TDateTime; + begin + //this is for gFTP Syntax + //such as: "SITE UTIME 20050815041129 /.bashrc" + LgPermitted := True; + if ALSender.Params.Count = 0 then begin + CmdSyntaxError(ALSender); + Exit; + end; + if IsValidTimeStamp(ALSender.Params[0]) then begin + LFileName := ALSender.UnparsedParams; + //This is local Time + LgMTime := FTPMLSToGMTDateTime(Fetch(LFileName)) - OffsetFromUTC; + LFileName := DoProcessPath(AContext, LFileName); + if Assigned(FOnSiteUTIME) then + begin + //indicate that both creation time and last access time should not be set + LDummy1 := 0; + LDummy2 := 0; + FOnSiteUTIME(AContext, LFileName, LDummy1, LgMTime, LDummy2, LgPermitted); + end + else if Assigned(FOnSetModifiedTime) then begin + FOnSetModifiedTime(AContext, LFileName, LgMTime); + end; + if LgPermitted then begin + ALSender.Reply.SetReply(200, RSFTPCHMODSuccessful); + end else begin + ALSender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end else + begin + CmdSyntaxError(ALSender); + end; + end; + +var + LContext : TIdFTPServerContext; + LPermitted : Boolean; + LFileName : String; + LIdx : Integer; + LDateCount : Integer; + LAccessTime, LModTime, LCreateTime : TDateTime; + i : Integer; +begin +{ +This is used by NcFTP like this: + +SITE UTIME test.txt 20050731224504 20050731041205 20050731035940 UTC + +where the first date is the "Last Access Time" +the second date is the "Last Modified Time" +and the final date is the "Creation File Time" + +I think the third parameter is optional. + +The final parameter is "UTC" + +gFTP does something different. It does something like: + +SITE UTIME 20050815041129 /.bashrc + +where the timestamp is probably in based on the local time. +} + LPermitted := True; + LAccessTime := 0; + LModTime := 0; + LCreateTime := 0; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then + begin + if Assigned(OnSiteUTIME) or Assigned(OnSetModifiedTime) or Assigned(OnSetCreationTime) then begin + LDateCount := 0; + LIdx := ASender.Params.Count - 1; + if ASender.Params.Count > 2 then begin + LPermitted := True; + if TextIsSame(ASender.Params[LIdx], 'UTC') then begin + //figure out how many dates we have and where the end of the filename is + Dec(LIdx); + Inc(LDateCount); + if IsValidTimeStamp(ASender.Params[LIdx]) then begin + Dec(LIdx); + Inc(LDateCount); + if IsValidTimeStamp(ASender.Params[LIdx]) then begin + Dec(LIdx); + Inc(LDateCount); + end; + end else begin + TryNewFTPSyntax(LContext, ASender); + Exit; + end; + //now extract the date + LAccessTime := FTPMLSToGMTDateTime(ASender.Params[LIdx]); + if LDateCount > 1 then begin + LModTime := FTPMLSToGMTDateTime(ASender.Params[LIdx+1]); + end; + if LDateCount > 2 then begin + LCreateTime := FTPMLSToGMTDateTime(ASender.Params[LIdx+2]); + end; + //extract filename including any spaces + LFileName := ''; + for i := 0 to LIdx-1 do begin + LFileName := LFileName + ' ' + ASender.Params[i]; + end; + IdDelete(LFileName,1,1); + LFileName := DoProcessPath(LContext,LFileName); + //now do it + if Assigned(FOnSiteUTIME) then begin + FOnSiteUTIME(LContext, LFileName, LAccessTime, LModTime, LCreateTime, LPermitted); + end else begin + if (LModTime <> 0) and Assigned(FOnSetModifiedTime) then begin + FOnSetModifiedTime(LContext, LFileName, LModTime); + end; + if (LCreateTime <> 0) and Assigned(FOnSetCreationTime) then begin + FOnSetCreationTime(LContext, LFileName, LCreateTime); + end; + end; + if LPermitted then begin + ASender.Reply.SetReply(200, RSFTPCHMODSuccessful); + end else begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + Exit; + end; + end; + end; + + TryNewFTPSyntax(LContext, ASender); + // CmdNotImplemented(ASender); + end; +end; + +procedure TIdFTPServer.DoOnSiteCHGRP(ASender: TIdFTPServerContext; + var AGroup: String; const AFileName: String; var VAUth: Boolean); +begin + if Assigned(FOnSiteCHGRP) then begin + FOnSiteCHGRP(ASender, AGroup, AFileName, VAuth); + end; +end; + +procedure TIdFTPServer.DoOnSiteCHOWN(ASender: TIdFTPServerContext; var AOwner, + AGroup: String; const AFileName: String; var VAUth: Boolean); +begin + if Assigned(FOnSiteCHOWN) then begin + OnSiteCHOWN(ASender, AOwner, AGroup, AFileName, VAuth); + end; +end; + +procedure TIdFTPServer.CommandSiteCHOWN(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LPermitted : Boolean; + LFileName : String; + LOwner, LGroup : string; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(OnSiteCHOWN) then begin + LPermitted := True; + LFileName := ASender.UnparsedParams; + LGroup := Fetch(LFileName); + LOwner := Fetch(LGroup,':'); + DoOnSiteCHOWN(LContext, LOwner, LGroup, DoProcessPath(LContext, LFileName), LPermitted); + if LPermitted then begin + ASender.Reply.SetReply(220, IndyFormat(RSFTPCmdSuccessful, [ASender.RawLine])); + end else begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end; + end; +end; + +procedure TIdFTPServer.CommandSiteCHGRP(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LPermitted : Boolean; + LFileName : String; + LGroup : String; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(FOnSiteCHGRP) then begin + LPermitted := True; + LFileName := ASender.UnparsedParams; + LGroup := Fetch(LFileName); + DoOnSiteCHGRP(LContext, LGroup, DoProcessPath(LContext, LFileName), LPermitted); + if LPermitted then begin + ASender.Reply.SetReply(200, IndyFormat(RSFTPCmdSuccessful, [ASender.RawLine])); + end else begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end; + end; +end; + +procedure TIdFTPServer.CommandSiteCHMOD(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LPermitted : Boolean; + LFileName : String; + LPerms : String; + LPermNo : Integer; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + LFileSystem := FTPFileSystem; + if Assigned(OnSiteCHMOD ) or Assigned(LFileSystem) then begin + LFileName := ASender.UnparsedParams; + LPerms := Fetch(LFileName); + If IsValidPermNumbers(LPerms) then begin + LPermitted := True; + LPermNo := IndyStrToInt(LPerms, 0); + DoOnSiteCHMOD(LContext, LPermNo, DoProcessPath(LContext, LFileName), LPermitted); + if LPermitted then begin + ASender.Reply.SetReply(220, RSFTPCHMODSuccessful); + end else begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.Assign(FReplyUnknownSITECommand); + end; + end; +end; + +procedure TIdFTPServer.CommandSiteUMASK(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LNewMask : Integer; + LPermitted : Boolean; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if Assigned(FOnSiteUMASK) then begin + if ASender.Params.Count > 0 then begin + If IsValidPermNumbers(ASender.Params[0]) then begin + LPermitted := True; + LNewMask := IndyStrToInt(ASender.Params[0], 0); + DoOnSiteUMASK(LContext, LNewMask, LPermitted); + if LPermitted then begin + ASender.Reply.SetReply(200, IndyFormat(RSFTPUMaskSet, [LNewMask, LContext.FUMask])); + LContext.FUMask := LNewMask; + end else begin + ASender.Reply.SetReply(553, RSFTPPermissionDenied); + end; + end else begin + CmdNotImplemented(ASender); + end; + end else begin + ASender.Reply.SetReply(200, IndyFormat(RSFTPUMaskIs, [LContext.FUMask])); + end; + end else begin + CmdNotImplemented(ASender); + end; + end; +end; + +function TIdFTPServer.IsValidPermNumbers(const APermNos: String): Boolean; +const + PERMDIGITS = '01234567'; +var + i: Integer; +begin + Result := False; + for i := 1 to Length(APermNos) do begin + if not CharIsInSet(APermNos, i, PERMDIGITS) then begin + Exit; + end; + end; + Result := True; +end; + +procedure TIdFTPServer.DoOnSiteUMASK(ASender: TIdFTPServerContext; + var VUMASK: Integer; var VAUth: Boolean); +begin + if Assigned(FOnSiteUMASK) then begin + FOnSiteUMASK(ASender,VUMASK,VAUth); + end; +end; + +procedure TIdFTPServer.DoOnSetATTRIB(ASender: TIdFTPServerContext; var VAttr : UInt32; const AFileName : String; var VAUth : Boolean); +begin + if Assigned( FOnSetATTRIB) then begin + FOnSetATTRIB(ASender, VAttr, AFileName, VAUth); + end; +end; + +procedure TIdFTPServer.DoOnSiteCHMOD(ASender: TIdFTPServerContext; + var APermissions: Integer; const AFileName: String; var VAUth: Boolean); +begin + if Assigned(FOnSiteCHMOD) then begin + FOnSiteCHMOD(ASender,APermissions,AFileName,VAUth); + end; +end; + +procedure TIdFTPServer.CommandSiteDIRSTYLE(ASender: TIdCommand); +//FMSDOSMode +var + LContext : TIdFTPServerContext; +//SITE DIRSTYLE is only for MS-DOS formatted directory lists so +//a program can flip to Unix directory list format. This is +//for compatability purposes, ONLY. +begin + LContext := ASender.Context as TIdFTPServerContext; + if (FDirFormat = ftpdfDOS) or + ((FDirFormat = ftpdfOSDependent) and (GOSType = otWindows)) then begin + if LContext.IsAuthenticated(ASender) then begin + if ASender.Params.Count = 0 then begin + LContext.FMSDOSMode := not LContext.FMSDOSMode; + if LContext.FMSDOSMode then begin + ASender.Reply.SetReply(200, IndyFormat(RSFTPDirStyle, [RSFTPOn])); + end else begin + ASender.Reply.SetReply(200, IndyFormat(RSFTPDirStyle, [RSFTPOff])); + end; + end; + end; + end else begin + ASender.Reply.Assign(FReplyUnknownSITECommand); + end; +end; + +procedure TIdFTPServer.CommandSiteHELP(ASender: TIdCommand); +var + s : String; + LCmds : TStringList; + LContext : TIdFTPServerContext; +begin + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + s := RSFTPSITECmdsSupported+EOL; + LCmds := TStringList.Create; + try + if Assigned(OnSetAttrib) then begin + LCmds.Add('ATTRIB'); {Do not translate} + end; + if Assigned(OnSiteCHMOD) then begin + LCmds.Add('CHMOD'); {Do not translate} + end; + if (FDirFormat = ftpdfDOS) or + ((FDirFormat = ftpdfOSDependent) and (GOSType = otWindows)) then begin + LCmds.Add('DIRSTYLE'); {Do not translate} + end; + if Assigned(OnSiteUMASK) then begin + LCmds.Add('UMASK'); {Do not translate} + end; + LCmds.Add('ZONE'); {Do not translate} + s := s + HelpText(LCmds) + FEndOfHelpLine; + ASender.Reply.SetReply(214, s); + finally + FreeAndNil(LCmds); + end; + end; +end; + +function TIdFTPServer.HelpText(Cmds: TStrings): String; +var + LRows : Integer; + LMod : Integer; + i : Integer; +begin + Result := ''; + if Cmds.Count =0 then begin + Exit; + end; + LRows := Cmds.Count div 6; + LMod := Cmds.Count mod 6; + if Cmds.Count < 6 then begin + Result := ' '; + for i := 0 to Cmds.Count -1 do begin + Result := Result + IndyFormat('%-10s', [Cmds[i]]); + end; + Result := Result + CR; + end else begin + for i := 0 to (LRows -1) do begin + if (i <= LMod-1) and (LMod<>0) then begin + Result := Result + IndyFormat(' %-10s%-10s%-10s%-10s%-10s%-10s%-10s', {Do not translate} + [ Cmds[i],Cmds[i+LRows],Cmds[(LRows*2)+i], + Cmds[(LRows*3)+i],Cmds[(LRows*4)+i],Cmds[(LRows*5)+i], + Cmds[(LRows*6)+i]])+CR; + end else begin + Result := Result + IndyFormat(' %-10s%-10s%-10s%-10s%-10s%-10s', {Do not translate} + [ Cmds[i],Cmds[i+LRows],Cmds[(LRows*2)+i], + Cmds[(LRows*3)+i],Cmds[(LRows*4)+i],Cmds[(LRows*5)+i]])+CR; + end; + end; + end; +end; + +procedure TIdFTPServer.CommandSITE(ASender: TIdCommand); +var + LCmd : String; +begin + LCmd := ASender.UnparsedParams; + ASender.Reply.Clear; + ASender.PerformReply := False; + if not FSITECommands.HandleCommand(ASender.Context, LCmd) then begin + ASender.Reply.NumericCode := 500; + CmdSyntaxError(ASender.Context, ASender.CommandHandler.Command + ' ' + LCmd, ASender.Reply); + ASender.PerformReply := False; + end; +end; + +function TIdFTPServer.MLSFEATLine(const AFactMask: TIdMLSDAttrs; + const AFacts: TIdFTPFactOutputs): String; +begin + Result := 'MLST size'; {Do not translate} + //the * indicates if the option is selected for MLST + if Size in AFacts then begin {Do not translate} + Result := Result + '*;'; + end else begin + Result := Result + ';' + end; + Result := Result + 'Type'; {Do not translate} + if ItemType in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + if mlsdPerms in FMLSDFacts then begin + Result := Result + 'Perm'; {Do not translate} + if Perm in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdFileCreationTime in FMLSDFacts then begin + Result := Result + 'Create'; {Do not translate} + if CreateTime in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + Result := Result + 'Modify'; {Do not translate} + if Modify in AFacts then begin + Result := Result + '*;'; + end else begin + Result := Result + ';'; + end; + if mlsdUnixModes in FMLSDFacts then begin + Result := Result + 'UNIX.mode'; {Do not translate} + if UnixMODE in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdUnixOwner in FMLSDFacts then + begin + Result := Result + 'UNIX.owner'; {Do not translate} + if UnixOwner in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdUnixGroup in FMLSDFacts then begin + Result := Result + 'UNIX.group'; {Do not translate} + if UnixGroup in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdUniqueID in FMLSDFacts then begin + Result := Result + 'Unique'; {Do not translate} + if Unique in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdFileLastAccessTime in FMLSDFacts then begin + Result := Result + 'Windows.lastaccesstime'; {Do not translate} + if CreateTime in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdWin32Attributes in FMLSDFacts then begin + Result := Result + 'Win32.ea'; {Do not translate} + if WinAttribs in AFacts then begin {Do not translate} + Result := Result + '*;'; {Do not translate} + end else begin + Result := Result + ';'; + end; + end; + if mlsdWin32DriveType in FMLSDFacts then begin + Result := Result + 'Win32.dt'; + if WinDriveType in AFacts then begin + Result := Result + '*;'; {Do not localize} + end else begin + Result := Result + ';'; {Do not localize} + end; + end; + if mlstWin32DriveLabel in FMLSDFacts then begin + Result := Result + 'Win32.dl'; + if WinDriveLabel in AFacts then begin + Result := Result + '*;'; {Do not localize} + end else begin + Result := Result + ';'; {Do not localize} + end; + end; + if Length(Result)>0 then begin + IdDelete(Result,Length(Result),1); + end; +end; + +procedure TIdFTPServer.CommandCLNT(ASender: TIdCommand); +begin + if Assigned(FOnClientID) then begin + FOnClientID(ASender.Context as TIdFTPServerContext, ASender.UnparsedParams); + end; +end; + +procedure TIdFTPServer.SetPASVBoundPortMax(const AValue: TIdPort); +begin + if FPASVBoundPortMin <> 0 then begin + if AValue <= FPASVBoundPortMin then begin + raise EIdFTPBoundPortMaxGreater.Create(RSFTPPASVBoundPortMaxMustBeGreater); + end; + end; + FPASVBoundPortMax := AValue; +end; + +procedure TIdFTPServer.SetPASVBoundPortMin(const AValue: TIdPort); +begin + if FPASVBoundPortMax <> 0 then begin + if FPASVBoundPortMax <= AValue then begin + raise EIdFTPBoundPortMinLess.Create(RSFTPPASVBoundPortMinMustBeLess); + end; + end; + FPASVBoundPortMin := AValue; +end; + +procedure TIdFTPServer.DoOnDataPortAfterBind(ASender: TIdFTPServerContext); +begin + if Assigned(FOnDataPortAfterBind) then begin + FOnDataPortAfterBind(ASender); + end; +end; + +procedure TIdFTPServer.DoOnDataPortBeforeBind(ASender: TIdFTPServerContext); +begin + if Assigned(FOnDataPortBeforeBind) then begin + FOnDataPortBeforeBind(ASender); + end; +end; + +function TIdFTPServer.FTPNormalizePath(const APath: String): String; +{ +Microsoft IIS accepts both a "/" and a "\" as path/file name separators. +We have to flatten this out so that our FTP server can behave like Microsoft IIS. + +In Unix, a "\" is a valid filename character so we don't anything there. + +This WILL cause a "\" to be treated differently in Unix and Win32. I submit that +this is really desirable as both file systems are like apples and oranges. +} +begin + case FPathProcessing of + ftppDOS : Result := ReplaceAll(APath, '\', '/'); + ftpOSDependent : + begin + if GOSType = otWindows then begin + Result := ReplaceAll(APath, '\', '/'); + end else begin + Result := APath; + end; + end; + else + Result := APath; + end; +end; + +function TIdFTPServer.DoProcessPath(ASender: TIdFTPServerContext; const APath: TIdFTPFileName): TIdFTPFileName; +begin + if FPathProcessing <> ftppCustom then begin + Result := FTPNormalizePath(APath); + Result := ProcessPath(ASender.CurrentDir, Result); {Do not Localize} + end else begin + Result := APath; + if Assigned(FOnCustomPathProcess) then begin + FOnCustomPathProcess(ASender, Result); + end; + end; +end; + +function TIdFTPServer.CDUPDir(AContext : TIdFTPServerContext) : String; +const + LCDUP_DOS = '..\'; + CDUP_UNIX = '../'; +begin + case FPathProcessing of + ftppDOS : Result := LCDUP_DOS; + ftpOSDependent : + if GOSType = otWindows then begin + Result := LCDUP_DOS; + end else begin + Result := CDUP_UNIX; + end; + ftppCustom : Result := DoProcessPath(AContext, '..'); + else + Result := CDUP_UNIX; + end; +end; + +function TIdFTPServer.DoSysType(ASender: TIdFTPServerContext): String; +begin +//We tie the SYST descriptor to the directory style for compatability +//reasons. Some FTP clients use the SYST descriptor to determine what +//type of FTP directory list parsing to do. I think TurboPower IPros does this. +//Note that I personally do not find this to be sound as a general rule. + case FDirFormat of + ftpdfOSDependent : + begin + if GOSType = otWindows then begin + Result := SYST_ID_NT; + end else begin + Result := SYST_ID_UNIX; + end; + end; + ftpdfUnix, ftpdfEPLF : Result := SYST_ID_UNIX; + ftpdfDOS : Result := SYST_ID_NT; + ftpdfCustom : Result := FCustomSystID; + end; +end; + +procedure TIdFTPServer.DoOnCustomListDirectory( + ASender: TIdFTPServerContext; const APath: string; + ADirectoryListing: TStrings; const ACmd, ASwitches: String); +begin + if Assigned(OnCustomListDirectory) then begin + OnCustomListDirectory(ASender,APath,ADirectoryListing,ACmd,ASwitches); + end; +end; + +procedure TIdFTPServer.CmdNotImplemented(ASender: TIdCommand); +begin + ASender.Reply.SetReply(550, IndyFormat(RSFTPCmdNotImplemented, [ASender.CommandHandler.Command ])); +end; + +procedure TIdFTPServer.CmdFileActionAborted(ASender: TIdCommand); +begin + ASender.Reply.SetReply(550, RSFTPFileActionAborted); +end; + +//This is for where the client didn't provide a valid number of parameters for a command +procedure TIdFTPServer.CmdInvalidParamNum(ASender: TIdCommand); +begin + ASender.Reply.SetReply(501, IndyFormat(RSFTPInvalidNumberArgs, [ASender.CommandHandler.Command])); +end; + +//This is for other command syntax issues. +procedure TIdFTPServer.CmdInvalidParams(ASender: TIdCommand); +begin + ASender.Reply.SetReply(501, IndyFormat(RSFTPParamError, [ASender.CommandHandler.Command])); +end; + +procedure TIdFTPServer.CmdTwineFileActionAborted(ASender: TIdCommand); +begin + ASender.Reply.SetReply(504, RSFTPFileActionAborted); +end; + +procedure TIdFTPServer.CmdCommandSuccessful(ASender: TIdCOmmand; const AReplyCode : Integer = 250); +begin + ASender.Reply.SetReply(AReplyCode, IndyFormat(RSFTPCmdSuccessful, [ASender.CommandHandler.Command])); +end; + +procedure TIdFTPServer.CommandSSCN(ASender: TIdCommand); +const + REPLY_SSCN_ON = 'SSCN:CLIENT METHOD'; {do not localize} + REPLY_SSCN_OFF = 'SSCN:SERVER METHOD'; {do not localize} +var + LContext : TIdFTPServerContext; +begin + if UseTLS = utNoTLSSupport then begin + CmdNotImplemented(ASender); + Exit; + end; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if ASender.Params.Count = 0 then begin + //check state + if LContext.SSCNOn then begin + ASender.Reply.SetReply(200, REPLY_SSCN_ON); + end else begin + ASender.Reply.SetReply(200, REPLY_SSCN_OFF); + end; + end else begin + //set state + case PosInStrArray(ASender.Params[0], OnOffStates, False) of + 0 : //'ON' + begin + LContext.SSCNOn := True; + ASender.Reply.SetReply(200, REPLY_SSCN_ON); + end; + 1 : //'OFF' + begin + LContext.SSCNOn := False; + ASender.Reply.SetReply(200, REPLY_SSCN_OFF); + end; + else + ASender.Reply.SetReply(504, RSFTPInvalidForParam); + end; + end; + end; +end; + +procedure TIdFTPServer.CommandCPSV(ASender: TIdCommand); +var + LContext : TIdFTPServerContext; + LIO : TIdSSLIOHandlerSocketBase; +begin + //CPSV must be used with SSL and can only be used with IPv4 + if (UseTLS = utNoTLSSupport) or + (ASender.Context.Binding.IPVersion <> Id_IPv4) then begin + CmdSyntaxError(ASender); + Exit; + end; + CommandPASV(ASender); + LContext := TIdFTPServerContext(ASender.Context); + LIO := LContext.DataChannel.FDataChannel.IOHandler as TIdSSLIOHandlerSocketBase; + //tell IOHandler to use ssl_Conntect + LIO.IsPeer := False; +end; + +procedure TIdFTPServer.CommandSiteZONE(ASender: TIdCommand); +var + LMin : Integer; +begin + LMin := MinutesFromGMT; + //plus must always be displayed for positive numbers + if LMin < 0 then begin + ASender.Reply.SetReply(210, IndyFormat('UTC%d', [MinutesFromGMT])); {do not localize} + end else begin + ASender.Reply.SetReply(210, IndyFormat('UTC+%d', [MinutesFromGMT])); {do not localize} + end; +end; + +procedure TIdFTPServer.CommandCheckSum(ASender: TIdCommand); +const + HashTypes: array[0..4] of TIdHashClass = (TIdHashCRC32, TIdHashMessageDigest5, TIdHashSHA1, TIdHashSHA256, TIdHashSHA512); +var + LCalcStream : TStream; + LFileName, LCheckSum, LBuf : String; + LBeginPos, LEndPos : TIdStreamSize; + LContext : TIdFTPServerContext; + LHashIdx: Integer; + // under ARC, convert a weak reference to a strong reference before working with it + LFileSystem: TIdFTPBaseFileSystem; +begin + if GetFIPSMode and + (PosInStrArray(ASender.CommandHandler.Command, ['XCRC', 'XMD5']) > -1) then begin + CmdSyntaxError(ASender); + Exit; + end; + LFileSystem := FTPFileSystem; + if Assigned(FOnCRCFile) or Assigned(LFileSystem) then begin + LContext := TIdFTPServerContext(ASender.Context); + if LContext.IsAuthenticated(ASender) then begin + LBuf := ASender.UnparsedParams; + if Pos('"', LBuf) > 0 then begin {do not localize} + Fetch(LBuf, '"'); {do not localize} + LFileName := Fetch(LBuf, '"'); {do not localize} + end else begin + LFileName := Fetch(LBuf); + end; + if LFileName = '' then begin + CmdInvalidParamNum(ASender); + Exit; + end; + LBuf := Trim(LBuf); + if LBuf <> '' then begin + LBeginPos := IndyStrToStreamSize(Fetch(LBuf), -1); + if LBeginPos < 0 then begin + CmdInvalidParams(ASender); + Exit; + end; + LBuf := Trim(LBuf); + if LBuf <> '' then begin + LEndPos := IndyStrToStreamSize(Fetch(LBuf), -1); + if LEndPos < 0 then begin + CmdInvalidParams(ASender); + Exit; + end; + end else begin + LEndPos := -1; + end; + end else begin + LBeginPos := 0; + LEndPos := -1; + end; + LCalcStream := nil; + LFileName := DoProcessPath(LContext, LFileName); + DoOnCRCFile(LContext, LFileName, LCalcStream); + if Assigned(LCalcStream) then begin + if LEndPos = -1 then begin + LEndPos := LCalcStream.Size; + end; + try + LCalcStream.Position := 0; + LHashIdx := PosInStrArray(ASender.CommandHandler.Command, ['XCRC', 'XMD5', 'XSHA1','XSHA256','XSHA512'], False); {do not localize} + LCheckSum := CalculateCheckSum(HashTypes[LHashIdx], LCalcStream, LBeginPos, LEndPos); + ASender.Reply.SetReply(250, LCheckSum); + finally + FreeAndNil(LCalcStream); + end; + end else begin + CmdFileActionAborted(ASender); + end; + end; + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.DoOnFileExistCheck(AContext: TIdFTPServerContext; + const AFileName: String; var VExist: Boolean); +begin + if Assigned(FOnFileExistCheck) then begin + FOnFileExistCheck(AContext, AFileName, VExist); + end; +end; + +procedure TIdFTPServer.CommandSPSV(ASender: TIdCommand); +var + LIP : String; + LBPort : Word; + LIPVer : TIdIPVersion; +begin + //just to keep the compiler happy + LBPort := 0; + if InternalPASV(ASender, LIP, LBPort, LIPVer) then begin + ASender.Reply.SetReply(227, IntToStr(LBPort)); + end; +end; + +function TIdFTPServer.InternalPASV(ASender: TIdCommand; var VIP : String; + var VPort: TIdPort; var VIPVersion : TIdIPVersion): Boolean; +var + LContext : TIdFTPServerContext; + LBPortMin, LBPortMax: TIdPort; + LDataChannel: TIdSimpleServer; +begin + Result := False; + LContext := ASender.Context as TIdFTPServerContext; + if LContext.IsAuthenticated(ASender) then begin + if LContext.FEPSVAll then begin + ASender.Reply.SetReply(501, IndyFormat(RSFTPNotAllowedAfterEPSVAll, [ASender.CommandHandler.Command])); + Exit; + end; + + VIP := LContext.Connection.Socket.Binding.IP; + VIPVersion := LContext.Binding.IPVersion; + + if (FPASVBoundPortMin <> 0) and (FPASVBoundPortMax <> 0) then begin + LBPortMin := FPASVBoundPortMin; + LBPortMax := FPASVBoundPortMax; + end else begin + LBPortMin := FDefaultDataPort; + LBPortMax := LBPortMin; + end; + DoOnPASVBeforeBind(LContext, VIP, LBPortMin, LBPortMax, VIPVersion); + + LContext.CreateDataChannel(True); + LDataChannel := TIdSimpleServer(LContext.FDataChannel.FDataChannel); + LDataChannel.BoundIP := VIP; + if LBPortMin = LBPortMax then begin + LDataChannel.BoundPort := LBPortMin; + LDataChannel.BoundPortMin := 0; + LDataChannel.BoundPortMax := 0; + end else begin + LDataChannel.BoundPort := 0; + LDataChannel.BoundPortMin := LBPortMin; + LDataChannel.BoundPortMax := LBPortMax; + end; + LDataChannel.IPVersion := VIPVersion; + LDataChannel.BeginListen; + VIP := LDataChannel.Binding.IP; + VPort := LDataChannel.Binding.Port; + + LContext.FPASV := True; + LContext.FDataPortDenied := False; + Result := True; + end; +end; + +procedure TIdFTPServer.DoOnPASVBeforeBind(ASender: TIdFTPServerContext; + var VIP: String; var VPortMin, VPortMax: TIdPort; const AIPVersion: TIdIPVersion); +begin + if Assigned(FOnPASVBeforeBind) then begin + FOnPASVBeforeBind(ASender, VIP, VPortMin, VPortMax, AIPVersion); + end; +end; + +procedure TIdFTPServer.DoOnPASVReply(ASender: TIdFTPServerContext; + var VIP: String; var VPort: TIdPort; const AIPVersion: TIdIPVersion); +begin + if Assigned(FOnPASVReply) then begin + FOnPASVReply(ASender, VIP, VPort, AIPVersion); + end; +end; + +function TIdFTPServer.ReadCommandLine(AContext: TIdContext): string; +var + i : Integer; + State: TIdFTPTelnetState; + lb : Byte; + LContext: TIdFTPServerContext; + { Receive the line in 8-bit initially so that .NET can then + decode any UTF-8 data into a Unicode string afterwards if + needed } + LLine: TIdBytes; + LReply: TIdBytes; + Finished: Boolean; +begin + Result := ''; + LContext := AContext as TIdFTPServerContext; + //we do it this way in case there's no data. We don't want to stop + //a data channel operation if that's the case. + AContext.Connection.IOHandler.CheckForDataOnSource(1); + if AContext.Connection.IOHandler.InputBufferIsEmpty then begin + Exit; + end; + // + SetLength(LLine, 0); + SetLength(LReply, 0); + Finished := False; + + State := tsData; + repeat + lb := AContext.Connection.IOHandler.ReadByte; + case State of + tsData: + begin + case lb of + $FF: //is a command + begin + State := tsIAC; + end; + $0D: //wait for the next character to see what to do + begin + State := tsCheckCR; + end; + else + AppendByte(LLine, lb); + end; + end; + + tsCheckCR: + begin + case lb of + $0: // preserve CR + begin + AppendByte(LLine, $0D); + State := tsData; + end; + $0A: + begin + State := tsData; + Finished := True; + end; + $FF: //unexpected IAC, just in case + begin + AppendByte(LLine, $0D); + State := tsIAC; + end; + else + ExpandBytes(LLine, Length(LLine), 2); + LLine[Length(LLine)-2] := $0D; + LLine[Length(LLine)-1] := lb; + State := tsData; + end; + end; + + tsIAC: + begin + case lb of + $F1, //no-operation - do nothing + $F3: //break - do nothing for now + begin + State := tsData; + end; + $F4: //interrupt process - clear result and wait for data mark + begin + SetLength(LLine, 0); + State := tsInterrupt; + end; + $F5: //abort output + begin + // note - the DM needs to be sent as OOB "Urgent" data + + SetLength(LReply, 4); + + // TELNET_IP + LReply[0] := $FF; + LReply[1] := $F4; + + // TELNET_DM + LReply[2] := $FF; + LReply[3] := $F2; + + AContext.Connection.IOHandler.Write(LReply); + SetLength(LReply, 0); + + State := tsData; + end; + $F6: //are you there - do nothing for now + begin + State := tsData; + end; + $F7: //erase character + begin + i := Length(LLine); + if i > 0 then begin + SetLength(LLine, i-1); + end; + State := tsData; + end; + $F8 : //erase line + begin + SetLength(LLine, 0); + State := tsData; + end; + $F9 : //go ahead - do nothing for now + begin + State := tsData; + end; + $FA : //begin sub-negotiation + begin + State := tsNegotiate; + end; + $FB : //I will use + begin + State := tsWill; + end; + $FC : //you won't use + begin + State := tsWont; + end; + $FD : //please, you use option + begin + State := tsDo; + end; + $FE : //please, you stop option + begin + State := tsDont; + end; + $FF : //data $FF + begin + AppendByte(LLine, $FF); + State := tsData; + end; + else + // unknown command, ignore + State := tsData; + end; + end; + + tsWill: + begin + SetLength(LReply, 3); + + // TELNET_WONT + LReply[0] := $FF; + LReply[1] := $FC; + LReply[2] := lb; + + AContext.Connection.IOHandler.Write(LReply); + SetLength(LReply, 0); + + State := tsData; + end; + + tsDo: + begin + SetLength(LReply, 3); + + // TELNET_DONT + LReply[0] := $FF; + LReply[1] := $FE; + LReply[2] := lb; + + AContext.Connection.IOHandler.Write(LReply); + SetLength(LReply, 0); + + State := tsData; + end; + + tsWont, + tsDont: + begin + State := tsData; + end; + + tsNegotiate: + begin + State := tsNegotiateData; + end; + + tsNegotiateData: + begin + case lb of + $FF: //is a command? + begin + State := tsNegotiateIAC; + end; + end; + end; + + tsNegotiateIAC: + begin + case lb of + $F0: //end sub-negotiation + begin + State := tsData; + end; + else + State := tsNegotiateData; + end; + end; + + tsInterrupt: + begin + case lb of + $FF: //is a command? + begin + State := tsInterruptIAC; + end; + end; + end; + + tsInterruptIAC: + begin + case lb of + $F2: //data mark + begin + State := tsData; + end; + end; + end; + + else + State := tsData; + end; + + until Finished or (not AContext.Connection.IOHandler.Connected); + + //The last char was #13, we have to make sure that we remove a trailing + //#10 if it exists so that it doesn't appear in the next line. + if (lb = $0D) and (State = tsData) then + begin + i := AContext.Connection.IOHandler.InputBuffer.Size; + if i > 0 then begin + lb := AContext.Connection.IOHandler.InputBuffer.PeekByte(i - 1); + if lb = $0A then begin + AContext.Connection.IOHandler.ReadByte; + end; + end; + end; + + Result := BytesToString(LLine, 0, MaxInt, LContext.Connection.IOHandler.DefStringEncoding); +end; + +procedure TIdFTPServer.DoReplyUnknownCommand(AContext: TIdContext; ALine: string); +begin + CmdSyntaxError(AContext, ALine); +end; + +procedure TIdFTPServer.DoTerminateContext(AContext: TIdContext); +begin + try + TIdFTPServerContext(AContext).KillDataChannel; + finally + inherited DoTerminateContext(AContext); + end; +end; + +procedure TIdFTPServer.CmdSyntaxError(AContext: TIdContext; ALine: string; const AReply : TIdReply = nil); +var + LTmp : String; + LReply : TIdReply; +begin + //First make the first word upper-case + LTmp := UpCaseFirstWord(ALine); + try + if Assigned(AReply) then begin + LReply := AReply; + end else begin + LReply := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts); + LReply.Assign(ReplyUnknownCommand); + end; + LReply.Text.Clear; + LReply.Text.Add(IndyFormat(RSFTPCmdNotRecognized, [LTmp])); + AContext.Connection.IOHandler.Write(LReply.FormattedReply); + finally + if not Assigned(AReply) then begin + FreeAndNil(LReply); + end; + end; +end; + +procedure TIdFTPServer.CmdSyntaxError(ASender: TIdCommand); +begin + CmdSyntaxError(ASender.Context, ASender.RawLine, FReplyUnknownCommand ); + ASender.PerformReply := False; +end; + +procedure TIdFTPServer.CommandSecRFC(ASender: TIdCommand); +//stub for RFC 2228 commands that we don't implement as +//part of the SSL framework. +begin + if IOHandler is TIdServerIOHandlerSSLBase then begin + CmdNotImplemented(ASender); + end else begin + CmdSyntaxError(ASender); + end; +end; + +procedure TIdFTPServer.CommandOptsMLST(ASender: TIdCommand); +const + LVALIDOPTS : array [0..12] of string = + ('type', 'size', 'modify', + 'UNIX.mode', 'UNIX.owner', 'UNIX.group', + 'unique', 'perm', 'create', + 'windows.lastaccesstime','win32.ea','win32.dt','win32.dl'); {Do not localize} +var + s: string; + LContext : TIdFTPServerContext; + + function ParseMLSParms(ASvr : TIdFTPServer; const AParms : String) : TIdFTPFactOutputs; + var + Ls : String; + begin + Result := []; + Ls := UpperCase(AParms); + while Ls <> '' do begin + case PosInStrArray(Fetch(Ls,';'), LVALIDOPTS, False) of + 0 : Result := Result + [ItemType]; //type + 1 : Result := Result + [Size]; //size + 2 : Result := Result + [Modify]; //modify + 3 : if mlsdUnixModes in ASvr.FMLSDFacts then begin + Result := Result + [UnixMODE]; //UnixMode + end; + 4 : if mlsdUnixOwner in ASvr.FMLSDFacts then begin + Result := Result + [UnixOwner]; //UNIX.owner + end; + 5 : if mlsdUnixGroup in ASvr.FMLSDFacts then begin + Result := Result + [UnixGroup]; //UNIX.group + end; + 6 : if mlsdUniqueID in ASvr.FMLSDFacts then begin //Unique + Result := Result + [Unique]; + end; + 7 : if mlsdPerms in ASvr.FMLSDFacts then begin //perm + Result := Result + [Perm]; + end; + 8 : if mlsdFileCreationTime in ASvr.FMLSDFacts then begin + Result := Result + [CreateTime]; + end; + 9 : if mlsdFileLastAccessTime in ASvr.FMLSDFacts then begin + Result := Result + [LastAccessTime]; + end; + 10 : if mlsdWin32Attributes in ASvr.FMLSDFacts then begin + Result := Result + [WinAttribs]; + end; + 11 : if mlsdWin32DriveType in ASvr.MLSDFacts then begin + Result := Result + [WinDriveType]; + end; + 12 : if mlstWin32DriveLabel in ASvr.MLSDFacts then begin + Result := Result + [WinDriveLabel]; + end; + end; + end; + end; + + function SetToOptsStr(AFacts : TIdFTPFactOutputs) : String; + begin + Result := ''; + if Size in AFacts then begin {Do not translate} + Result := Result + 'size;'; {Do not localize} + end; + if ItemType in AFacts then begin {Do not translate} + Result := Result + 'type;'; {Do not translate} + end; + if Perm in AFacts then begin {Do not translate} + Result := Result + 'perm;'; {Do not translate} + end; + if CreateTime in AFacts then begin {Do not translate} + Result := Result + 'create;'; {Do not translate} + end; + if Modify in AFacts then begin + Result := Result + 'modify;'; {Do not translate} + end; + if UnixMODE in AFacts then begin {Do not translate} + Result := Result + 'UNIX.mode;'; {Do not translate} + end; + if UnixOwner in AFacts then begin{Do not translate} + Result := Result + 'UNIX.owner;'; {Do not translate} + end; + if UnixGroup in AFacts then begin {Do not translate} + Result := Result + 'UNIX.group;'; {Do not translate} + end; + if Unique in AFacts then begin {Do not translate} + Result := Result + 'unique;'; {Do not translate} + end; + if LastAccessTime in AFacts then begin + Result := Result + 'windows.lastaccesstime;'; {Do not translate} + end; + if IdFTPListOutput.WinAttribs in AFacts then begin + Result := Result + 'win32.ea;'; {Do not translate} + end; + if IdFTPListOutput.WinDriveType in AFacts then begin + Result := Result + 'Win32.dt;'; {Do not localize} + end; + if IdFTPListOutput.WinDriveLabel in AFacts then begin + Result := Result + 'Win32.dl;'; {Do not localize} + end; + end; + +begin + LContext := ASender.Context as TIdFTPServerContext; + s := ASender.UnparsedParams; + if IndyPos(' ', s) = 0 then begin + LContext.MLSOpts := ParseMLSParms(Self, Trim(s)); + //the string is standardized format + ASender.Reply.SetReply(200, Trim(IndyFormat('MLST OPTS %s', [SetToOptsStr(LContext.MLSOpts)]))); {Do not Localize} + end else begin + ASender.Reply.SetReply(501, IndyFormat(RSFTPInvalidOps, ['MLST'])); {Do not Localize} + end; +end; + +procedure TIdFTPServer.CommandOptsMODEZ(ASender: TIdCommand); +const + OPT_NAMES : Array[0..4] of String = + ('ENGINE','LEVEL','METHOD','BLOCKSIZE','EXTRA'); {do not localize} +var + s: string; + LOptName, LOptVal : String; + LContext : TIdFTPServerContext; + LFirstPar : Boolean; + LError : Boolean; + LNoVal : Integer; + LReset : Boolean; + + procedure ReportSettings(ACxt : TIdFTPServerContext; AReply : TIdReply); + begin + AReply.NumericCode := 200; + AReply.Text.Clear; + AReply.Text.Add('MODE Z ENGINE set to ZLIB.'); {do not localize} + AReply.Text.Add('MODE Z LEVEL set to ' + IntToStr(ACxt.FZLibCompressionLevel) + '.'); {do not localize} + AReply.Text.Add('MODE Z METHOD set to ' + IntToStr(DEF_ZLIB_METHOD) + '.'); {do not localize} + end; + + procedure SyntaxError(AReply : TIdCommand); + var + LOpts : String; + begin + //drop the OPTS part of the command for display + LOpts := ASender.RawLine; + Fetch(LOpts); + LOpts := TrimLeft(LOpts); + ASender.Reply.SetReply(501, IndyFormat(RSFTPInvalidOps, [LOpts])); + end; + +begin + LFirstPar := True; + LReset := True; + LError := True; + LContext := ASender.Context as TIdFTPServerContext; + s := Trim(ASender.UnparsedParams); + if s = '' then begin + LContext.ResetZLibSettings; + ReportSettings(LContext, ASender.Reply); + end; + repeat + LOptName := Fetch(s); + if s = '' then begin + if LFirstPar then begin + SyntaxError(ASender); + Exit; + end; + end; + LOptVal := Fetch(s); + if Trim(s) <> '' then begin + //if there's more, than we see if there's a valid option. + LFirstPar := False; + end; + if LFirstPar and (PosInStrArray(LOptName, OPT_NAMES, False) = -1) then begin + SyntaxError(ASender); + Exit; + end; + LFirstPar := False; + case PosInStrArray(LOptName, OPT_NAMES, False) of + 0 : //'ENGINE' - we only support ZLIB + begin + LError := False; + end; + 1 : begin //,'LEVEL', - implemented + LNoVal := IndyStrToInt(LOptVal, -1); + if (LNoVal > -1) and (LNoVal < 8) then begin + LContext.FZLibCompressionLevel := LNoVal; + LReset := False; + LError := False; + end; + end; + 2 : begin //'METHOD', - not implemented - jst do syntax check + LNoVal := IndyStrToInt(LOptVal, -1); + if LNoVal <> -1 then begin + LError := False; + end; + end; + 3 : begin ///'BLOCKSIZE', -not implemented - just do syntax check + LNoVal := IndyStrToInt(LOptVal, -1); + if LNoVal <> -1 then begin + LError := False; + end; + end; + 4 : begin //'EXTRA') - not implemented - just do syntax check + if PosInStrArray(LOptVal, OnOffStates, False) > -1 then begin + LError := False; + end; + end; + end; + until (s = ''); + + if LError then begin + SyntaxError(ASender); + Exit; + end; + if LReset then begin + LContext.ResetZLibSettings; + end; + ReportSettings(LContext, ASender.Reply); +end; + +procedure TIdFTPServer.CommandOptsUTF8(ASender: TIdCommand); +var + s: String; + LContext: TIdFTPServerContext; + + procedure SyntaxError(AReply : TIdCommand); + var + LOpts : String; + begin + //drop the OPTS part of the command for display + LOpts := ASender.RawLine; + Fetch(LOpts); + LOpts := TrimLeft(LOpts); + ASender.Reply.SetReply(501, IndyFormat(RSFTPInvalidOps, [LOpts])); + end; + +begin + LContext := ASender.Context as TIdFTPServerContext; + s := Trim(ASender.UnparsedParams); + + if TextIsSame(ASender.CommandHandler.Command, 'UTF-8') then begin + // OPTS UTF-8 + // http://www.ietf.org/proceedings/02nov/I-D/draft-ietf-ftpext-utf-8-option-00.txt + if s = '' then begin + LContext.NLSTUtf8 := False; // disable UTF-8 over data connection + end + else if TextIsSame(s, 'NLST') then begin + LContext.NLSTUtf8 := True; // enable UTF-8 over data connection + end else begin + SyntaxError(ASender); + Exit; + end; + // enable UTF-8 over control connection + LContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; + end else begin + // OPTS UTF8 + // non-standard Microsoft IE implementation!!!! + case PosInStrArray(s, OnOffStates, False) of + 0: begin // 'ON' + LContext.NLSTUtf8 := True; + LContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; + end; + 1: begin // 'OFF' + LContext.NLSTUtf8 := False; + LContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; + end; + else + begin + SyntaxError(ASender); + Exit; + end; + end; + end; + ASender.Reply.NumericCode := 200; +end; + +function TIdFTPServer.IgnoreLastPathDelim(const APath: String): String; +//This internal function is needed because path processing is different in Windows +//than in Linux. The path separators on a FTP server on either system will be different. +// +//On Windows machines, both '/' and '\' +// +//On a Linux machine, a FTP server would probably only use '/' because '\' is a valid +//filename char. +var + i : Integer; + LPathProcessing : TIdFTPPathProcessing; +begin + Result := APath; + i := Length(Result); + if FPathProcessing <> ftpOSDependent then begin + LPathProcessing := FPathProcessing; + end else begin + case GOSType of + otUnix : + begin + LPathProcessing := ftppUnix; + end; + otUnknown : + begin + LPathProcessing := ftppCustom; + end + else + LPathProcessing := ftppDOS; + end; + end; + case LPathProcessing of + ftppDOS : + begin + if Result <>'' then begin + if CharIsInSet(Result, i, '/\') then begin + IdDelete(Result, i, 1); + end; + end; + end; + ftppUnix : + begin + if Result <>'' then begin + if TextEndsWith(Result, '/') then begin + IdDelete(Result, i, 1); + end; + end; + end; + ftppCustom : + begin + Exit; + end; + end; + //Done so that something like "cd /" or "cd \" will go to + //the main directory + if Result = '' then begin + Result := '/'; + end; +end; + +function TIdFTPServer.SupportTaDirSwitches(AContext : TIdFTPServerContext): Boolean; +begin + Result := True; + case FDirFormat of + ftpdfCustom, ftpdfEPLF: + Result := False; + ftpdfDOS: + Result := not AContext.FMSDOSMode; + ftpdfOSDependent: + if (GOSType = otWindows) or (GOSType = otDotNET) then begin + Result := not AContext.FMSDOSMode; + end; + end; +end; + +{ TIdFTPSecurityOptions } + +procedure TIdFTPSecurityOptions.Assign(Source: TPersistent); +var + LSrc : TIdFTPSecurityOptions; +begin + if Source is TIdFTPSecurityOptions then begin + LSrc := Source as TIdFTPSecurityOptions; + + BlockAllPORTTransfers := LSrc.BlockAllPORTTransfers; + DisableSTATCommand := LSrc.DisableSTATCommand; + DisableSYSTCommand := LSrc.DisableSYSTCommand; + PasswordAttempts := LSrc.PasswordAttempts; + InvalidPassDelay := LSrc.InvalidPassDelay; + NoReservedRangePORT := LSrc.NoReservedRangePORT; + RequirePASVFromSameIP := LSrc.RequirePASVFromSameIP; + RequirePORTFromSameIP := LSrc.RequirePORTFromSameIP; + PermitCCC := LSrc.PermitCCC; + end else begin + inherited Assign(Source); + end; +end; + +constructor TIdFTPSecurityOptions.Create; +begin + inherited Create; + //limit login attempts - some hackers will try guessing passwords from a dictionary + PasswordAttempts := DEF_FTP_PASSWORDATTEMPTS; + //should slow-down a password guessing attack - note those dictionaries + InvalidPassDelay := DEF_FTP_INVALIDPASS_DELAY; + //client IP Address is the only one that we will accept a PASV + //transfer from + //http://cr.yp.to/ftp/security.html + RequirePASVFromSameIP := DEF_FTP_PASV_SAME_IP; + //Accept port transfers from the same IP address as the client - + //should prevent bounce attacks + RequirePORTFromSameIP := DEF_FTP_PORT_SAME_IP; + //Do not accept port requests to ports in the reserved range. That is dangerous on some systems + NoReservedRangePORT := DEF_FTP_NO_RESERVED_PORTS; + //Do not accept any PORT transfers at all. This is a little extreme but reduces troubles further. + //This will break the the Win32 console clients and a number of other programs. + BlockAllPORTTransfers := DEF_FTP_BLOCK_ALL_PORTS; + //Disable SYST command. SYST usually gives the system description. + //Disabling it may make it harder for a trouble maker to know about your computer + //but will not be a complete security solution. See http://www.sans.org/rr/infowar/fingerprint.php for details + //On the other hand, disabling it will break RFC 959 complience and may break some FTP programs. + DisableSYSTCommand := DEF_FTP_DISABLE_SYST; + //Disable STAT command. STAT gives freeform information about the connection status. + // http://www.sans.org/rr/infowar/fingerprint.php advises administrators to disable this + //because servers tend to give distinct patterns of information and some trouble makers + //can figure out what type of server you are running simply with this. + DisableSTATCommand := DEF_FTP_DISABLE_STAT; + //Permit CCC command when using TLS with FTP to clear the control connection. + //That may be helpful for someone behind a NAT where an IP address can NOT be altered by the NAT + //when using SSL. On the other hand, some administrators may NOT permit this for security reasons. + //That's a debate I'll leave up to the programmer in hopes that they will pass it to the user. + PermitCCC := DEF_FTP_PERMIT_CCC; +end; + +{ TIdDataChannel } + +constructor TIdDataChannel.Create(APASV: Boolean; AControlContext: TIdFTPServerContext; + const ARequirePASVFromSameIP: Boolean; AServer: TIdFTPServer); +var + LIO: TIdIOHandlerSocket; + LDataChannelSvr: TIdSimpleServer; + LDataChannelCli: TIdTCPClient; +begin + inherited Create; + FNegotiateTLS := False; + FOKReply := TIdReplyRFC.Create(nil); + FErrorReply := TIdReplyRFC.Create(nil); + FReply := TIdReplyRFC.Create(nil); + FRequirePASVFromSameIP := ARequirePASVFromSameIP; + FControlContext := AControlContext; + FServer := AServer; + + // RLebeau: do not set both BoundPortMin/Max and BoundPort at the same time. + // If they are all non-zero, BoundPort will take priority in TIdSocketHandle. + // The DefaultDataPort property should not be assigned to zero in order to + // support Active-mode transfers, but doing so will cause BoundPortMin/Max + // to be ignored for Passive-mode transfers. So assign them in an either-or + // manner. + + if APASV then begin + FDataChannel := TIdSimpleServer.Create(nil); + LDataChannelSvr := TIdSimpleServer(FDataChannel); + LDataChannelSvr.BoundIP := FControlContext.Connection.Socket.Binding.IP; + if (AServer.PASVBoundPortMin <> 0) and (AServer.PASVBoundPortMax <> 0) then begin + LDataChannelSvr.BoundPortMin := AServer.PASVBoundPortMin; + LDataChannelSvr.BoundPortMax := AServer.PASVBoundPortMax; + end else begin + LDataChannelSvr.BoundPort := AServer.DefaultDataPort; + end; + LDataChannelSvr.IPVersion := FControlContext.Binding.IPVersion; + LDataChannelSvr.OnBeforeBind := AControlContext.PortOnBeforeBind; + LDataChannelSvr.OnAfterBind := AControlContext.PortOnAfterBind; + end else begin + FDataChannel := TIdTCPClient.Create(nil); + //the TCPClient for the dataport must be bound to a default port + LDataChannelCli := TIdTCPClient(FDataChannel); + LDataChannelCli.BoundIP := FControlContext.Connection.Socket.Binding.IP; + LDataChannelCli.BoundPort := AServer.DefaultDataPort; + LDataChannelCli.IPVersion := FControlContext.Binding.IPVersion; + end; + + if AControlContext.Server.IOHandler is TIdServerIOHandlerSSLBase then begin + if APASV then begin + LIO := TIdServerIOHandlerSSLBase(AServer.IOHandler).MakeFTPSvrPasv; + end else begin + LIO := TIdServerIOHandlerSSLBase(AServer.IOHandler).MakeFTPSvrPort; + end; + (LIO as TIdSSLIOHandlerSocketBase).PassThrough := True; + // always uses a ssl iohandler, but passthrough is true... + end else begin + LIO := FServer.IOHandler.MakeClientIOHandler(nil) as TIdIOHandlerSocket; + end; + + LIO.OnBeforeBind := AControlContext.PortOnBeforeBind; + LIO.OnAfterBind := AControlContext.PortOnAfterBind; + FDataChannel.IOHandler := LIO; + + if LIO is TIdSSLIOHandlerSocketBase then begin + case AControlContext.DataProtection of + ftpdpsClear: begin + TIdSSLIOHandlerSocketBase(LIO).PassThrough := True; + end; + ftpdpsPrivate: begin + FNegotiateTLS := True; + end; + end; + end; +end; + +destructor TIdDataChannel.Destroy; +begin + FreeAndNil(FOKReply); + FreeAndNil(FErrorReply); + FreeAndNil(FReply); + {$IFNDEF USE_OBJECT_ARC} + FDataChannel.IOHandler.Free; + {$ENDIF} + FDataChannel.IOHandler := nil; + FreeAndNil(FDataChannel); + inherited Destroy; +end; + +function TIdDataChannel.GetPeerIP: String; +begin + Result := ''; + if Assigned(FDataChannel) then begin + if Assigned(FDataChannel.Socket) then begin + if Assigned(FDataChannel.Socket.Binding) then begin + Result := FDataChannel.Socket.Binding.PeerIP; + end; + end; + end; +end; + +function TIdDataChannel.GetPeerPort: TIdPort; +begin + Result := 0; + if Assigned(FDataChannel) then begin + if Assigned(FDataChannel.Socket) then begin + if Assigned(FDataChannel.Socket.Binding) then begin + Result := FDataChannel.Socket.Binding.PeerPort; + end; + end; + end; +end; + +function TIdDataChannel.GetLocalIP: String; +begin + Result := ''; + if Assigned(FDataChannel) then begin + if Assigned(FDataChannel.Socket) then begin + if Assigned(FDataChannel.Socket.Binding) then begin + Result := FDataChannel.Socket.Binding.IP; + end; + end; + end; +end; + +function TIdDataChannel.GetLocalPort: TIdPort; +begin + Result := 0; + if Assigned(FDataChannel) then begin + if Assigned(FDataChannel.Socket) then begin + if Assigned(FDataChannel.Socket.Binding) then begin + Result := FDataChannel.Socket.Binding.Port; + end; + end; + end; +end; + +procedure TIdDataChannel.InitOperation(const AConnectMode : Boolean = False); +var + LIO : TIdSSLIOHandlerSocketBase; +begin + try + if FDataChannel is TIdSimpleServer then begin + TIdSimpleServer(FDataChannel).Listen; + if FRequirePASVFromSameIP then begin + {//BGO} + if FControlContext.Binding.PeerIP <> TIdSimpleServer(FDataChannel).Binding.PeerIP then begin + TIdFTPServerContext(FControlContext).FDataPortDenied := True; + ErrorReply.SetReply(504, RSFTPSameIPAddress); + FControlContext.Connection.IOHandler.Write(ErrorReply.FormattedReply); + TIdSimpleServer(FDataChannel).Disconnect(False); + Exit; + end; + end; + {//BGO} + if FNegotiateTLS then begin + LIO := FDataChannel.IOHandler as TIdSSLIOHandlerSocketBase; + if AConnectMode then begin + LIO.IsPeer := False; + end; + LIO.Passthrough := False; + end; + end + else if FDataChannel is TIdTCPClient then begin + TIdTCPClient(FDataChannel).Connect; + if FNegotiateTLS then begin + LIO := FDataChannel.IOHandler as TIdSSLIOHandlerSocketBase; + if AConnectMode then begin + LIO.IsPeer := False; + end; + LIO.Passthrough := False; + end; + end; + except + FControlContext.Connection.IOHandler.Write(FErrorReply.FormattedReply); //426 + raise; + end; +end; + +procedure TIdDataChannel.SetErrorReply(const AValue: TIdReplyRFC); +begin + FErrorReply.Assign(AValue); +end; + +procedure TIdDataChannel.SetOKReply(const AValue: TIdReplyRFC); +begin + FOKReply.Assign(AValue); +end; + +procedure TIdFTPServerContext.PortOnAfterBind(ASender: TObject); +begin + FServer.DoOnDataPortAfterBind(Self); +end; + +procedure TIdFTPServerContext.PortOnBeforeBind(ASender: TObject); +begin + FServer.DoOnDataPortBeforeBind(Self); +end; + +procedure TIdFTPServerContext.ResetZLibSettings; +begin + //Settings specified by + // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt + FZLibCompressionLevel := DEF_ZLIB_COMP_LEVEL; + FZLibWindowBits := DEF_ZLIB_WINDOW_BITS; //-15 - no extra headers + FZLibMemLevel := DEF_ZLIB_MEM_LEVEL; + FZLibStratagy := DEF_ZLIB_STRATAGY; // - default +end; + +end. diff --git a/indy/Protocols/IdFTPServerContextBase.pas b/indy/Protocols/IdFTPServerContextBase.pas new file mode 100644 index 0000000..004872a --- /dev/null +++ b/indy/Protocols/IdFTPServerContextBase.pas @@ -0,0 +1,97 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 8/24/2003 06:47:42 PM JPMugaas + FTPContext base class so that the ThreadClass may be shared with the + FileSystem classes. +} + +unit IdFTPServerContextBase; + +{ + This is for a basic thread class that can be shared with the FTP File System + component and any other file system class so they can share more information + than just the Username +} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdCustomTCPServer, IdFTPList; + +type + TIdFTPUserType = (utNone, utAnonymousUser, utNormalUser); + + TIdFTPServerContextBase = class(TIdServerContext) + protected + FUserType: TIdFTPUserType; + FAuthenticated: Boolean; + FALLOSize: Integer; + FCurrentDir: TIdFTPFileName; + FHomeDir: TIdFTPFileName; + FHost : String; + FUsername: string; + FPassword: string; + FAccount : String; + FAccountNeeded : Boolean; + FRESTPos: Integer; + FRNFR: string; + FNLSTUtf8: Boolean; + procedure ReInitialize; virtual; + public + property Authenticated: Boolean read FAuthenticated write FAuthenticated; + property ALLOSize: Integer read FALLOSize write FALLOSize; + property CurrentDir: TIdFTPFileName read FCurrentDir write FCurrentDir; + property HomeDir: TIdFTPFileName read FHomeDir write FHomeDir; + property Password: string read FPassword write FPassword; + property Username: string read FUsername write FUsername; + property Account : String read FAccount write FAccount; + property AccountNeeded : Boolean read FAccountNeeded write FAccountNeeded; + + //for virtual domains + property Host: string read FHost write FHost; + property UserType: TIdFTPUserType read FUserType write FUserType; + property RESTPos: Integer read FRESTPos write FRESTPos; + property RNFR: string read FRNFR write FRNFR; + property NLSTUtf8: Boolean read FNLSTUtf8 write FNLSTUtf8; + end; + +implementation + +{ TIdFTPServerContextBase } + +procedure TIdFTPServerContextBase.ReInitialize; +begin + UserType := utNone; + FAuthenticated := False; + FALLOSize := 0; + FCurrentDir := '/'; {Do not Localize} + FHomeDir := ''; {Do not Localize} + FHost := ''; {Do not localize} + FUsername := ''; {Do not Localize} + FPassword := ''; {Do not Localize} + FAccount := ''; {Do not localize} + FRESTPos := 0; + FRNFR := ''; {Do not Localize} + FNLSTUtf8 := False; + FAccountNeeded := False; +end; + +end. diff --git a/indy/Protocols/IdFinger.pas b/indy/Protocols/IdFinger.pas new file mode 100644 index 0000000..bf8242e --- /dev/null +++ b/indy/Protocols/IdFinger.pas @@ -0,0 +1,152 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 2004.02.03 5:45:10 PM czhower + Name changes + + Rev 1.5 1/21/2004 2:29:38 PM JPMugaas + InitComponent + + Rev 1.4 2/24/2003 08:41:20 PM JPMugaas + Should compile with new code. + + Rev 1.3 12/8/2002 07:58:54 PM JPMugaas + Now compiles properly. + + Rev 1.2 12/8/2002 07:26:38 PM JPMugaas + Added published host and port properties. + + Rev 1.1 12/6/2002 05:29:34 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.0 11/14/2002 02:19:50 PM JPMugaas + + 2000-April-30 J. Peter Mugaas + -adjusted CompleteQuery to permit recursive finger queries such + as "test@test.com@example.com". I had mistakenly assumed that + everything after the first @ was the host name. + -Added option for verbose output request from server - note that + many do not support this. +} + +unit IdFinger; + +{*******************************************************} +{ } +{ Indy Finger Client TIdFinger } +{ } +{ Copyright (C) 2000 Winshoes Working Group } +{ Original author J. Peter Mugaas } +{ 2000-April-23 } +{ Based on RFC 1288 } +{ } +{*******************************************************} + +interface + +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdTCPClient; + +type + TIdFinger = class(TIdTCPClientCustom) + protected + FQuery: String; + FVerboseOutput: Boolean; + Procedure SetCompleteQuery(AQuery: String); + Function GetCompleteQuery: String; + Procedure InitComponent; override; + public + {This connects to a server, does the finger querry specified in the Query + property and returns the results of the querry} + function Finger: String; + published + property Port default IdPORT_FINGER; + property Host; + {This is the querry to the server which you set with the Host Property} + Property Query: String read FQuery write FQuery; + {This is the complete querry such as "user@host"} + Property CompleteQuery: String read GetCompleteQuery write SetCompleteQuery; + {This indicates that the server should give more detailed information on + some systems. However, this will probably not work on many systems so it is + False by default} + Property VerboseOutput: Boolean read FVerboseOutPut write FVerboseOutPut + default False; + end; + +implementation + +uses + IdGlobal, IdGlobalProtocols, + IdTCPConnection; + +{ TIdFinger } + +procedure TIdFinger.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_FINGER; +end; + +{This is the method used for retreiving Finger Data which is returned in the + result} +function TIdFinger.Finger: String; +var + QStr : String; +begin + QStr := FQuery; + if VerboseOutPut then + begin + QStr := QStr + '/W'; {Do not Localize} + end; //if VerboseOutPut then + Connect; + try + {Write querry} + Result := ''; {Do not Localize} + IOHandler.WriteLn(QStr); + {Read results} + Result := IOHandler.AllData; + finally + Disconnect; + end; +end; + +function TIdFinger.GetCompleteQuery: String; +begin + Result := FQuery + '@' + Host; {Do not Localize} +end; + +procedure TIdFinger.SetCompleteQuery(AQuery: String); +var + p : Integer; +begin + p := RPos('@', AQuery, -1); {Do not Localize} + if p <> 0 then begin + if p < Length(AQuery) then + begin + Host := Copy(AQuery, p+1, MaxInt); + end; + FQuery := Copy(AQuery, 1, p-1); + end else begin + FQuery := AQuery; + end; +end; + +end. diff --git a/indy/Protocols/IdFingerServer.pas b/indy/Protocols/IdFingerServer.pas new file mode 100644 index 0000000..f9fe722 --- /dev/null +++ b/indy/Protocols/IdFingerServer.pas @@ -0,0 +1,121 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 12/2/2004 4:23:54 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.5 1/21/2004 2:29:40 PM JPMugaas + InitComponent + + Rev 1.4 2/24/2003 08:41:24 PM JPMugaas + Should compile with new code. + + Rev 1.3 1/17/2003 05:35:02 PM JPMugaas + Now compiles with new design. + + Rev 1.2 1/9/2003 07:10:56 AM JPMugaas + Changed Finger server API so developers do not have to mess with the Context + and Connnection objects. + + Rev 1.1 1-1-2003 20:13:02 BGooijen + Changed to support the new TIdContext class + + Rev 1.0 11/14/2002 02:19:56 PM JPMugaas + + 2000-May-15 J. Peter Mugaas + -Added verbose querry event to complement TIdFinger + + 2000-Apr-22 J Peter Mugass + -Ported to Indy + + 2000-Jan-13 MTL + -Moved to new Palette Scheme (Winshoes Servers) + + 1999-Apr-13 + -Final Version +} + + +unit IdFingerServer; + +{ + Original Author: Ozz Nixon +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Type + TIdFingerGetEvent = procedure (AContext:TIdContext; const AUserName: String; var VResponse : String) of object; + + TIdFingerServer = class ( TIdCustomTCPServer ) + protected + FOnCommandFinger : TIdFingerGetEvent; + FOnCommandVerboseFinger : TIdFingerGetEvent; + // + function DoExecute(AContext:TIdContext): boolean; override; + procedure InitComponent; override; + published + {This event fires when you make a regular querry} + property OnCommandFinger: TIdFingerGetEvent read FOnCommandFinger + write FOnCommandFinger; + { This event fires when you receive a VERBOSE finger request} + property OnCommandVerboseFinger : TIdFingerGetEvent + read FOnCommandVerboseFinger write FOnCommandVerboseFinger; + property DefaultPort default IDPORT_Finger; + end; + +implementation + +uses + IdGlobal, SysUtils; + +procedure TIdFingerServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_FINGER; +end; + +function TIdFingerServer.DoExecute(AContext:TIdContext): boolean; +Var + s, LResponse: String; +begin + Result := True; + {We use TrimRight in case there are spaces ending the query which are problematic + for verbose queries. CyberKit puts a space after the /W parameter} + s := TrimRight(AContext.Connection.IOHandler.ReadLn); + if Assigned(FOnCommandVerboseFinger) and TextEndsWith(s, '/W') then {Do not Localize} + begin + {we remove the /W switch before calling the event} + s := Copy(s, 1, Length(s)-2); + OnCommandVerboseFinger(AContext, s, LResponse); + AContext.Connection.IOHandler.Write(LResponse); + end + else if Assigned(OnCommandFinger) then begin + OnCommandFinger(AContext, s, LResponse); + AContext.Connection.IOHandler.Write(LResponse); + end; + AContext.Connection.Disconnect; +end; + +end. diff --git a/indy/Protocols/IdGlobalProtocols.pas b/indy/Protocols/IdGlobalProtocols.pas new file mode 100644 index 0000000..089a8f7 --- /dev/null +++ b/indy/Protocols/IdGlobalProtocols.pas @@ -0,0 +1,4971 @@ +{ + $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" + {$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 Avance de l'Atlantique - North America {do not localize} + (TimeZone:'HAC'; Offset:'-0500'), // Heure Avance 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 Avance de l'Est - North America {do not localize} + (TimeZone:'HAP'; Offset:'-0700'), // Heure Avance du Pacifique - North America {do not localize} + (TimeZone:'HAR'; Offset:'-0600'), // Heure Avance 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 Avance de Terre-Neuve - North America {do not localize} + (TimeZone:'HAY'; Offset:'-0800'), // Heure Avance 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'), // Mitteleuropische Hochsommerzeit - Europe {do not localize} + (TimeZone:'MESZ'; Offset:'+0200'), // Mitteleuroische Sommerzeit - Europe {do not localize} + (TimeZone:'MEZ'; Offset:'+0100'), // Mitteleuropische 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: + // + 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); + // '' + // '' (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) + 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. diff --git a/indy/Protocols/IdGopher.pas b/indy/Protocols/IdGopher.pas new file mode 100644 index 0000000..c2f7f97 --- /dev/null +++ b/indy/Protocols/IdGopher.pas @@ -0,0 +1,724 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.17 3/4/2005 10:34:34 PM JPMugaas + Fix for compiler warnings and removed duplicate code. + + Rev 1.16 2004.10.27 9:17:52 AM czhower + For TIdStrings + + Rev 1.15 10/26/2004 10:10:58 PM JPMugaas + Updated refs. + + Rev 1.14 2004.05.20 11:37:06 AM czhower + IdStreamVCL + + Rev 1.13 2004.02.03 5:44:46 PM czhower + Name changes + + Rev 1.12 1/21/2004 3:26:42 PM JPMugaas + InitComponent + + Rev 1.11 10/24/2003 03:26:18 PM JPMugaas + Attempted to restore functionality after Kudzu's "surgery" + + Rev 1.10 2003.10.24 10:43:06 AM czhower + TIdSTream to dos + + Rev 1.9 10/21/2003 8:47:44 PM BGooijen + Fixed WriteLn and ReadLn namespaces + + Rev 1.7 10/19/2003 6:00:04 PM BGooijen + Did Todo + + Rev 1.6 2003.10.12 3:50:42 PM czhower + Compile todos + + Rev 1.5 6/5/2003 04:54:12 AM JPMugaas + Reworkings and minor changes for new Reply exception framework. + + Rev 1.4 2/24/2003 08:50:58 PM JPMugaas + + Rev 1.3 12/8/2002 07:26:22 PM JPMugaas + Added published host and port properties. + + Rev 1.2 12/6/2002 05:29:46 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.1 12/6/2002 04:35:04 PM JPMugaas + Now compiles with new code. + + Rev 1.0 11/13/2002 08:29:48 AM JPMugaas + Initial import from FTP VC. + + 2000-June- 9 J. Peter Mugaas + -adjusted the Gopher+ support so that line-unfolding is disabled in + FGopherBlock. Many headers we use start with spaces + -made the ASK block into a TIdHeaderList to facilitate use better. This does + unfold lines + + 2000-May -24 J. Peter Mugaas + -changed interface of file retrieval routines to so DestStream property does + not have to even exist now. + + 2000-May -17 J. Peter Mugaas + -Optimized the DoneSettingInfoBlock method in the TIdGopherMenuItem object + -Added Ask property to the TIdGopherMenuItem + + 2000-May -13 J. Peter Mugaas + -Chanded the event types and classes to be prefixed with Id. + + 2000-Apr.-28 J. Peter Mugaas + -Added built in Gopher+ support + + 2000-Apr.-21 J. Peter Mugaas + -Added the ability to receive a file + -Restructured this component to make the code more reabible, + facilitate processing, and improve object orientation + + 2000-Apr.-20 J. Peter Mugaas + -Started this unit +} + +unit IdGopher; + +{*******************************************************} +{ } +{ Indy Gopher Client TIdGopher } +{ } +{ Copyright (C) 2000 Winshoes Working Group } +{ Started by J. Peter Mugaas } +{ April 20, 2000 } +{ } +{*******************************************************} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, + IdEMailAddress, + IdGlobal, + IdHeaderList, IdTCPClient, IdBaseComponent; + +type + TIdGopherMenuItem = class(TCollectionItem) + protected + FTitle : String; + FItemType : Char; + FSelector : String; + FServer : String; + FPort : TIdPort; + FGopherPlusItem : Boolean; + FGopherBlock : TIdHeaderList; + FViews : TStrings; + FURL : String; + FAbstract : TStrings; + FAsk : TIdHeaderList; + fAdminEmail : TIdEMailAddressItem; + function GetLastModified : String; + function GetOrganization : String; + function GetLocation : String; + function GetGeog : String; + public + constructor Create(ACollection: TCollection); override; + destructor Destroy; override; + {This procedure updates several internal variables and should be done when + all data has been added} + procedure DoneSettingInfoBlock; virtual; + {This is the title for the gopher Menu item and should be displayed to the + user} + property Title : String read FTitle write FTitle; + {This charactor indicates the type of Item that this is. + Use this to determine what methods to call to get the item} + property ItemType : Char read FItemType write FItemType; + {This is the Selector you use to retreive the item} + property Selector : String read FSelector write FSelector; + {This is the server you connect to and request the item from. Set the host + property to this when retrieving it} + property Server : String read FServer write FServer; + {This indicates the port you connect to in order to request the item. Set + the port property to this value to get an item.} + property Port : TIdPort read FPort write FPort; + {This indicates if the item is on a Gopher+ server - you can use + GetExtended Menues for menus} + property GopherPlusItem : Boolean read FGopherPlusItem + write FGopherPlusItem; + {These items are only available if you use the GetExtendedMenu method} + {This is the complete information block for this gopher+ item} + property GopherBlock : TIdHeaderList read FGopherBlock; + {URL listed at +URL: Section } + property URL : String read FURL; + {This is the Gopher Views available for the item. You can include this + when requesting it} + property Views : TStrings read FViews; + {abstract of Gopher item - had to be AAbstract due to Pascal reserved word} + {this is a summery of a particular item - e.g. "Read about our greate + products"} + property AAbstract : TStrings read FAbstract; + {This is the date that the item was last modified} + property LastModified : String read GetLastModified; + {This is contact information for the adminst} + property AdminEMail : TIdEMailAddressItem read fAdminEmail; + {This is the organization running the server and + is usually only found in the Root item} + property Organization : String read GetOrganization; + {This is the location where the Gopher is + and is usually only found in the Root item} + property Location : String read GetLocation; + {This is the latitude longitude and longitude of the Gopher server + and is usually only found in the Root item} + property Geog : String read GetGeog; + {This Gopher+ information is used for prmoting users for Query data} + property Ask : TIdHeaderList read FAsk; + end; + + TIdGopherMenu = class ( TCollection ) + protected + function GetItem ( Index: Integer ) : TIdGopherMenuItem; + procedure SetItem ( Index: Integer; const Value: TIdGopherMenuItem ); + public + constructor Create; reintroduce; + function Add: TIdGopherMenuItem; + property Items [ Index: Integer ] : TIdGopherMenuItem read GetItem + write SetItem; default; + end; + + TIdGopherMenuEvent = procedure ( Sender : TObject; + MenuItem : TIdGopherMenuItem ) of object; + + TIdGopher = class ( TIdTCPClientCustom ) + private + { Private declarations } + protected + { Protected declarations } + FOnMenuItem : TIdGopherMenuEvent; + {This triggers the menu item event} + Procedure DoMenu ( MenuItem : TIdGopherMenuItem ); + {This fires an exception for Gopher+ errors} + Procedure ProcessGopherError; + {This takes parses a string and makes a Menu Item for it} + Function MenuItemFromString ( stLine : String; Menu : TIdGopherMenu) + : TIdGopherMenuItem; + {Process the menu while we retreive it} + Function ProcessDirectory ( PreviousData : String = ''; {Do not Localize} + const ExpectedLength: Integer = 0) : TIdGopherMenu; + {This processes extended Gopher Menues} + Function LoadExtendedDirectory ( PreviousData : String = ''; {Do not Localize} + const ExpectedLength: Integer = 0) : TIdGopherMenu; + {This processes the file when we retreive it and puts it in ADestStream. } + procedure ProcessFile ( ADestStream : TStream; APreviousData : String = ''; {Do not Localize} + const ExpectedLength : Integer = 0); + {For Gopher +, we call this routine when we get a -2 length which means, + read until you see EOL+.+EOL} + Procedure ProcessTextFile ( ADestStream : TStream; + APreviousData: String = ''; const ExpectedLength: Integer = 0); {Do not Localize} + procedure InitComponent; override; + public + { Public declarations } + Function GetMenu (ASelector : String; IsGopherPlus : Boolean = False; AView : String = '' ) : {Do not Localize} + TIdGopherMenu; + Function Search(ASelector, AQuery : String) : TIdGopherMenu; + procedure GetFile (ASelector : String; ADestStream : TStream; IsGopherPlus : Boolean = False; AView: String = ''); {Do not Localize} + procedure GetTextFile(ASelector : String; ADestStream : TStream; IsGopherPlus : Boolean = False; AView: String = ''); {Do not Localize} + Function GetExtendedMenu (ASelector : String; AView: String = '' ) : TIdGopherMenu; {Do not Localize} + published + { Published declarations } + property OnMenuItem : TIdGopherMenuEvent read FOnMenuItem write FOnMenuItem; + property Port default IdPORT_Gopher; + property Host; + end; + +implementation + +uses + IdComponent, IdException, + IdGlobalProtocols, IdGopherConsts, IdReplyRFC, + IdTCPConnection, SysUtils; + +{ TIdGopher } + +procedure TIdGopher.InitComponent; +begin + inherited InitComponent; + Port := IdPORT_GOPHER; +end; + +procedure TIdGopher.DoMenu(MenuItem: TIdGopherMenuItem); +begin + if Assigned( FOnMenuItem ) then + FOnMenuItem( Self, MenuItem ); +end; + +procedure TIdGopher.ProcessGopherError; +var ErrorNo : Integer; + ErrMsg : String; +begin + ErrMsg := IOHandler.AllData; + {Get the error number from the error reply line} + ErrorNo := IndyStrToInt ( Fetch ( ErrMsg ) ); + {we want to drop the CRLF+'.'+CRLF} {Do not Localize} + LastCmdResult.SetReply(ErrorNo,ErrMsg); + LastCmdResult.RaiseReplyError; +end; + +function TIdGopher.MenuItemFromString(stLine: String; + Menu: TIdGopherMenu): TIdGopherMenuItem; +begin + {just in case a space thows things off} + stLine := Trim(stLine); + if Assigned ( Menu ) then + begin + Result := Menu.Add; + end // if Assigned ( Menu ) then + else + begin + Result := TIdGopherMenuItem.Create( nil ); + end; // else .. if Assigned ( Menu ) then + {title and Item Type} + Result.Title := Fetch ( stLine, TAB ); + if Length ( Result.Title ) > 0 then + begin + Result.ItemType := Result.Title [ 1 ]; + end //if Length.Result.Title > 0 then + else + begin + Result.ItemType := IdGopherItem_Error; + end; //else..if Length.Result.Title > 0 then + {drop first charactor because that was the item type indicator} + Result.Title := Copy ( Result.Title, 2, Length ( Result.Title ) ); + {selector string} + Result.Selector := Fetch ( stLine, TAB ); + {server} + Result.Server := Fetch ( stLine, TAB ); + {port} + Result.Port := IndyStrToInt ( Fetch ( stLine, TAB ) ); + {is Gopher + Item} + stLine := Fetch ( stLine, TAB ); + Result.GopherPlusItem := ( (Length ( stLine) > 0 ) and + ( stLine [ 1 ] = '+' ) ); {Do not Localize} +end; + +Function TIdGopher.LoadExtendedDirectory ( PreviousData : String = ''; {Do not Localize} + const ExpectedLength: Integer = 0) : TIdGopherMenu; +var + stLine : String; + gmnu : TIdGopherMenuItem; +begin + BeginWork(wmRead, ExpectedLength); try + Result := TIdGopherMenu.Create; + gmnu := nil; + repeat + stLine := PreviousData + IOHandler.ReadLn; + {we use the Previous data only ONCE} + PreviousData := ''; {Do not Localize} + {we process each line only if it is not the last and the + OnMenuItem is assigned} + if ( stLine <> '.' ) then {Do not Localize} + begin + {This is a new Extended Gopher menu so lets start it} + if ( Copy (stLine, 1, Length ( IdGopherPlusInfo ) ) = IdGopherPlusInfo ) then + begin + {fire event for previous item} + if (gmnu <> nil) then + begin + gmnu.DoneSettingInfoBlock; + DoMenu ( gmnu ); + end; //if (gmnu <> nil) then + gmnu := MenuItemFromString ( RightStr( stLine, + Length ( stLine ) - Length ( IdGopherPlusInfo ) ) , Result ); + gmnu.GopherBlock.Add ( stLine); + end //if (Pos(IdGopherGPlusInfo, stLine) = 0) then + else + begin + if Assigned( gmnu ) and (stLine <> '') then {Do not Localize} + begin + gmnu.GopherBlock.Add ( stLine ); + end; + end; //else...if (Pos(IdGopherGPlusInfo, stLine) = 0) then + end //if not stLine = '.' then {Do not Localize} + else + begin + {fire event for the last line} + if (gmnu <> nil) then + begin + DoMenu ( gmnu ); + end; //if (gmnu <> nil) then + end; //if ( stLine <> '.' ) then {Do not Localize} + until (stLine = '.') or not Connected; {Do not Localize} + finally EndWork(wmRead); end; +end; + +Function TIdGopher.ProcessDirectory ( PreviousData : String = ''; {Do not Localize} + const ExpectedLength: Integer = 0) : TIdGopherMenu; +var stLine : String; + +begin + BeginWork(wmRead,ExpectedLength); try + Result := TIdGopherMenu.Create; + repeat + stLine := PreviousData + IOHandler.ReadLn; + {we use the Previous data only ONCE} + PreviousData := ''; {Do not Localize} + {we process each line only if it is not the last and the OnMenuItem + is assigned} + if ( stLine <> '.' ) then {Do not Localize} + begin + //add Gopher Menu item and fire event + DoMenu ( MenuItemFromString ( stLine, Result ) ); + end; //if not stLine = '.' then {Do not Localize} + until (stLine = '.') or not Connected; {Do not Localize} + finally + EndWork(wmRead); + end; //try..finally +end; + +procedure TIdGopher.ProcessTextFile(ADestStream : TStream; APreviousData: String = ''; {Do not Localize} + const ExpectedLength: Integer = 0); +var + LEnc: IIdTextEncoding; +begin + LEnc := IndyTextEncoding_8Bit; + WriteStringToStream(ADestStream, APreviousData, LEnc{$IFDEF STRING_IS_ANSI}, LEnc{$ENDIF}); + BeginWork(wmRead,ExpectedLength); + try + IOHandler.Capture(ADestStream, '.', True); {Do not Localize} + finally + EndWork(wmRead); + end; //try..finally +end; + +procedure TIdGopher.ProcessFile ( ADestStream : TStream; APreviousData : String = ''; {Do not Localize} + const ExpectedLength : Integer = 0); +var + LEnc: IIdTextEncoding; +begin + BeginWork(wmRead,ExpectedLength); + try + LEnc := IndyTextEncoding_8Bit; + WriteStringToStream(ADestStream, APreviousData, LEnc{$IFDEF STRING_IS_ANSI}, LEnc{$ENDIF}); + IOHandler.ReadStream(ADestStream, -1, True); + ADestStream.Position := 0; + finally + EndWork(wmRead); + end; +end; + +Function TIdGopher.Search(ASelector, AQuery : String) : TIdGopherMenu; +begin + Connect; + try + {Gopher does not give a greating} + IOHandler.WriteLn ( ASelector + TAB + AQuery ); + Result := ProcessDirectory; + finally + Disconnect; + end; {try .. finally .. end } +end; + +procedure TIdGopher.GetFile (ASelector : String; ADestStream : TStream; + IsGopherPlus : Boolean = False; + AView: String = ''); {Do not Localize} +var + Reply : Char; + LengthBytes : Integer; {legnth of the gopher items} +begin + Connect; + try + if not IsGopherPlus then + begin + IOHandler.WriteLn ( ASelector ); + ProcessFile ( ADestStream ); + end // if not IsGopherPlus then + else + begin + {I hope that this drops the size attribute and that this will cause the + Views to work, I'm not sure} {Do not Localize} + AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize} + IOHandler.WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize} + {We read only one byte from the peer} + Reply := Char(IOHandler.ReadByte); + {Get the additonal reply code for error or success} + case Reply of + '-' : begin {Do not Localize} + {Get the length byte} + IOHandler.ReadLn; + ProcessGopherError; + end; {-} + {success - read file} + '+' : begin {Do not Localize} + {Get the length byte} + LengthBytes := IndyStrToInt ( IOHandler.ReadLn ); + case LengthBytes of + {dot terminated - probably a text file} + -1 : ProcessTextFile ( ADestStream ); + {just read until I disconnect you} + -2 : ProcessFile ( ADestStream ); + else + ProcessFile ( ADestStream, '', LengthBytes); {Do not Localize} + end; //case LengthBytes of + end; {+} + else + begin + ProcessFile ( ADestStream, Reply ); + end; //else ..case Reply of + end; //case Reply of + end; //else..if IsGopherPlus then + finally + Disconnect; + end; {try .. finally .. end } +end; + +function TIdGopher.GetMenu ( ASelector : String; IsGopherPlus : Boolean = False; AView : String = '' ) : {Do not Localize} + TIdGopherMenu; +var + Reply : Char; + LengthBytes : Integer; {legnth of the gopher items} +begin + Result := nil; + Connect; + try + if not IsGopherPlus then + begin + IOHandler.WriteLn ( ASelector ); + Result := ProcessDirectory; + end // if not IsGopherPlus then + else + begin + {Gopher does not give a greating} + IOHandler.WriteLn ( ASelector + TAB+'+' + AView ); {Do not Localize} + {We read only one byte from the peer} + Reply := Char(IOHandler.ReadByte); + {Get the additonal reply code for error or success} + case Reply of + '-' : begin {Do not Localize} + IOHandler.ReadLn; + ProcessGopherError; + end; {-} + '+' : begin {Do not Localize} + {Get the length byte} + LengthBytes := IndyStrToInt ( IOHandler.ReadLn ); + Result := ProcessDirectory ('', LengthBytes ); {Do not Localize} + end; {+} + else + begin + Result := ProcessDirectory ( Reply ); + end; //else..case Reply of + end; //case Reply of + end; //if not IsGopherPlus then + finally + Disconnect; + end; {try .. finally .. end } +end; + +Function TIdGopher.GetExtendedMenu(ASelector, AView: String) : TIdGopherMenu; +var + Reply : Char; + LengthBytes : Integer; {legnth of the gopher items} +begin + Result := nil; + Connect; try + {Gopher does not give a greating} + IOHandler.WriteLn(ASelector + TAB + '$' + AView); {Do not Localize} + {We read only one byte from the peer} + Reply := Char(IOHandler.ReadByte); + {Get the additonal reply code for error or success} + case Reply of + '-' : begin {Do not Localize} + IOHandler.ReadLn; + ProcessGopherError; + end; {-} + '+' : begin {Do not Localize} + {Get the length byte} + LengthBytes := IndyStrToInt ( IOHandler.ReadLn ); + Result := LoadExtendedDirectory( '', LengthBytes); {Do not Localize} + end; {+} + else + Result := ProcessDirectory ( Reply ); + end; //case Reply of + finally + Disconnect; + end; {try .. finally .. end } +end; + +procedure TIdGopher.GetTextFile(ASelector: String; ADestStream: TStream; + IsGopherPlus: Boolean; AView: String); +var + Reply : Char; + LengthBytes : Integer; {length of the gopher items} +begin + Connect; + try + if not IsGopherPlus then + begin + IOHandler.WriteLn ( ASelector ); + ProcessTextFile ( ADestStream ); + end // if not IsGopherPlus then + else + begin + {I hope that this drops the size attribute and that this will cause the + Views to work, I'm not sure} {Do not Localize} + AView := Trim ( Fetch ( AView, ':' ) ); {Do not Localize} + IOHandler.WriteLn ( ASelector + TAB +'+'+ AView ); {Do not Localize} + {We read only one byte from the peer} + Reply := Char(IOHandler.ReadByte); + {Get the additonal reply code for error or success} + case Reply of + '-' : begin {Do not Localize} + {Get the length byte} + IOHandler.ReadLn; + ProcessGopherError; + end; {-} + {success - read file} + '+' : begin {Do not Localize} + {Get the length byte} + LengthBytes := IndyStrToInt ( IOHandler.ReadLn ); + case LengthBytes of + {dot terminated - probably a text file} + -1 : ProcessTextFile ( ADestStream ); + {just read until I disconnect you} + -2 : ProcessFile ( ADestStream ); + else + ProcessTextFile ( ADestStream, '', LengthBytes); {Do not Localize} + end; //case LengthBytes of + end; {+} + else + begin + ProcessTextFile ( ADestStream, Reply ); + end; //else ..case Reply of + end; //case Reply of + end; //else..if IsGopherPlus then + finally + Disconnect; + end; {try .. finally .. end } +end; + +{ TIdGopherMenu } + +function TIdGopherMenu.Add: TIdGopherMenuItem; +begin + Result := TIdGopherMenuItem ( inherited Add ); +end; + +constructor TIdGopherMenu.Create; +begin + inherited Create ( TIdGopherMenuItem ); +end; + +function TIdGopherMenu.GetItem(Index: Integer): TIdGopherMenuItem; +begin + result := TIdGopherMenuItem( inherited Items [ index ] ); +end; + +procedure TIdGopherMenu.SetItem( Index: Integer; + const Value: TIdGopherMenuItem ); +begin + inherited SetItem ( Index, Value ); +end; + +{ TIdGopherMenuItem } + +constructor TIdGopherMenuItem.Create(ACollection: TCollection); +begin + inherited Create(ACollection); + FGopherBlock := TIdHeaderList.Create(QuotePlain); + {we don't unfold or fold lines as headers in that block start with a space} {Do not Localize} + FGopherBlock.UnfoldLines := False; + FGopherBlock.FoldLines := False; + FViews := TStringList.Create; + FAbstract := TStringList.Create; + FAsk := TIdHeaderList.Create(QuotePlain); + fAdminEmail := TIdEMailAddressItem.Create ( nil ); +end; + +destructor TIdGopherMenuItem.Destroy; +begin + FreeAndNil ( fAdminEmail ); + FreeAndNil ( FAsk ); + FreeAndNil ( FAbstract ); + FreeAndNil ( FGopherBlock ); + FreeAndNil ( FViews ); + inherited Destroy; +end; + +procedure TIdGopherMenuItem.DoneSettingInfoBlock; +{These constants are for blocks we wish to obtain - don't change as they are + part of Gopher+ protocol} +const + BlockTypes : Array [1..3] of String = ('+VIEWS', '+ABSTRACT', '+ASK'); {Do not Localize} +var + idx : Integer; + line : String; + + Procedure ParseBlock ( Block : TStrings); + {Put our the sublock in the Block TIdStrings and increment + the pointer appropriatriately} + begin + Inc ( idx ); + while ( idx < FGopherBlock.Count ) and + ( FGopherBlock [ idx ] [ 1 ] = ' ' ) do {Do not Localize} + begin + Block.Add ( TrimLeft ( FGopherBlock [ idx ] ) ); + Inc ( idx ); + end; //while + {correct for incrementation in the main while loop} + Dec ( idx ); + end; + +begin + idx := 0; + while ( idx < FGopherBlock.Count ) do + begin + Line := FGopherBlock [ idx ]; + Line := Fetch( Line, ':' ); {Do not Localize} + case PosInStrArray ( Line, BlockTypes, False ) of + {+VIEWS:} + 0 : ParseBlock ( FViews ); + {+ABSTRACT:} + 1 : ParseBlock ( FAbstract ); + {+ASK:} + 2 : ParseBlock ( FAsk ); + end; + Inc ( idx ); + end; + fAdminEmail.Text := FGopherBlock.Values [ ' Admin' ]; {Do not Localize} +end; + +function TIdGopherMenuItem.GetGeog: String; +begin + Result := FGopherBlock.Values [ ' Geog' ]; {Do not Localize} +end; + +function TIdGopherMenuItem.GetLastModified: String; +begin + Result := FGopherBlock.Values [ ' Mod-Date' ]; {Do not Localize} +end; + +function TIdGopherMenuItem.GetLocation: String; +begin + Result := FGopherBlock.Values [ ' Loc' ]; {Do not Localize} +end; + +function TIdGopherMenuItem.GetOrganization: String; +begin + Result := FGopherBlock.Values [ ' Org' ]; {Do not Localize} +end; + +end. diff --git a/indy/Protocols/IdGopherConsts.pas b/indy/Protocols/IdGopherConsts.pas new file mode 100644 index 0000000..d366930 --- /dev/null +++ b/indy/Protocols/IdGopherConsts.pas @@ -0,0 +1,151 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 2004.02.03 5:44:46 PM czhower + Name changes + + Rev 1.1 10/17/2003 12:06:16 AM DSiders + Added localization comments. + + Rev 1.0 11/13/2002 08:30:10 AM JPMugaas + Initial import from FTP VC. +} + +unit IdGopherConsts; + +{*******************************************************} +{ } +{ Indy IdGopherConsts - this just contains } +{ Constants used for writing Gopher servers } +{ and clients } +{ } +{ Copyright (C) 2000 Winshoes Working Group } +{ Original author: Pete Mee and moved to } +{ this unit by J. Peter Mugaas } +{ 2000-April-23 } +{ } +{*******************************************************} + +interface +{$i IdCompilerDefines.inc} + +uses + IdGlobal; + +Const + {Item constants - comments taken from RFC} + IdGopherItem_Document = '0'; // Item is a file + IdGopherItem_Directory = '1'; // Item is a directory + IdGopherItem_CSO = '2'; // Item is a CSO phone-book server + IdGopherItem_Error = '3'; // Error + IdGopherItem_BinHex = '4'; // Item is a BinHexed Macintosh file. + IdGopherItem_BinDOS = '5'; // Item is DOS binary archive of some sort. + // Client must read until the TCP connection closes. Beware. + IdGopherItem_UUE = '6'; // Item is a UNIX uuencoded file. + IdGopherItem_Search = '7'; // Item is an Index-Search server. + IdGopherItem_Telnet = '8'; // Item points to a text-based telnet session. + IdGopherItem_Binary = '9'; // Item is a binary file. + // Client must read until the TCP connection closes. Beware. + IdGopherItem_Redundant = '+'; // Item is a redundant server + IdGopherItem_TN3270 = 'T'; // Item points to a text-based tn3270 session. + IdGopherItem_GIF = 'g'; // Item is a GIF format graphics file. + IdGopherItem_Image = ':'; // Item is some kind of image file. + // Client decides how to display. Was 'I', but depracted + IdGopherItem_Image2 = 'I'; //Item is some kind of image file - + // this was drepreciated + + {Items discovered outside of Gopher RFC - "Gopher+"} + IdGopherItem_Sound = '<'; //Was 'S', but deprecated + IdGopherItem_Sound2 = 'S'; //This was depreciated but should be used with clients + IdGopherItem_Movie = ';'; //Was 'M', but deprecated + IdGopherItem_HTML = 'h'; + IdGopherItem_MIME = 'M'; //See above for a potential conflict with Movie + IdGopherItem_Information = 'i'; // Not a file - just information + + IdGopherPlusIndicator = IdGopherItem_Redundant; // Observant people will note + // the conflict here...! + IdGopherPlusInformation = '!'; // Formatted information + IdGopherPlusDirectoryInformation = '$'; + + //Gopher+ additional information + IdGopherPlusInfo = '+INFO: '; {do not localize} + { Info format is the standard Gopher directory entry + TAB + '+'. + The info is contained on the same line as the '+INFO: '} + IdGopherPlusAdmin = '+ADMIN:' + EOL; {do not localize} + { Admin block required for every item. The '+ADMIN:' occurs on a + line of it's own (starting with a space) and is followed by + the fields - one per line. + + Required fields: + ' Admin: ' [+ comments] + '<' + admin e-mail address + '>' + ' ModDate: ' [+ comments] + '<' + dateformat:YYYYMMDDhhnnss + '>' + + Optional fields regardless of location: + ' Score: ' + relevance-ranking + ' Score-range: ' + lower-bound + ' ' + upper-bound + + Optional fields recommended at the root only: + ' Site: ' + site-name + ' Org: ' + organization-description + ' Loc: ' + city + ', ' + state + ', ' + country + ' Geog: ' + latitude + ' ' + longitude + ' TZ: ' + GMT-offset + + Additional recorded possibilities: + ' Provider: ' + item-provider-name + ' Author: ' + author + ' Creation-Date: ' + '<' + YYYYMMDDhhnnss + '>' + ' Expiration-Date: ' + '<' + YYYYMMDDhhnnss + '>' + } + IdGopherPlusViews = '+VIEWS:' + EOL; {do not localize} + { View formats are one per line: + ' ' + mime/type [+ langcode] + ': <' + size estimate + '>' + ' ' + logcode = ' ' + ISO-639-Code + '_' + ISO-3166-Code + } + IdGopherPlusAbstract = '+ABSTRACT:' + EOL; {do not localize} + { Is followed by a (multi-)line description. Line(s) begin with + a space.} + IdGopherPlusAsk = '+ASK:'; {do not localize} + + //Questions for +ASK section: + IdGopherPlusAskPassword = 'AskP: '; {do not localize} + IdGopherPlusAskLong = 'AskL: '; {do not localize} + IdGopherPlusAskFileName = 'AskF: '; {do not localize} + + // Prompted responses for +ASK section: + + // Multi-choice, multi-selection + IdGopherPlusSelect = 'Select: '; {do not localize} + // Multi-choice, single-selection + IdGopherPlusChoose = 'Choose: '; {do not localize} + //Multi-choice, single-selection + IdGopherPlusChooseFile = 'ChooseF: '; {do not localize} + + //Known response types: + IdGopherPlusData_BeginSign = '+-1' + EOL; + IdGopherPlusData_EndSign = EOL + '.' + EOL; + IdGopherPlusData_UnknownSize = '+-2' + EOL; + IdGopherPlusData_ErrorBeginSign = '--1' + EOL; + IdGopherPlusData_ErrorUnknownSize = '--2' + EOL; + IdGopherPlusError_NotAvailable = '1'; + IdGopherPlusError_TryLater = '2'; + IdGopherPlusError_ItemMoved = '3'; + +implementation + +end. diff --git a/indy/Protocols/IdGopherServer.pas b/indy/Protocols/IdGopherServer.pas new file mode 100644 index 0000000..740d22d --- /dev/null +++ b/indy/Protocols/IdGopherServer.pas @@ -0,0 +1,192 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 12/2/2004 4:23:54 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.6 2004.02.03 5:44:48 PM czhower + Name changes + + Rev 1.5 1/21/2004 3:26:46 PM JPMugaas + InitComponent + + Rev 1.4 2/24/2003 08:54:00 PM JPMugaas + + Rev 1.3 1/17/2003 07:10:26 PM JPMugaas + Now compiles under new framework. + + Rev 1.2 1-1-2003 20:13:12 BGooijen + Changed to support the new TIdContext class + + Rev 1.1 12/6/2002 04:35:10 PM JPMugaas + Now compiles with new code. + + Rev 1.0 11/13/2002 08:30:20 AM JPMugaas + Initial import from FTP VC. + + 2000-Apr-29 Pete Mee + - Converted to new Indy format. + + 1999-Oct-03 Pete Mee + - Gopher server is very basic... started & completed... +} + +unit IdGopherServer; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, + IdContext, + IdCustomTCPServer, + IdGlobal; + +{ + Typical connection: + - Client attaches with no data + - Server accepts with no data + - Client sends request with CR LF termate (CRLF only for root) + - Server sends items available each with CRLF termating + - Server sends .CRLF + - Server close connection +} + +type + TRequestEvent = procedure(AContext:TIdContext;ARequest:String) of object; + TPlusRequestEvent = procedure(AContext:TIdContext;ARequest:String; + APlusData : String) of object; + + TIdGopherServer = class(TIdCustomTCPServer) + private + fAdminEmail : String; + + fOnRequest : TRequestEvent; + fOnPlusRequest : TPlusRequestEvent; + + fTruncateUserFriendly : Boolean; + fTruncateLength : Integer; + protected + function DoExecute(AContext: TIdContext): Boolean; override; + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + function ReturnGopherItem(ItemType : Char; + UserFriendlyName, RealResourceName : String; + HostServer : String; HostPort : TIdPort): String; + procedure SendDirectoryEntry(AContext:TIdContext; + ItemType : Char; UserFriendlyName, RealResourceName : String; + HostServer : String; HostPort : TIdPort); + published + property AdminEmail : String read fAdminEmail write fAdminEmail; + property OnRequest: TRequestEvent read fOnRequest write fOnRequest; + property OnPlusRequest : TPlusRequestEvent read fOnPlusRequest + write fOnPlusRequest; + property TruncateUserFriendlyName : Boolean read fTruncateUserFriendly + write fTruncateUserFriendly default True; + property TruncateLength : Integer read fTruncateLength + write fTruncateLength default 70; + property DefaultPort default IdPORT_GOPHER; + end; + +implementation + +uses + IdGopherConsts, IdResourceStringsProtocols, SysUtils; + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdGopherServer.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdGopherServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_GOPHER; + fAdminEmail := ''; {Do not Localize} + fTruncateUserFriendly := True; + fTruncateLength := 70; +end; + +function TIdGopherServer.DoExecute(AContext: TIdContext): boolean; +var + s : String; + i : Integer; +begin + Result := True; + s := AContext.Connection.IOHandler.ReadLn; + i := Pos(TAB, s); + if i > 0 then begin + // Is a Gopher+ request + if Assigned(OnPlusRequest) then begin + OnPlusRequest(AContext, Copy(s, 1, i - 1), Copy(s, i + 1, Length(s))); + end else if Assigned(OnRequest) then begin + OnRequest(AContext, s); + end else begin + AContext.Connection.IOHandler.Write( + IdGopherPlusData_ErrorBeginSign + + IdGopherPlusError_NotAvailable + + RSGopherServerNoProgramCode + EOL + + IdGopherPlusData_EndSign); + end; + end else if Assigned(OnRequest) then begin + OnRequest(AContext, s); + end else begin + AContext.Connection.IOHandler.Write(RSGopherServerNoProgramCode + EOL + IdGopherPlusData_EndSign); + end; + AContext.Connection.Disconnect; +end; + +function TIdGopherServer.ReturnGopherItem(ItemType : Char; + UserFriendlyName, RealResourceName : String; + HostServer : String; HostPort : TIdPort): String; +begin + if fTruncateUserFriendly then begin + if (Length(UserFriendlyName) > fTruncateLength) and (fTruncateLength <> 0) then begin + UserFriendlyName := Copy(UserFriendlyName, 1, fTruncateLength); + end; + end; + Result := ItemType + UserFriendlyName + + TAB + RealResourceName + TAB + HostServer + TAB + IntToStr(HostPort); +end; + +procedure TIdGopherServer.SendDirectoryEntry; +{ +Format of server reply to directory (assume no spacing between - i.e., +one line, with CR LF at the end) + - Item Type + - User Description (without tab characters) + - Tab + - Server-assigned string to this individual Item Type resource + - Tab + - Domain Name of host + - Tab + - Port # of host +} +begin + AContext.Connection.IOHandler.WriteLn(ReturnGopherItem(ItemType, UserFriendlyName, + RealResourceName, HostServer, HostPort)); +end; + +end. diff --git a/indy/Protocols/IdHL7.pas b/indy/Protocols/IdHL7.pas new file mode 100644 index 0000000..b504690 --- /dev/null +++ b/indy/Protocols/IdHL7.pas @@ -0,0 +1,1595 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 9/30/2004 5:04:18 PM BGooijen + Self was not initialized + + Rev 1.8 6/11/2004 9:36:14 AM DSiders + Added "Do not Localize" comments. + + Rev 1.7 2004.02.07 5:03:02 PM czhower + .net fixes. + + Rev 1.6 2004.02.03 5:43:44 PM czhower + Name changes + + Rev 1.5 1/21/2004 2:42:46 PM JPMugaas + InitComponent + + Rev 1.4 1/3/2004 12:59:54 PM JPMugaas + These should now compile with Kudzu's change in IdCoreGlobal. + + Rev 1.3 4/12/2003 9:21:32 PM GGrieve + give up on Indy10 for the moment + + Rev 1.2 10/15/2003 9:53:42 PM GGrieve + DotNet changes + + Rev 1.1 23/6/2003 22:33:54 GGrieve + update for indy10 IOHandler model + + Rev 1.0 11/13/2002 07:53:58 AM JPMugaas + + 05/09/2002 Grahame Grieve + Fixed SingleThread Timeout Issues + WaitForConnection + + 23/01/2002 Grahame Grieve + Fixed for network changes to TIdTCPxxx wrote DUnit testing, + increased assertions change OnMessageReceive, + added VHandled parameter + + 07/12/2001 Grahame Grieve Various fixes for cmSingleThread mode + + 05/11/2001 Grahame Grieve Merge into Indy + + 03/09/2001 Grahame Grieve Prepare for Indy +} + +{ + ============================================================================== + Warning: this code is currently broken in Indy 10. + + The extensive changes to the IOHandler architecture mean that the way + this unit works - doing asynchronous IO in a single connection - can + no longer work without causing access violations whenever the socket + is closed + + This code needs to be re-written to resolve these issues somehow, but + no clear design has emerged at this point + ============================================================================== +} + +{ + Indy HL7 Minimal Lower Layer Protocol TIdHL7 + + Original author Grahame Grieve + + This code was donated by HL7Connect.com + For more HL7 open source code see + http://www.hl7connect.com/tools + + This unit implements support for the Standard HL7 minimal Lower Layer + protocol. For further details, consult the HL7 standard (www.hl7.org). + + Before you can use this component, you must set the following properties: + CommunicationMode + Address (if you want to be a client) + Port + isListener + and hook the appropriate events (see below) + + This component will operate as either a server or a client depending on + the configuration +} + +(* + note: Events are structurally important for this component. However there is + a bug in SyncObjs for Linux under Kylix 1 and 2 where TEvent.WaitFor cannot be + used with timeouts. If you compile your own RTL, then you can fix the routine + like this: + + function TEvent.WaitFor(Timeout: LongWord): TWaitResult; + {$IFDEF LINUX} + var ts : TTimeSpec; + begin + ts.tv_sec := timeout div 1000; + ts.tv_nsec := (timeout mod 1000) * 1000000; + if sem_timedwait(FSem, ts) = 0 then + result := wrSignaled + else + result := wrTimeOut; + {$ENDIF} + + and then disable this define: + + this is a serious issue - unless you fix the RTL, this component does not + function properly on Linux at the present time. This may be fixed in a + future version +*) + +{ TODO : use Server.MaxConnections } + +unit IdHL7; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdBaseComponent, + IdContext, + IdException, + IdGlobal, + IdTCPClient, + IdTCPConnection, + IdTCPServer, + SysUtils; + +const + MSG_START = #$0B; {do not localize} + MSG_END = #$1C#$0D; {do not localize} + +type + EHL7CommunicationError = class(EIdException) + Protected + FInterfaceName: String; + Public + constructor Create(AnInterfaceName, AMessage: String); + property InterfaceName: String Read FInterfaceName; + end; + + + THL7CommunicationMode = (cmUnknown, // not valid - default setting must be changed by application + cmAsynchronous, // see comments below for meanings of the other parameters + cmSynchronous, + cmSingleThread); + + TSendResponse = (srNone, // internal use only - never returned + srError, // internal use only - never returned + srNoConnection, // you tried to send but there was no connection + srSent, // you asked to send without waiting, and it has been done + srOK, // sent ok, and response returned + srTimeout); // we sent but there was no response (connection will be dropped internally + + TIdHL7Status = (isStopped, // not doing anything + isNotConnected, // not Connected (Server state) + isConnecting, // Client is attempting to connect + isWaitReconnect, // Client is in delay loop prior to attempting to connect + isConnected, // connected OK + isUnusable // Not Usable - stop failed + ); + +const + { default property values } + DEFAULT_ADDRESS = ''; {do not localize} + DEFAULT_PORT = 0; + DEFAULT_TIMEOUT = 30000; + DEFAULT_RECEIVE_TIMEOUT = 30000; + NULL_IP = '0.0.0.0'; {do not localize} + DEFAULT_CONN_LIMIT = 1; + DEFAULT_RECONNECT_DELAY = 15000; + DEFAULT_COMM_MODE = cmUnknown; + DEFAULT_IS_LISTENER = True; + MILLISECOND_LENGTH = (1 / (24 * 60 * 60 * 1000)); + +type + // the connection is provided in these events so that applications can obtain information about the + // the peer. It's never OK to write to these connections + TMessageArriveEvent = procedure(ASender: TObject; AConnection: TIdTCPConnection; AMsg: String) of object; + TMessageReceiveEvent = procedure(ASender: TObject; AConnection: TIdTCPConnection; AMsg: String; var VHandled: Boolean; var VReply: String) of object; + TReceiveErrorEvent = procedure(ASender: TObject; AConnection: TIdTCPConnection; AMsg: String; AException: Exception; var VReply: String; var VDropConnection: Boolean) of object; + + TIdHL7 = class; + TIdHL7ConnCountEvent = procedure (ASender : TIdHL7; AConnCount : integer) of object; + + TIdHL7ClientThread = class(TThread) + Protected + FClient: TIdTCPClient; + FCloseEvent: TIdLocalEvent; + FOwner: TIdHL7; + procedure Execute; Override; + procedure PollStack; + Public + constructor Create(aOwner: TIdHL7); + destructor Destroy; Override; + end; + + TIdHL7 = class(TIdBaseComponent) + Protected + FLock: TIdCriticalSection; + FStatus: TIdHL7Status; + FStatusDesc: String; + + // these queues hold messages when running in singlethread mode + FMsgQueue: TList; + FHndMsgQueue: TList; + + FAddress: String; + FCommunicationMode: THL7CommunicationMode; + FConnectionLimit: Word; + FIPMask: String; + FIPRestriction: String; + FIsListener: Boolean; + FObject: TObject; + FPreStopped: Boolean; + FPort: Word; + FReconnectDelay: LongWord; + FTimeOut: Cardinal; + FReceiveTimeout: LongWord; + + FOnConnect: TNotifyEvent; + FOnDisconnect: TNotifyEvent; + FOnConnCountChange : TIdHL7ConnCountEvent; + FOnMessageArrive: TMessageArriveEvent; + FOnReceiveMessage: TMessageReceiveEvent; + FOnReceiveError: TReceiveErrorEvent; + + FIsServer: Boolean; + // current connection count (server only) (can only exceed 1 when mode is not + // asynchronous and we are listening) + FConnCount: Integer; + FServer: TIdTCPServer; + // if we are a server, and the mode is not asynchronous, and we are not listening, then + // we will track the current server connection with this, so we can initiate sending on it + FServerConn: TIdTCPConnection; + + // A thread exists to connect and receive incoming tcp traffic + FClientThread: TIdHL7ClientThread; + FClient: TIdTCPClient; + + // these fields are used for handling message response in synchronous mode + FWaitingForAnswer: Boolean; + FWaitStop: TDateTime; + FMsgReply: String; + FReplyResponse: TSendResponse; + FWaitEvent: TIdLocalEvent; + + procedure SetAddress(const AValue: String); + procedure SetConnectionLimit(const AValue: Word); + procedure SetIPMask(const AValue: String); + procedure SetIPRestriction(const AValue: String); + procedure SetPort(const AValue: Word); + procedure SetReconnectDelay(const AValue: LongWord); + procedure SetTimeOut(const AValue: LongWord); + procedure SetCommunicationMode(const AValue: THL7CommunicationMode); + procedure SetIsListener(const AValue: Boolean); + function GetStatus: TIdHL7Status; + function GetStatusDesc: String; + + procedure InternalSetStatus(const AStatus: TIdHL7Status; ADesc: String); + + procedure CheckServerParameters; + procedure StartServer; + procedure StopServer; + procedure DropServerConnection; + procedure ServerConnect(AContext: TIdContext); + procedure ServerExecute(AContext: TIdContext); + procedure ServerDisconnect(AContext: TIdContext); + + procedure CheckClientParameters; + procedure StartClient; + procedure StopClient; + procedure DropClientConnection; + + procedure HandleIncoming(const AMsg : String; AConnection: TIdTCPConnection); + function HandleMessage(const AMsg: String; AConn: TIdTCPConnection; var VReply: String): Boolean; + procedure InitComponent; override; + Public + destructor Destroy; Override; + + procedure EnforceWaitReplyTimeout; + + function Going: Boolean; + + // for the app to use to hold any related object + property ObjTag: TObject Read FObject Write FObject; + + // status + property Status: TIdHL7Status Read GetStatus; + property StatusDesc: String Read GetStatusDesc; + function Connected: Boolean; + + property IsServer: Boolean Read FIsServer; + procedure Start; + procedure PreStop; // call this in advance to start the shut down process. You do not need to call this + procedure Stop; + + procedure WaitForConnection(AMaxLength: Integer); // milliseconds + + // asynchronous. + function AsynchronousSend(AMsg: String): TSendResponse; + property OnMessageArrive: TMessageArriveEvent Read FOnMessageArrive Write FOnMessageArrive; + + // synchronous + function SynchronousSend(AMsg: String; var VReply: String): TSendResponse; + property OnReceiveMessage: TMessageReceiveEvent Read FOnReceiveMessage Write FOnReceiveMessage; + procedure CheckSynchronousSendResult(AResult: TSendResponse; AMsg: String); + + // single thread + procedure SendMessage(AMsg: String); + // you can't call SendMessage again without calling GetReply first + function GetReply(var VReply: String): TSendResponse; + function GetMessage(var VMsg: String): TObject; // return nil if no messages + // if you don't call SendReply then no reply will be sent. + procedure SendReply(AMsgHnd: TObject; AReply: String); + + Published + // basic properties + property Address: String Read FAddress Write SetAddress; // leave blank and we will be server + property Port: Word Read FPort Write SetPort Default DEFAULT_PORT; + + // milliseconds - message timeout - how long we wait for other system to reply + property TimeOut: LongWord Read FTimeOut Write SetTimeOut Default DEFAULT_TIMEOUT; + + // milliseconds - message timeout. When running cmSingleThread, how long we wait for the application to process an incoming message before giving up + property ReceiveTimeout: LongWord Read FReceiveTimeout Write FReceiveTimeout Default DEFAULT_RECEIVE_TIMEOUT; + + // server properties + property ConnectionLimit: Word Read FConnectionLimit Write SetConnectionLimit Default DEFAULT_CONN_LIMIT; // ignored if isListener is false + property IPRestriction: String Read FIPRestriction Write SetIPRestriction; + property IPMask: String Read FIPMask Write SetIPMask; + + // client properties + + // milliseconds - how long we wait after losing connection to retry + property ReconnectDelay: LongWord Read FReconnectDelay Write SetReconnectDelay Default DEFAULT_RECONNECT_DELAY; + + // message flow + + // Set this to one of 4 possibilities: + // + // cmUnknown + // Default at start up. You must set a value before starting + // + // cmAsynchronous + // Send Messages with AsynchronousSend. does not wait for + // remote side to respond before returning + // Receive Messages with OnMessageArrive. Message may + // be response or new message + // The application is responsible for responding to the remote + // application and dropping the link as required + // You must hook the OnMessageArrive Event before setting this mode + // The property IsListener has no meaning in this mode + // + // cmSynchronous + // Send Messages with SynchronousSend. Remote applications response + // will be returned (or timeout). Only use if IsListener is false + // Receive Messages with OnReceiveMessage. Only if IsListener is + // true + // In this mode, the object will wait for a response when sending, + // and expects the application to reply when a message arrives. + // In this mode, the interface can either be the listener or the + // initiator but not both. IsListener controls which one. + // note that OnReceiveMessage must be thread safe if you allow + // more than one connection to a server + // + // cmSingleThread + // Send Messages with SendMessage. Poll for answer using GetReply. + // Only if isListener is false + // Receive Messages using GetMessage. Return a response using + // SendReply. Only if IsListener is true + // This mode is the same as cmSynchronous, but the application is + // assumed to be single threaded. The application must poll to + // find out what is happening rather than being informed using + // an event in a different thread + + property CommunicationMode: THL7CommunicationMode Read FCommunicationMode Write SetCommunicationMode Default DEFAULT_COMM_MODE; + + // note that IsListener is not related to which end is client. Either end + // may make the connection, and thereafter only one end will be the initiator + // and one end will be the listener. Generally it is recommended that the + // listener be the server. If the client is listening, network conditions + // may lead to a state where the client has a phantom connection and it will + // never find out since it doesn't initiate traffic. In this case, restart + // the interface if there isn't traffic for a period + property IsListener: Boolean Read FIsListener Write SetIsListener Default DEFAULT_IS_LISTENER; + + // useful for application + property OnConnect: TNotifyEvent Read FOnConnect Write FOnConnect; + property OnDisconnect: TNotifyEvent Read FOnDisconnect Write FOnDisconnect; + // this is called whenever OnConnect and OnDisconnect are called, and at other times, but only when server + // it will be called after OnConnect and before OnDisconnect + property OnConnCountChange : TIdHL7ConnCountEvent read FOnConnCountChange write FOnConnCountChange; + + // this is called when an unhandled exception is generated by the + // hl7 object or the application. It allows the application to + // construct a useful return error, log the exception, and drop the + // connection if it wants + property OnReceiveError: TReceiveErrorEvent Read FOnReceiveError Write FOnReceiveError; + end; + +implementation + +uses + {$IFDEF USE_VCL_POSIX} + {$IFDEF DARWIN} + CoreServices, + {$ENDIF} + PosixSysSelect, + PosixSysTime, + {$ENDIF} + IdGlobalProtocols, + IdResourceStringsProtocols; + +type + TQueuedMessage = class(TIdInterfacedObject) + Private + FEvent: TIdLocalEvent; + FMsg: String; + FTimeOut: LongWord; + FReply: String; + procedure Wait; + Public + constructor Create(aMsg: String; ATimeOut: LongWord); + destructor Destroy; Override; + end; + + { TQueuedMessage } + +constructor TQueuedMessage.Create(aMsg: String; ATimeOut: LongWord); +begin + assert(aMsg <> '', 'Attempt to queue an empty message'); {do not localize} + assert(ATimeout <> 0, 'Attempt to queue a message with a 0 timeout'); {do not localize} + inherited Create; + FEvent := TIdLocalEvent.Create(False, False); + FMsg := aMsg; + FTimeOut := ATimeOut; +end; + +destructor TQueuedMessage.Destroy; +begin + assert(self <> NIL); + FreeAndNil(FEvent); + inherited; +end; + +procedure TQueuedMessage.Wait; +begin + assert(Assigned(Self)); + assert(Assigned(FEvent)); + FEvent.WaitFor(FTimeOut); +end; + +{ EHL7CommunicationError } + +constructor EHL7CommunicationError.Create(AnInterfaceName, AMessage: String); +begin + // assert(AInterfaceName <> '', 'Attempt to create an exception for an unnamed interface') + // assert(AMessage <> '', 'Attempt to create an exception with an empty message') + // actually, we do not enforce either of these conditions, though they should both be true, + // since we are already raising an exception + FInterfaceName := AnInterfaceName; + if FInterfaceName <> '' then {do not localize} + begin + inherited Create('[' + AnInterfaceName + '] ' + AMessage) + end + else + begin + inherited Create(AMessage); + end +end; + +{ TIdHL7 } + +procedure TIdHL7.InitComponent; +begin + inherited; + + raise EIdException.create(RSHL7Broken); {do not localize} + + // partly redundant initialization of properties + + FIsListener := DEFAULT_IS_LISTENER; + FCommunicationMode := DEFAULT_COMM_MODE; + FTimeOut := DEFAULT_TIMEOUT; + FReconnectDelay := DEFAULT_RECONNECT_DELAY; + FReceiveTimeout := DEFAULT_RECEIVE_TIMEOUT; + FConnectionLimit := DEFAULT_CONN_LIMIT; + FIPMask := NULL_IP; + FIPRestriction := NULL_IP; + FAddress := DEFAULT_ADDRESS; + FPort := DEFAULT_PORT; + FOnReceiveMessage := NIL; + FOnConnect := NIL; + FOnDisconnect := NIL; + FObject := NIL; + + // initialise status + FStatus := IsStopped; + FStatusDesc := RSHL7StatusStopped; + + // build internal infrastructure + Flock := TIdCriticalSection.Create; + FConnCount := 0; + FServer := NIL; + FServerConn := NIL; + FClientThread := NIL; + FClient := NIL; + FMsgQueue := TList.Create; + FHndMsgQueue := TList.Create; + FWaitingForAnswer := False; + FMsgReply := ''; {do not localize} + FReplyResponse := srNone; + FWaitEvent := TIdLocalEvent.Create(False, False); +end; + +destructor TIdHL7.Destroy; +begin + assert(Assigned(Self)); + try + if Going then + begin + Stop; + end; + finally + FreeAndNil(FMsgQueue); + FreeAndNil(FHndMsgQueue); + FreeAndNil(FWaitEvent); + FreeAndNil(FLock); + inherited; + end; +end; + +{========================================================== + Property Servers + ==========================================================} + +procedure TIdHL7.SetAddress(const AValue: String); +begin + assert(Assigned(Self)); + // we don't make any assertions about AValue - will be '' if we are a server + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['Address'])); {do not localize??} + end; + FAddress := AValue; +end; + +procedure TIdHL7.SetConnectionLimit(const AValue: Word); +begin + assert(Assigned(Self)); + // no restrictions on AValue + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['ConnectionLimit'])); {do not localize??} + end; + FConnectionLimit := AValue; +end; + +procedure TIdHL7.SetIPMask(const AValue: String); +begin + assert(Assigned(Self)); + // to do: enforce that AValue is a valid Subnet mask + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['IP Mask'])); {do not localize??} + end; + FIPMask := AValue; +end; + +procedure TIdHL7.SetIPRestriction(const AValue: String); +begin + assert(Assigned(Self)); + // to do: enforce that AValue is a valid IP address range + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['IP Restriction'])); {do not localize??} + end; + FIPRestriction := AValue; +end; + +procedure TIdHL7.SetPort(const AValue: Word); +begin + assert(Assigned(Self)); + assert(AValue <> 0, 'Attempt to use Port 0 for HL7 Communications'); {do not localize} + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['Port'])); {do not localize} + end; + FPort := AValue; +end; + +procedure TIdHL7.SetReconnectDelay(const AValue: LongWord); +begin + assert(Assigned(Self)); + // any value for AValue is accepted, although this may not make sense + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['Reconnect Delay'])); {do not localize} + end; + FReconnectDelay := AValue; +end; + +procedure TIdHL7.SetTimeOut(const AValue: LongWord); +begin + assert(Assigned(Self)); + assert(FTimeout > 0, 'Attempt to configure TIdHL7 with a Timeout of 0'); {do not localize} + // we don't fucntion at all if timeout is 0, though there is circumstances where it's not relevent + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['Time Out'])); {do not localize??} + end; + FTimeOut := AValue; +end; + +procedure TIdHL7.SetCommunicationMode(const AValue: THL7CommunicationMode); +begin + assert(Assigned(Self)); + Assert((AValue >= Low(THL7CommunicationMode)) and (AValue <= High(THL7CommunicationMode)), 'Value for TIdHL7.CommunicationMode not in range'); {do not localize} + // only could arise if someone is typecasting? + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['Communication Mode'])); {do not localize} + end; + FCommunicationMode := AValue; +end; + +procedure TIdHL7.SetIsListener(const AValue: Boolean); +begin + assert(Assigned(Self)); + // AValue isn't checked + if Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWhileWorking, ['IsListener'])); {do not localize} + end; + FIsListener := AValue; +end; + +function TIdHL7.GetStatus: TIdHL7Status; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + Result := FStatus; + finally + FLock.Leave; + end; +end; + +function TIdHL7.Connected: Boolean; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + Result := FStatus = IsConnected; + finally + FLock.Leave; + end; +end; + +function TIdHL7.GetStatusDesc: String; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + Result := FStatusDesc; + finally + FLock.Leave; + end; +end; + +procedure TIdHL7.InternalSetStatus(const AStatus: TIdHL7Status; ADesc: String); +begin + assert(Assigned(Self)); + Assert((AStatus >= Low(TIdHL7Status)) and (AStatus <= High(TIdHL7Status)), 'Value for TIdHL7.CommunicationMode not in range'); {do not localize} + // ADesc is allowed to be anything at all + assert(Assigned(FLock)); + FLock.Enter; + try + FStatus := AStatus; + FStatusDesc := ADesc; + finally + FLock.Leave; + end; +end; + +{========================================================== + Application Control + ==========================================================} + +procedure TIdHL7.Start; +var + LStatus: TIdHL7Status; +begin + assert(Assigned(Self)); + LStatus := GetStatus; + if LStatus = IsUnusable then + begin + raise EHL7CommunicationError.Create(Name, RSHL7NotFailedToStop); + end; + if LStatus <> IsStopped then + begin + raise EHL7CommunicationError.Create(Name, RSHL7AlreadyStarted); + end; + if FCommunicationMode = cmUnknown then + begin + raise EHL7CommunicationError.Create(Name, RSHL7ModeNotSet); + end; + if FCommunicationMode = cmAsynchronous then + begin + if not Assigned(FOnMessageArrive) then + begin + raise EHL7CommunicationError.Create(Name, RSHL7NoAsynEvent); + end; + end; + if (FCommunicationMode = cmSynchronous) and IsListener then + begin + if not Assigned(FOnReceiveMessage) then + begin + raise EHL7CommunicationError.Create(Name, RSHL7NoSynEvent); + end; + end; + FIsServer := (FAddress = ''); + if FIsServer then + begin + StartServer + end + else + begin + StartClient; + end; + FPreStopped := False; + FWaitingForAnswer := False; +end; + +procedure TIdHL7.PreStop; + procedure JolList(l: TList); + var + i: Integer; + begin + for i := 0 to l.Count - 1 do + begin + TQueuedMessage(l[i]).FEvent.SetEvent; + end; + end; +begin + assert(Assigned(Self)); + if FCommunicationMode = cmSingleThread then + begin + assert(Assigned(FLock)); + assert(Assigned(FMsgQueue)); + assert(Assigned(FHndMsgQueue)); + FLock.Enter; + try + JolList(FMsgQueue); + JolList(FHndMsgQueue); + finally + FLock.Leave; + end; + end; + FPreStopped := True; +end; + +procedure TIdHL7.Stop; +begin + assert(Assigned(Self)); + if not Going then + begin + raise EHL7CommunicationError.Create(Name, RSHL7AlreadyStopped); + end; + + if not FPreStopped then + begin + PreStop; + IndySleep(10); // give other threads a chance to clean up + end; + + if FIsServer then + begin + StopServer + end + else + begin + StopClient; + end; +end; + + +{========================================================== + Server Connection Maintainance + ==========================================================} + +procedure TIdHL7.EnforceWaitReplyTimeout; +begin + Stop; + Start; +end; + +function TIdHL7.Going: Boolean; +var + LStatus: TIdHL7Status; +begin + assert(Assigned(Self)); + LStatus := GetStatus; + Result := (LStatus <> IsStopped) and (LStatus <> IsUnusable); +end; + +procedure TIdHL7.WaitForConnection(AMaxLength: Integer); +var + LStopWaiting: TDateTime; +begin + LStopWaiting := Now + (AMaxLength * ((1 / (24 * 60)) / (60 * 1000))); + while not Connected and (LStopWaiting > Now) do + IndySleep(50); +end; + +procedure TIdHL7.CheckSynchronousSendResult(AResult: TSendResponse; AMsg: String); +begin + case AResult of + srNone: + raise EHL7CommunicationError.Create(Name,RSHL7ErrInternalsrNone); + srError: + raise EHL7CommunicationError.Create(Name, AMsg); + srNoConnection: + raise EHL7CommunicationError.Create(Name,RSHL7ErrNotConn); + srSent: + // cause this should only be returned asynchronously + raise EHL7CommunicationError.Create(Name,RSHL7ErrInternalsrSent); + srOK:; // all ok + srTimeout: + raise EHL7CommunicationError.Create(Name,RSHL7ErrNoResponse); + else + raise EHL7CommunicationError.Create(Name,RSHL7ErrInternalUnknownVal + IntToStr(Ord(AResult))); {do not localize} + end; +end; + +procedure TIdHL7.CheckServerParameters; +begin + assert(Assigned(Self)); + if (FCommunicationMode = cmAsynchronous) or not FIsListener then + begin + FConnectionLimit := 1; + end; + + if (FPort < 1) then // though we have already ensured that this cannot happen + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7InvalidPort, [FPort])); + end; +end; + +procedure TIdHL7.StartServer; +begin + assert(Assigned(Self)); + CheckServerParameters; + FServer := TIdTCPServer.Create(NIL); + try + FServer.DefaultPort := FPort; + Fserver.OnConnect := ServerConnect; + FServer.OnExecute := ServerExecute; + FServer.OnDisconnect := ServerDisconnect; + FServer.Active := True; + InternalSetStatus(IsNotConnected, RSHL7StatusNotConnected); + except + on e: + Exception do + begin + InternalSetStatus(IsStopped, IndyFormat(RSHL7StatusFailedToStart, [e.message])); + FreeAndNil(FServer); + raise; + end; + end; +end; + +procedure TIdHL7.StopServer; +begin + assert(Assigned(Self)); + try + FServer.Active := False; + FreeAndNil(FServer); + InternalSetStatus(IsStopped, RSHL7StatusStopped); + except + on e: + Exception do + begin + // somewhat arbitrary decision: if for some reason we fail to shutdown, + // we will stubbornly refuse to work again. + InternalSetStatus(IsUnusable, IndyFormat(RSHL7StatusFailedToStop, [e.message])); + FServer := NIL; + raise + end; + end; +end; + +procedure TIdHL7.ServerConnect(AContext: TIdContext); +var + LNotify : Boolean; + LConnCount : integer; + LValid : Boolean; +begin + assert(Assigned(Self)); + assert(Assigned(AContext)); + assert(Assigned(FLock)); + FLock.Enter; + try + LNotify := FConnCount = 0; + inc(FConnCount); + LConnCount := FConnCount; + // it would be better to stop getting here in the case of an invalid connection + // cause here we drop it - nasty for the client. To be investigated later + LValid := FConnCount <= FConnectionLimit; + if (FConnCount = 1) and (FCommunicationMode <> cmAsynchronous) and not IsListener then + begin + FServerConn := AContext.Connection; + end; + if LNotify then + begin + InternalSetStatus(IsConnected, RSHL7StatusConnected); + end; + finally + FLock.Leave; + end; + if LValid then + begin + if LNotify and Assigned(FOnConnect) then + begin + FOnConnect(self); + end; + if Assigned(FOnConnCountChange) and (FConnectionLimit <> 1) then + begin + FOnConnCountChange(Self, LConnCount); + end; + end + else + begin + // Thread exceeds connection limit + AContext.Connection.Disconnect; + end; +end; + +procedure TIdHL7.ServerDisconnect(AContext: TIdContext); +var + LNotify: Boolean; + LConnCount : integer; +begin + assert(Assigned(Self)); + assert(Assigned(AContext)); + assert(Assigned(FLock)); + FLock.Enter; + try + dec(FConnCount); + LNotify := FConnCount = 0; + LConnCount := FConnCount; + if AContext.Connection = FServerConn then + begin + FServerConn := NIL; + end; + if LNotify then + begin + InternalSetStatus(IsNotConnected, RSHL7StatusNotConnected); + end; + finally + FLock.Leave; + end; + if Assigned(FOnConnCountChange) and (FConnectionLimit <> 1) then + begin + FOnConnCountChange(Self, LConnCount); + end; + if LNotify and Assigned(FOnDisconnect) then + begin + FOnDisconnect(self); + end; +end; + +procedure TIdHL7.ServerExecute(AContext: TIdContext); +var + s : String; +begin + assert(Assigned(Self)); + assert(Assigned(AContext)); + + try + // 1. prompt the network for content. + AContext.Connection.IOHandler.ReadLn(MSG_START); // throw this content away + if Assigned(AContext.Connection.IOHandler) then + begin + s := AContext.Connection.IOHandler.ReadLn(MSG_END); + if length(s) > 0 then + begin + HandleIncoming(s, AContext.Connection); + end; + end; + except + try + // well, there was some network error. We aren't sure what it + // was, and it doesn't matter for this layer. we're just going + // to make sure that we start again. + // to review: what happens to the error messages? + AContext.Connection.Disconnect; + except + end; + end; +end; + +procedure TIdHL7.DropServerConnection; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + if Assigned(FServerConn) then + FServerConn.Disconnect; + finally + FLock.Leave; + end; +end; + + +{========================================================== + Client Connection Maintainance + ==========================================================} + +procedure TIdHL7.CheckClientParameters; +begin + assert(Assigned(Self)); + if (FPort < 1) then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7InvalidPort, [FPort])); + end; +end; + +procedure TIdHL7.StartClient; +begin + assert(Assigned(Self)); + CheckClientParameters; + FClientThread := TIdHL7ClientThread.Create(self); + InternalSetStatus(isConnecting, RSHL7StatusConnecting); +end; + +procedure TIdHL7.StopClient; +var + LFinished: Boolean; + LWaitStop: LongWord; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + FClientThread.Terminate; + FClientThread.FClient.Disconnect; + FClientThread.FCloseEvent.SetEvent; + finally + FLock.Leave; + end; + LWaitStop := Ticks + 5000; + repeat + LFinished := (GetStatus = IsStopped); + if not LFinished then + begin + IndySleep(10); + end; + until LFinished or (Ticks > LWaitStop); + if GetStatus <> IsStopped then + begin + // for some reason the client failed to shutdown. We will stubbornly refuse to work again + InternalSetStatus(IsUnusable, IndyFormat(RSHL7StatusFailedToStop, [RSHL7ClientThreadNotStopped])); + end; +end; + + +procedure TIdHL7.DropClientConnection; +begin + assert(Assigned(Self)); + assert(Assigned(FLock)); + FLock.Enter; + try + if Assigned(FClientThread) and Assigned(FClientThread.FClient) then + begin + FClientThread.FClient.Disconnect; + end + else + begin + // This may happen validly because both ends are trying to drop the connection simultaineously + end; + finally + FLock.Leave; + end; +end; + +{ TIdHL7ClientThread } + +constructor TIdHL7ClientThread.Create(aOwner: TIdHL7); +begin + assert(Assigned(AOwner)); + FOwner := aOwner; + FCloseEvent := TIdLocalEvent.Create(True, False); + inherited Create(False); + FreeOnTerminate := True; +end; + +destructor TIdHL7ClientThread.Destroy; +begin + assert(Assigned(Self)); + assert(Assigned(FOwner)); + assert(Assigned(FOwner.FLock)); + FreeAndNil(FCloseEvent); + try + FOwner.FLock.Enter; + try + FOwner.FClientThread := NIL; + FOwner.InternalSetStatus(isStopped, RSHL7StatusStopped); + finally + FOwner.FLock.Leave; + end; + except + // it's really vaguely possible that the owner + // may be dead before we are. If that is the case, we blow up here. + // who cares. + end; + inherited; +end; + +procedure TIdHL7ClientThread.PollStack; +var + LBuffer: String; +begin + assert(Assigned(Self)); + LBuffer := ''; + repeat + // we don't send here - we just poll the stack for content + // if the application wants to terminate us at this point, + // then it will disconnect the socket and we will get thrown + // out + // we really don't care at all whether the disconnect was clean or ugly + + // but we do need to suppress exceptions that come from + // indy otherwise the client thread will terminate + + try + FClient.IOHandler.ReadLn(MSG_START); // we toss this content + if Assigned(FClient.IOHandler) then + begin + LBuffer := FClient.IOHandler.ReadLn(MSG_END); + if LBuffer <> '' then + begin + FOwner.HandleIncoming(LBuffer, FClient); + end; + end; + except + try + // well, there was some network error. We aren't sure what it + // was, and it doesn't matter for this layer. we're just going + // to make sure that we start again. + // to review: what happens to the error messages? + FClient.Disconnect; + except + end; + end; + until Terminated or not FClient.Connected; +end; + +procedure TIdHL7ClientThread.Execute; +var + LRecTime: TDateTime; +begin + assert(Assigned(Self)); + try + FClient := TIdTCPClient.Create(NIL); + try + FClient.Host := FOwner.FAddress; + FClient.Port := FOwner.FPort; + repeat + // try to connect. Try indefinitely but wait Owner.FReconnectDelay + // between attempts. Problems: how long does Connect take? + repeat + FOwner.InternalSetStatus(IsConnecting, rsHL7StatusConnecting); + try + FClient.Connect; + except + on e: + Exception do + begin + LRecTime := Now + ((FOwner.FReconnectDelay / 1000) * {second length} (1 / (24 * 60 * 60))); + //not we can take more liberties with the time and date output because it's only + //for human consumption (probably in a log + FOwner.InternalSetStatus(IsWaitReconnect, IndyFormat(rsHL7StatusReConnect, [DateTimeToStr(LRecTime), e.message])); {do not localize??} + end; + end; + if not Terminated and not FClient.Connected then + begin + FCloseEvent.WaitFor(FOwner.FReconnectDelay); + end; + until Terminated or FClient.Connected; + if Terminated then + begin + exit; + end; + + FOwner.FLock.Enter; + try + FOwner.FClient := FClient; + FOwner.InternalSetStatus(IsConnected, rsHL7StatusConnected); + finally + FOwner.FLock.Leave; + end; + if Assigned(FOwner.FOnConnect) then + begin + FOwner.FOnConnect(FOwner); + end; + try + PollStack; + finally + FOwner.FLock.Enter; + try + FOwner.FClient := NIL; + FOwner.InternalSetStatus(IsNotConnected, RSHL7StatusNotConnected); + finally + FOwner.FLock.Leave; + end; + if Assigned(FOwner.FOnDisconnect) then + begin + FOwner.FOnDisconnect(FOwner); + end; + end; + if not Terminated then + begin + // we got disconnected. ReconnectDelay applies. + FCloseEvent.WaitFor(FOwner.FReconnectDelay); + end; + until terminated; + finally + FreeAndNil(FClient); + end; + except + on e: + Exception do + // presumably some comms or indy related exception + // there's not really anyplace good to put this???? + end; +end; + +{========================================================== + Internal process management + ==========================================================} + +procedure TIdHL7.HandleIncoming(const AMsg : String; AConnection: TIdTCPConnection); +var + LReply: String; +begin + assert(Assigned(Self)); + assert(AMsg <> '', 'Attempt to handle an empty Message'); {do not localize} + assert(Assigned(AConnection)); + try + // process any messages in the buffer (may get more than one per packet) + if HandleMessage(AMsg, AConnection, LReply) then + begin + if LReply <> '' then + begin + AConnection.IOHandler.Write(MSG_START + LReply + MSG_END); + end; + end + else + begin + AConnection.Disconnect; + end; + except + // well, we need to suppress the exception, and force a reconnection + // we don't know why an exception has been allowed to propagate back + // to us, it shouldn't be allowed. so what we're going to do, is drop + // the connection so that we force all the network layers on both + // ends to reconnect. + // this is a waste of time if the error came from the application but + // this is not supposed to happen + try + AConnection.Disconnect; + except + // nothing - suppress + end; + end; +end; + +function TIdHL7.HandleMessage(const AMsg: String; AConn: TIdTCPConnection; var VReply: String): Boolean; +var + LQueMsg: TQueuedMessage; + LIndex: Integer; +begin + assert(Assigned(Self)); + assert(AMsg <> '', 'Attempt to handle an empty Message'); {do not localize} + assert(Assigned(FLock)); + VReply := ''; + Result := True; + try + case FCommunicationMode of + cmUnknown: + begin + raise EHL7CommunicationError.Create(Name, RSHL7ImpossibleMessage); + end; + cmAsynchronous: + begin + FOnMessageArrive(self, AConn, Amsg); + end; + cmSynchronous, cmSingleThread: + begin + if IsListener then + begin + if FCommunicationMode = cmSynchronous then + begin + Result := False; + FOnReceiveMessage(self, AConn, AMsg, Result, VReply) + end + else + begin + LQueMsg := TQueuedMessage.Create(AMsg, FReceiveTimeout); + LQueMsg._AddRef; + try + FLock.Enter; + try + FMsgQueue.Add(LQueMsg); + finally + FLock.Leave; + end; + LQueMsg.wait; + // no locking. There is potential problems here. To be reviewed + VReply := LQueMsg.FReply; + finally + FLock.Enter; + try + LIndex := FMsgQueue.IndexOf(LQueMsg); + if LIndex > -1 then + FMsgQueue.Delete(LIndex); + finally + FLock.Leave; + end; + LQueMsg._Release; + end; + end + end + else + begin + FLock.Enter; + try + if FWaitingForAnswer then + begin + FWaitingForAnswer := False; + FMsgReply := AMsg; + FReplyResponse := srOK; + if FCommunicationMode = cmSynchronous then + begin + assert(Assigned(FWaitEvent)); + FWaitEvent.SetEvent; + end; + end + else + begin + // we could have got here by timing out, but this is quite unlikely, + // since the connection will be dropped in that case. We will report + // this as a spurious message + raise EHL7CommunicationError.Create(Name, RSHL7UnexpectedMessage); + end; + finally + FLock.Leave; + end; + end + end; + else + begin + raise EHL7CommunicationError.Create(Name, RSHL7UnknownMode); + end; + end; + except + on e: + Exception do + if Assigned(FOnReceiveError) then + begin + FOnReceiveError(self, AConn, AMsg, e, VReply, Result) + end + else + begin + Result := False; + end; + end; +end; + +{========================================================== + Sending + ==========================================================} + +// this procedure is not technically thread safe. +// if the connection is disappearing when we are attempting +// to write, we can get transient access violations. Several +// strategies are available to prevent this but they significantly +// increase the scope of the locks, which costs more than it gains + +function TIdHL7.AsynchronousSend(AMsg: String): TSendResponse; +begin + assert(Assigned(self)); + assert(AMsg <> '', 'Attempt to send an empty message'); {do not localize} + assert(Assigned(FLock)); + Result := srNone; // just to suppress the compiler warning + FLock.Enter; + try + if not Going then + begin + raise EHL7CommunicationError.Create(Name, IndyFormat(RSHL7NotWorking, [RSHL7SendMessage])) + end + else if GetStatus <> isConnected then + begin + Result := srNoConnection + end + else + begin + if FIsServer then + begin + if Assigned(FServerConn) then + begin + FServerConn.IOHandler.Write(MSG_START + AMsg + MSG_END); + Result := srSent + end + else + begin + raise EHL7CommunicationError.Create(Name, RSHL7NoConnectionFound); + end + end + else + begin + FClient.IOHandler.Write(MSG_START + AMsg + MSG_END); + Result := srSent + end; + end; + finally + FLock.Leave; + end +end; + +function TIdHL7.SynchronousSend(AMsg: String; var VReply: String): TSendResponse; +begin + assert(Assigned(self)); + assert(AMsg <> '', 'Attempt to send an empty message'); {do not localize} + assert(Assigned(FLock)); + Result := srError; + FLock.Enter; + try + FWaitingForAnswer := True; + FWaitStop := Now + (FTimeOut * MILLISECOND_LENGTH); + FReplyResponse := srTimeout; + FMsgReply := ''; + finally + FLock.Leave; + end; + try + Result := AsynchronousSend(AMsg); + if Result = srSent then + begin + assert(Assigned(FWaitEvent)); + FWaitEvent.WaitFor(FTimeOut); + end; + finally + FLock.Enter; + try + FWaitingForAnswer := False; + if Result = srSent then + begin + Result := FReplyResponse; + end; + if Result = srTimeout then + begin + if FIsServer then + DropServerConnection + else + DropClientConnection; + end; + VReply := FMsgReply; + finally + FLock.Leave; + end; + end; +end; + +procedure TIdHL7.SendMessage(AMsg: String); +begin + assert(Assigned(self)); + assert(AMsg <> '', 'Attempt to send an empty message'); {do not localize} + assert(Assigned(FLock)); + if FWaitingForAnswer then + raise EHL7CommunicationError.Create(Name, RSHL7WaitForAnswer); + + FLock.Enter; + try + FWaitingForAnswer := True; + FWaitStop := Now + (FTimeOut * MILLISECOND_LENGTH); + FMsgReply := ''; + FReplyResponse := AsynchronousSend(AMsg); + finally + FLock.Leave; + end; +end; + +function TIdHL7.GetReply(var VReply: String): TSendResponse; +begin + assert(Assigned(self)); + assert(Assigned(FLock)); + FLock.Enter; + try + if FWaitingForAnswer then + begin + if FWaitStop < Now then + begin + Result := srTimeout; + VReply := ''; + FWaitingForAnswer := False; + FReplyResponse := srError; + end + else + begin + Result := srNone; + end; + end + else + begin + Result := FReplyResponse; + if Result = srSent then + begin + Result := srTimeOut; + end; + VReply := FMsgReply; + FWaitingForAnswer := False; + FReplyResponse := srError; + end; + finally + FLock.Leave; + end; +end; + +function TIdHL7.GetMessage(var VMsg: String): TObject; +begin + assert(Assigned(self)); + assert(Assigned(FLock)); + assert(Assigned(FMsgQueue)); + FLock.Enter; + try + if FMsgQueue.Count = 0 then + begin + Result := NIL; + end + else + begin + Result := FMsgQueue[0]; + TQueuedMessage(Result)._AddRef; + VMsg := TQueuedMessage(Result).FMsg; + FMsgQueue.Delete(0); + FHndMsgQueue.Add(Result); + end; + finally + FLock.Leave; + end; +end; + +procedure TIdHL7.SendReply(AMsgHnd: TObject; AReply: String); +var + qm: TQueuedMessage; +begin + assert(Assigned(self)); + assert(Assigned(AMsgHnd)); + assert(AReply <> '', 'Attempt to send an empty reply'); {do not localize} + assert(Assigned(FLock)); + FLock.Enter; + try + qm := AMsgHnd as TQueuedMessage; + qm.FReply := AReply; + qm._Release; + FHndMsgQueue.Delete(FHndMsgQueue.IndexOf(AMsgHnd)); + finally + FLock.Leave; + end; + qm.FEvent.SetEvent; +end; + +end. diff --git a/indy/Protocols/IdHMAC.pas b/indy/Protocols/IdHMAC.pas new file mode 100644 index 0000000..6019ef8 --- /dev/null +++ b/indy/Protocols/IdHMAC.pas @@ -0,0 +1,191 @@ +{ + $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$ +} +{ + HMAC specification on the NIST website + http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf +} + +unit IdHMAC; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFIPS, + IdGlobal, IdHash; + +type + TIdHMACKeyBuilder = class(TObject) + public + class function Key(const ASize: Integer) : TIdBytes; + class function IV(const ASize: Integer) : TIdBytes; + end; + + TIdHMAC = class + protected + FHashSize: Integer; // n bytes + FBlockSize: Integer; // n bytes + FKey: TIdBytes; + FHash: TIdHash; + FHashName: string; + procedure InitHash; virtual; abstract; + procedure InitKey; + procedure SetHashVars; virtual; abstract; + function HashValueNative(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1) : TIdBytes; // for now, supply in bytes + function HashValueIntF(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1) : TIdBytes; // for now, supply in bytes + function IsIntFAvail : Boolean; virtual; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; virtual; abstract; + public + constructor Create; virtual; + destructor Destroy; override; + function HashValue(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1) : TIdBytes; // for now, supply in bytes + property HashSize: Integer read FHashSize; + property BlockSize: Integer read FBlockSize; + property HashName: string read FHashName; + property Key: TIdBytes read FKey write FKey; + end; + +implementation + +uses + SysUtils; + +{ TIdHMACKeyBuilder } + +class function TIdHMACKeyBuilder.Key(const ASize: Integer): TIdBytes; +var + I: Integer; +begin + SetLength(Result, ASize); + for I := Low(Result) to High(Result) do begin + Result[I] := Byte(Random(255)); + end; +end; + +class function TIdHMACKeyBuilder.IV(const ASize: Integer): TIdBytes; +var + I: Integer; +begin + SetLength(Result, ASize); + for I := Low(Result) to High(Result) do begin + Result[I] := Byte(Random(255)); + end; +end; + +{ TIdHMAC } + +constructor TIdHMAC.Create; +begin + inherited Create; + SetLength(FKey, 0); + SetHashVars; + if IsHMACAvail then begin + FHash := nil; + end else begin + InitHash; + end; +end; + +destructor TIdHMAC.Destroy; +begin + FreeAndNil(FHash); + inherited Destroy; +end; + +function TIdHMAC.HashValueNative(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1) : TIdBytes; // for now, supply in bytes +const + CInnerPad : Byte = $36; + COuterPad : Byte = $5C; +var + TempBuffer1: TIdBytes; + TempBuffer2: TIdBytes; + LKey: TIdBytes; + I: Integer; +begin + InitKey; + LKey := Copy(FKey, 0, MaxInt); + SetLength(LKey, FBlockSize); + SetLength(TempBuffer1, FBlockSize + Length(ABuffer)); + for I := Low(LKey) to High(LKey) do begin + TempBuffer1[I] := LKey[I] xor CInnerPad; + end; + CopyTIdBytes(ABuffer, 0, TempBuffer1, Length(LKey), Length(ABuffer)); + TempBuffer2 := FHash.HashBytes(TempBuffer1); + SetLength(TempBuffer1, 0); + SetLength(TempBuffer1, FBlockSize + FHashSize); + for I := Low(LKey) to High(LKey) do begin + TempBuffer1[I] := LKey[I] xor COuterPad; + end; + CopyTIdBytes(TempBuffer2, 0, TempBuffer1, Length(LKey), Length(TempBuffer2)); + Result := FHash.HashBytes(TempBuffer1); + SetLength(TempBuffer1, 0); + SetLength(TempBuffer2, 0); + SetLength(LKey, 0); + if ATruncateTo > -1 then begin + SetLength(Result, ATruncateTo); + end; +end; + +function TIdHMAC.HashValueIntF(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1) : TIdBytes; // for now, supply in bytes +var + LCtx : TIdHMACIntCtx; +begin + if FKey = nil then begin + FKey := TIdHMACKeyBuilder.Key(FHashSize); + end; + LCtx := InitIntFInst(FKey); + try + UpdateHMACInst(LCtx,ABuffer); + finally + Result := FinalHMACInst(LCtx); + end; + if (ATruncateTo >-1) and (ATruncateTo < Length(Result)) then begin + SetLength(Result, ATruncateTo); + end; +end; + +function TIdHMAC.HashValue(const ABuffer: TIdBytes; const ATruncateTo: Integer = -1): TIdBytes; // for now, supply in bytes +begin + if IsIntFAvail then begin + Result := HashValueIntF(ABuffer,ATruncateTo); + end else begin + Result := HashValueNative(ABuffer,ATruncateTo); + end; +end; + +procedure TIdHMAC.InitKey; +begin + if FKey = nil then begin + FKey := TIdHMACKeyBuilder.Key(FHashSize); + end + else if Length(FKey) > FBlockSize then begin + FKey := FHash.HashBytes(FKey); + end; +end; + +function TIdHMAC.IsIntFAvail: Boolean; +begin + Result := IsHMACAvail; +end; + +initialization + Randomize; + +end. diff --git a/indy/Protocols/IdHMACMD5.pas b/indy/Protocols/IdHMACMD5.pas new file mode 100644 index 0000000..5ce7a61 --- /dev/null +++ b/indy/Protocols/IdHMACMD5.pas @@ -0,0 +1,69 @@ +{ + $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$ +} +{ + HMAC specification on the NIST website + http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf +} + +unit IdHMACMD5; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdFIPS, + IdGlobal, + IdHash, IdHashMessageDigest, IdHMAC; + +type + TIdHMACMD5 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + +implementation + +{ TIdHMACMD5 } + +procedure TIdHMACMD5.InitHash; +begin + FHash := TIdHashMessageDigest5.Create; +end; + +function TIdHMACMD5.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACMD5HashInst(AKey); +end; + +function TIdHMACMD5.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACMD5Avail; +end; + +procedure TIdHMACMD5.SetHashVars; +begin + FHashName := 'MD5'; + FHashSize := 16; + FBlockSize := 64; +end; + +end. diff --git a/indy/Protocols/IdHMACSHA1.pas b/indy/Protocols/IdHMACSHA1.pas new file mode 100644 index 0000000..0652102 --- /dev/null +++ b/indy/Protocols/IdHMACSHA1.pas @@ -0,0 +1,199 @@ +{ + $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$ +} +{ + HMAC specification on the NIST website + http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf +} + +unit IdHMACSHA1; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdFIPS, + IdGlobal, + IdHash, IdHashSHA, IdHMAC; + +type + TIdHMACSHA1 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + {$IFNDEF DOTNET} + TIdHMACSHA224 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + {$ENDIF} + TIdHMACSHA256 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + TIdHMACSHA384 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + TIdHMACSHA512 = class(TIdHMAC) + protected + procedure SetHashVars; override; + function IsIntFAvail : Boolean; override; + function InitIntFInst(const AKey : TIdBytes) : TIdHMACIntCtx; override; + procedure InitHash; override; + end; + +implementation + +{ TIdHMACSHA1 } + +procedure TIdHMACSHA1.InitHash; +begin + FHash := TIdHashSHA1.Create; +end; + +function TIdHMACSHA1.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACSHA1HashInst(AKey); +end; + +function TIdHMACSHA1.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACSHA1Avail; +end; + +procedure TIdHMACSHA1.SetHashVars; +begin + FHashSize := 20; + FBlockSize := 64; + FHashName := 'SHA1'; +end; + +{ TIdHMACSHA224 } + + {$IFNDEF DOTNET} +procedure TIdHMACSHA224.InitHash; +begin + FHash := TIdHashSHA224.Create; +end; + +function TIdHMACSHA224.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACSHA224HashInst(AKey); +end; + +function TIdHMACSHA224.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACSHA224Avail; +end; + +procedure TIdHMACSHA224.SetHashVars; +begin + FHashSize := 28; + FBlockSize := 64; + FHashName := 'SHA224'; +end; + +{$ENDIF} + +{ TIdHMACSHA256 } + +procedure TIdHMACSHA256.InitHash; +begin + FHash := TIdHashSHA256.Create; +end; + +function TIdHMACSHA256.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACSHA256HashInst(AKey); +end; + +function TIdHMACSHA256.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACSHA256Avail; +end; + +procedure TIdHMACSHA256.SetHashVars; +begin + FHashSize := 32; + FBlockSize := 64; + FHashName := 'SHA256'; +end; + +{ TIdHMACSHA384 } + +procedure TIdHMACSHA384.InitHash; +begin + FHash := TIdHashSHA384.Create; +end; + +function TIdHMACSHA384.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACSHA384HashInst(AKey); +end; + +function TIdHMACSHA384.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACSHA384Avail; +end; + +procedure TIdHMACSHA384.SetHashVars; +begin + FHashSize := 48; + FBlockSize := 128; + FHashName := 'SHA384'; +end; + +{ TIdHMACSHA512 } + +procedure TIdHMACSHA512.InitHash; +begin + + FHash := TIdHashSHA512.Create; +end; + +function TIdHMACSHA512.InitIntFInst(const AKey: TIdBytes): TIdHMACIntCtx; +begin + Result := GetHMACSHA512HashInst(AKey); +end; + +function TIdHMACSHA512.IsIntFAvail: Boolean; +begin + Result := inherited IsIntFAvail and IsHMACSHA512Avail; +end; + +procedure TIdHMACSHA512.SetHashVars; +begin + FHashSize := 64; + FBlockSize := 128; + FHashName := 'SHA512'; +end; + +end. diff --git a/indy/Protocols/IdHTTP.pas b/indy/Protocols/IdHTTP.pas new file mode 100644 index 0000000..dc2de92 --- /dev/null +++ b/indy/Protocols/IdHTTP.pas @@ -0,0 +1,3059 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.65 3/5/2005 3:33:52 PM JPMugaas + Fix for some compiler warnings having to do with TStream.Read being platform + specific. This was fixed by changing the Compressor API to use TIdStreamVCL + instead of TStream. I also made appropriate adjustments to other units for + this. + + Rev 1.64 2/13/2005 3:09:20 PM DSiders + Modified TIdCustomHTTP.PrepareRequest to free the local URI instance if an + exception occurs in the method. (try...finally) + + Rev 1.63 2/11/05 11:29:34 AM RLebeau + Removed compiler warning + + Rev 1.62 2/9/05 2:12:08 AM RLebeau + Fixes for Compiler errors + + Rev 1.61 2/8/05 6:43:42 PM RLebeau + Added OnHeaderAvailable event + + Rev 1.60 1/11/05 1:25:08 AM RLebeau + More changes to SetHostAndPort() + + Rev 1.59 1/6/05 2:28:52 PM RLebeau + Fix for SetHostAndPort() not using its local variables properly + + Rev 1.58 06/01/2005 22:23:04 CCostelloe + Bug fix (typo, gizp instead of gzip) + + Rev 1.57 05/12/2004 23:10:58 CCostelloe + Recoded fix to suit Delphi < 7 + + Rev 1.56 30/11/2004 23:46:12 CCostelloe + Bug fix for SSL connections giving a "Connection closed gracefully" exception + and requested page not getting returned (IOHandler.Response is empty) + + Rev 1.55 25/11/2004 21:28:06 CCostelloe + Bug fix for POSTing fields that have the same name + + Rev 1.54 10/26/2004 10:13:24 PM JPMugaas + Updated refs. + + Rev 1.53 7/16/04 1:19:20 AM RLebeau + Fix for compiler error + + Rev 1.52 7/15/04 8:19:30 PM RLebeau + Updated TIdHTTPProtocol.ProcessResponse() to treat 302 redirects like 303. + + Updated TIdHTTPProtocol.BuildAndSendRequest() to use a try...except block + + Rev 1.51 6/17/2004 8:30:04 AM DSiders + TIdCustomHTTP modified: + - Fixed error in AuthRetries property reading wrong member var. + - Added AuthProxyRetries and MaxAuthRetries properties to public interface. + + TIdHTTP modified to publish AuthRetries, AuthProxyRetries, and MaxAuthRetries. + + TIdHTTPProtocol.ProcessResponse modified to use public properties + AuthRetries, AuthProxyRetries, and MaxAutrhRetries. + + Rev 1.50 2004.05.20 11:36:46 AM czhower + IdStreamVCL + + Rev 1.49 4/28/04 1:45:26 PM RLebeau + Updated TIdCustomHTTP.SetRequestParams() to strip off the trailing CRLF + before encoding rather than afterwards + + Rev 1.48 2004.04.07 11:18:08 PM czhower + Bug and naming fix. + + Rev 1.47 7/4/2004 6:00:02 PM SGrobety + Reformatted to match project guidelines + + Rev 1.46 7/4/2004 4:58:24 PM SGrobety + Reformatted to match project guidelines + + Rev 1.45 6/4/2004 5:16:40 PM SGrobety + Added AMaxHeaderCount: integer parameter to TIdHTTPProtocol.RetrieveHeaders + and MaxHeaderLines property to TIdCustomHTTP (default to 255) + + Rev 1.44 2004.03.06 10:39:52 PM czhower + Removed duplicate code + + Rev 1.43 2004.03.06 8:56:30 PM czhower + -Change to disconnect + -Addition of DisconnectNotifyPeer + -WriteHeader now write bufers + + Rev 1.42 3/3/2004 5:58:00 AM JPMugaas + Some IFDEF excluses were removed because the functionality is now in DotNET. + + Rev 1.41 2004.02.23 9:33:12 PM czhower + Now can optionally ignore response codes for exceptions. + + Rev 1.40 2/15/2004 6:34:02 AM JPMugaas + Fix for where I broke the HTTP client with a parameter change in the GZip + decompress method. + + Rev 1.39 2004.02.03 5:43:44 PM czhower + Name changes + + Rev 1.38 2004.02.03 2:12:10 PM czhower + $I path change + + Rev 1.37 2004.01.27 11:41:18 PM czhower + Removed const arguments + + Rev 1.35 24/01/2004 19:22:34 CCostelloe + Cleaned up warnings + + Rev 1.34 2004.01.22 5:29:02 PM czhower + TextIsSame + + Rev 1.33 2004.01.21 1:04:50 PM czhower + InitComponenet + + Rev 1.32 1/2/2004 11:41:48 AM BGooijen + Enabled IPv6 support + + Rev 1.31 22/11/2003 12:04:28 AM GGrieve + Add support for HTTP status code 303 + + Rev 1.30 10/25/2003 06:51:58 AM JPMugaas + Updated for new API changes and tried to restore some functionality. + + Rev 1.29 2003.10.24 10:43:08 AM czhower + TIdSTream to dos + + Rev 1.28 24/10/2003 10:58:40 AM SGrobety + Made authentication work even if no OnAnthenticate envent handler present + + Rev 1.27 10/18/2003 1:53:10 PM BGooijen + Added include + + Rev 1.26 10/17/2003 12:08:48 AM DSiders + Added localization comments. + + Rev 1.25 2003.10.14 1:27:52 PM czhower + DotNet + + Rev 1.24 10/7/2003 11:33:54 PM GGrieve + Get works under DotNet + + Rev 1.23 10/7/2003 10:07:04 PM GGrieve + Get HTTP compiling for DotNet + + Rev 1.22 10/4/2003 9:15:58 PM GGrieve + fix to compile + + Rev 1.21 9/26/2003 01:41:48 PM JPMugaas + Fix for problem wihere "identity" was being added more than once to the + accepted encoding contents. + + Rev 1.20 9/14/2003 07:54:20 PM JPMugaas + Published the Compressor property. + + Rev 1.19 7/30/2003 05:34:22 AM JPMugaas + Fix for bug where decompression was not done if the Content Length was + specified. I found that at http://www.news.com. + Added Identity to the content encoding to be consistant with Opera. Identity + is the default Accept-Encoding (RFC 2616). + + Rev 1.18 7/13/2003 10:57:28 PM BGooijen + Fixed GZip and Deflate decoding + + Rev 1.17 7/13/2003 11:29:06 AM JPMugaas + Made sure some GZIP decompression stub code is in IdHTTP. + + Rev 1.15 10.7.2003 . 21:03:02 DBondzhev + Fixed NTML proxy authorization + + Rev 1.14 6/19/2003 02:36:56 PM JPMugaas + Removed a connected check and it seems to work better that way. + + Rev 1.13 6/5/2003 04:53:54 AM JPMugaas + Reworkings and minor changes for new Reply exception framework. + + Rev 1.12 4/30/2003 01:47:24 PM JPMugaas + Added TODO concerning a ConnectTimeout. + + Rev 1.11 4/2/2003 3:18:30 PM BGooijen + fixed av when retrieving an url when no iohandler was assigned + + Rev 1.10 3/26/2003 5:13:40 PM BGooijen + TIdSSLIOHandlerSocketBase.URIToCheck is now set + + Rev 1.9 3/13/2003 11:05:26 AM JPMugaas + Now should work with 3rd party vendor SSL IOHandlers. + + Rev 1.8 3/11/2003 10:14:52 PM BGooijen + Undid the stripping of the CR + + Rev 1.7 2/27/2003 2:04:26 PM BGooijen + If any call to iohandler.readln returns a CR at the end, it is removed now. + + Rev 1.6 2/26/2003 11:50:08 AM BGooijen + things were messed up in TIdHTTPProtocol.RetrieveHeaders, because the call to + readln doesn't strip the CR at the end (terminator=LF), therefore the end of + the header was not found. + + Rev 1.5 2/26/2003 11:42:46 AM BGooijen + changed ReadLn (IOerror 6) to IOHandler.ReadLn + + Rev 1.4 2/4/2003 6:30:44 PM BGooijen + Re-enabled SSL-support + + Rev 1.3 1/17/2003 04:14:42 PM JPMugaas + Fixed warnings. + + Rev 1.2 12/7/2002 05:32:16 PM JPMugaas + Now compiles with destination removed. + + Rev 1.1 12/6/2002 05:29:52 PM JPMugaas + Now decend from TIdTCPClientCustom instead of TIdTCPClient. + + Rev 1.0 11/13/2002 07:54:12 AM JPMugaas + +2001-Nov Nick Panteleeff + - Authentication and POST parameter extentsions + +2001-Sept Doychin Bondzhev + - New internal design and new Authentication procedures. + - Bug fixes and new features in few other supporting components + +2001-Jul-7 Doychin Bondzhev + - new property AllowCookie + - There is no more ExtraHeders property in Request/Response. Raw headers is used for that purpose. + +2001-Jul-1 Doychin Bondzhev + - SSL support is up again - Thanks to Gregor + +2001-Jun-17 Doychin Bondzhev + - New unit IdHTTPHeaderInfo.pas that contains the + TIdHeaderInfo(TIdEntytiHeaderInfo, TIdRequestHeaderInfo and TIdResponseHeaderInfo) + - Still in development and not verry well tested + By default when there is no authorization object associated with HTTP compoenet and there is user name and password + HTTP component creates and instance of TIdBasicAuthentication class. This behaivor is for both web server and proxy server + authorizations + +2001-Apr-17 Doychin Bondzhev + - Added OnProxyAuthorization event. This event is called on 407 response from the HTTP Proxy. + - Added 2 new properties in TIdHeaderInfo + property AuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme + requested by the web server + property ProxyAuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme + requested by the proxy server + - Now the component authomaticly reconginizes the requested authorization scheme and it supports Basic like before and has been + extend to support Digest authorization + +2001-Mar-31 Doychin Bondzhev + - If there is no CookieManager it does not support cookies. + +2001-Feb-18 Doychin Bondzhev + - Added OnAuthorization event. This event is called on 401 response from the HTTP server. + This can be used to ask the user program to supply user name and password in order to acces + the requested resource + +2001-Feb-02 Doychin Bondzhev + - Added Cookie support and relative paths on redirect + +2000-Jul-25 Hadi Hariri + - Overloaded POst and moved clearing to disconect. + +2000-June-22 Hadi Hariri + - Added Proxy support. + +2000-June-10 Hadi Hariri + - Added Chunk-Encoding support and HTTP version number. Some additional + improvements. + +2000-May-23 J. Peter Mugaas + -added redirect capability and supporting properties. Redirect is optional + and is set with HandleRedirects. Redirection is limited to RedirectMaximum + to prevent stack overflow due to recursion and to prevent redirects between + two places which would cause this to go on to infinity. + +2000-May-22 J. Peter Mugaas + -adjusted code for servers which returned LF instead of EOL + -Headers are now retreived before an exception is raised. This + also facilitates server redirection where the server tells the client to + get a document from another location. + +2000-May-01 Hadi Hariri + -Converted to Mercury + +2000-May-01 Hadi Hariri + -Added PostFromStream and some clean up + +2000-Apr-10 Hadi Hariri + -Re-done quite a few things and fixed GET bugs and finished POST method. + +2000-Jan-13 MTL + -Moved to the New Palette Scheme + +2000-Jan-08 MTL + -Cleaned up a few compiler hints during 7.038 build + +1999-Dec-10 Hadi Hariri + -Started. +} + +unit IdHTTP; + +{ + Implementation of the HTTP protcol as specified in RFC 2616, 2109, 2965. + (See NOTE below for details of what is exactly implemented) + + Author: Hadi Hariri (hadi@urusoft.com) + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. + + Initials: Hadi Hariri - HH +} +{ + TODO: Figure out what to do with ConnectTimeout. + Ideally, that should be in the core and is not the same as a read Timeout. +} + +interface + +{$I IdCompilerDefines.inc} + +uses + Classes, + IdException, IdExceptionCore, IdAssignedNumbers, IdHeaderList, IdHTTPHeaderInfo, IdReplyRFC, + IdSSL, IdZLibCompressorBase, + IdTCPClient, IdURI, IdCookie, IdCookieManager, IdAuthentication, IdAuthenticationManager, + IdMultipartFormData, IdGlobal, IdBaseComponent, IdUriUtils; + +type + // TO DOCUMENTATION TEAM + // ------------------------ + // For internal use. No need of documentation + // hmConnect - Used to connect trought CERN proxy to SSL enabled sites. + TIdHTTPMethod = string; + +const + Id_HTTPMethodHead = 'HEAD'; + Id_HTTPMethodGet = 'GET'; + Id_HTTPMethodPost = 'POST'; + Id_HTTPMethodOptions = 'OPTIONS'; + Id_HTTPMethodTrace = 'TRACE'; + Id_HTTPMethodPut = 'PUT'; + Id_HTTPMethodDelete = 'DELETE'; + Id_HTTPMethodConnect = 'CONNECT'; + Id_HTTPMethodPatch = 'PATCH'; + //(hmHead, hmGet, hmPost, hmOptions, hmTrace, hmPut, hmDelete, hmConnect, hmPatch); + +type + TIdHTTPWhatsNext = (wnGoToURL, wnJustExit, wnDontKnow, wnReadAndGo, wnAuthRequest); + TIdHTTPConnectionType = (ctNormal, ctSSL, ctProxy, ctSSLProxy); + + // Protocol options + TIdHTTPOption = (hoInProcessAuth, hoKeepOrigProtocol, hoForceEncodeParams, + hoNonSSLProxyUseConnectVerb, hoNoParseMetaHTTPEquiv, hoWaitForUnexpectedData, + hoTreat302Like303, hoNoProtocolErrorException, hoNoReadMultipartMIME); + TIdHTTPOptions = set of TIdHTTPOption; + + // Must be documented + TIdHTTPProtocolVersion = (pv1_0, pv1_1); + + TIdHTTPOnRedirectEvent = procedure(Sender: TObject; var dest: string; var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod) of object; + TIdHTTPOnHeadersAvailable = procedure(Sender: TObject; AHeaders: TIdHeaderList; var VContinue: Boolean) of object; + TIdOnSelectAuthorization = procedure(Sender: TObject; var AuthenticationClass: TIdAuthenticationClass; AuthInfo: TIdHeaderList) of object; + TIdOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: Boolean) of object; + // TIdProxyOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: boolean) of object; + +const + Id_TIdHTTP_ProtocolVersion = pv1_1; + Id_TIdHTTP_RedirectMax = 15; + Id_TIdHTTP_MaxHeaderLines = 255; + Id_TIdHTTP_HandleRedirects = False; + Id_TIdHTTP_MaxAuthRetries = 3; + +type + TIdCustomHTTP = class; + + // TO DOCUMENTATION TEAM + // ------------------------ + // The following classes are used internally and no need of documentation + // Only TIdHTTP must be documented + // + TIdHTTPResponse = class(TIdResponseHeaderInfo) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FHTTP: TIdCustomHTTP; + FResponseCode: Integer; + FResponseText: string; + FKeepAlive: Boolean; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FContentStream: TStream; + FResponseVersion: TIdHTTPProtocolVersion; + FMetaHTTPEquiv : TIdMetaHTTPEquiv; + // + function GetKeepAlive: Boolean; + function GetResponseCode: Integer; + + procedure SetResponseText(const AValue: String); + procedure ProcessMetaHTTPEquiv; + public + constructor Create(AHTTP: TIdCustomHTTP); reintroduce; virtual; + destructor Destroy; override; + procedure Clear; override; + property KeepAlive: Boolean read GetKeepAlive write FKeepAlive; + property MetaHTTPEquiv: TIdMetaHTTPEquiv read FMetaHTTPEquiv; + property ResponseText: string read FResponseText write SetResponseText; + property ResponseCode: Integer read GetResponseCode write FResponseCode; + property ResponseVersion: TIdHTTPProtocolVersion read FResponseVersion write FResponseVersion; + property ContentStream: TStream read FContentStream write FContentStream; + end; + + TIdHTTPRequest = class(TIdRequestHeaderInfo) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FHTTP: TIdCustomHTTP; + FURL: string; + FMethod: TIdHTTPMethod; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FSourceStream: TStream; + FUseProxy: TIdHTTPConnectionType; + FIPVersion: TIdIPVersion; + FDestination: string; + public + constructor Create(AHTTP: TIdCustomHTTP); reintroduce; virtual; + property URL: string read FURL write FURL; + property Method: TIdHTTPMethod read FMethod write FMethod; + property Source: TStream read FSourceStream write FSourceStream; + property UseProxy: TIdHTTPConnectionType read FUseProxy; + property IPVersion: TIdIPversion read FIPVersion write FIPVersion; + property Destination: string read FDestination write FDestination; + end; + + TIdHTTPProtocol = class(TObject) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FHTTP: TIdCustomHTTP; + FRequest: TIdHTTPRequest; + FResponse: TIdHTTPResponse; + public + constructor Create(AConnection: TIdCustomHTTP); + destructor Destroy; override; + function ProcessResponse(AIgnoreReplies: array of Int16): TIdHTTPWhatsNext; + procedure BuildAndSendRequest(AURI: TIdURI); + procedure RetrieveHeaders(AMaxHeaderCount: integer); + // + property Request: TIdHTTPRequest read FRequest; + property Response: TIdHTTPResponse read FResponse; + end; + + TIdCustomHTTP = class(TIdTCPClientCustom) + protected + {Retries counter for WWW authorization} + FAuthRetries: Integer; + {Retries counter for proxy authorization} + FAuthProxyRetries: Integer; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FCookieManager: TIdCookieManager; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FCompressor : TIdZLibCompressorBase; + FImplicitCookieManager: Boolean; + {Max retries for authorization} + FMaxAuthRetries: Integer; + FMaxHeaderLines: integer; + FAllowCookies: Boolean; + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FAuthenticationManager: TIdAuthenticationManager; + FProtocolVersion: TIdHTTPProtocolVersion; + + {this is an internal counter for redirects} + FRedirectCount: Integer; + FRedirectMax: Integer; + FHandleRedirects: Boolean; + FOptions: TIdHTTPOptions; + FURI: TIdURI; + FHTTPProto: TIdHTTPProtocol; + FProxyParameters: TIdProxyConnectionInfo; + // + FOnHeadersAvailable: TIdHTTPOnHeadersAvailable; + FOnRedirect: TIdHTTPOnRedirectEvent; + FOnSelectAuthorization: TIdOnSelectAuthorization; + FOnSelectProxyAuthorization: TIdOnSelectAuthorization; + FOnAuthorization: TIdOnAuthorization; + FOnProxyAuthorization: TIdOnAuthorization; + // +{ + procedure SetHost(const Value: string); override; + procedure SetPort(const Value: integer); override; +} + procedure DoRequest(const AMethod: TIdHTTPMethod; AURL: string; + ASource, AResponseContent: TStream; AIgnoreReplies: array of Int16); virtual; + function CreateProtocol: TIdHTTPProtocol; virtual; + procedure InitComponent; override; + function InternalReadLn: String; + procedure SetAuthenticationManager(Value: TIdAuthenticationManager); + procedure SetCookieManager(ACookieManager: TIdCookieManager); + procedure SetAllowCookies(AValue: Boolean); + function GetResponseCode: Integer; + function GetResponseText: string; + function DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual; + function DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual; + function DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean; virtual; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); + function SetHostAndPort: TIdHTTPConnectionType; + procedure SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest); + procedure ReadResult(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); + procedure PrepareRequest(ARequest: TIdHTTPRequest); + procedure ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); + function GetResponse: TIdHTTPResponse; + function GetRequest: TIdHTTPRequest; + function GetMetaHTTPEquiv: TIdMetaHTTPEquiv; + procedure SetRequest(Value: TIdHTTPRequest); + procedure SetProxyParams(AValue: TIdProxyConnectionInfo); + + function SetRequestParams(ASource: TStrings; AByteEncoding: IIdTextEncoding + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding{$ENDIF} + ): string; + + procedure CheckAndConnect(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); + procedure DoOnDisconnected; override; + + //misc internal stuff + function ResponseCharset: String; + public + destructor Destroy; override; + + procedure Delete(AURL: string; AResponseContent: TStream); overload; + function Delete(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + + procedure Options(AURL: string; AResponseContent: TStream); overload; + function Options(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + + procedure Get(AURL: string; AResponseContent: TStream); overload; + procedure Get(AURL: string; AResponseContent: TStream; AIgnoreReplies: array of Int16); overload; + function Get(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function Get(AURL: string; AIgnoreReplies: array of Int16 + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + + procedure Trace(AURL: string; AResponseContent: TStream); overload; + function Trace(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + procedure Head(AURL: string); + + function Post(AURL: string; const ASourceFile: String + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function Post(AURL: string; ASource: TStrings; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil; ADestEncoding: IIdTextEncoding = nil{$ENDIF}): string; overload; + function Post(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + function Post(AURL: string; ASource: TIdMultiPartFormDataStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + + procedure Post(AURL: string; const ASourceFile: String; AResponseContent: TStream); overload; + procedure Post(AURL: string; ASource: TStrings; AResponseContent: TStream; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}); overload; + procedure Post(AURL: string; ASource, AResponseContent: TStream); overload; + procedure Post(AURL: string; ASource: TIdMultiPartFormDataStream; AResponseContent: TStream); overload; + + function Put(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + procedure Put(AURL: string; ASource, AResponseContent: TStream); overload; + + procedure Patch(AURL: string; ASource, AResponseContent: TStream); overload; + function Patch(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; overload; + + {This is an object that can compress and decompress HTTP Deflate encoding} + property Compressor : TIdZLibCompressorBase read FCompressor write FCompressor; + + {This is the response code number such as 404 for File not Found} + property ResponseCode: Integer read GetResponseCode; + {This is the text of the message such as "404 File Not Found here Sorry"} + property ResponseText: string read GetResponseText; + property Response: TIdHTTPResponse read GetResponse; + property MetaHTTPEquiv: TIdMetaHTTPEquiv read GetMetaHTTPEquiv; + { This is the last processed URL } + property URL: TIdURI read FURI; + // number of retry attempts for Authentication + property AuthRetries: Integer read FAuthRetries; + property AuthProxyRetries: Integer read FAuthProxyRetries; + // maximum number of Authentication retries permitted + property MaxAuthRetries: Integer read FMaxAuthRetries write FMaxAuthRetries default Id_TIdHTTP_MaxAuthRetries; + property AllowCookies: Boolean read FAllowCookies write SetAllowCookies; + {Do we handle redirect requests or simply raise an exception and let the + developer deal with it} + property HandleRedirects: Boolean read FHandleRedirects write FHandleRedirects default Id_TIdHTTP_HandleRedirects; + property ProtocolVersion: TIdHTTPProtocolVersion read FProtocolVersion write FProtocolVersion default Id_TIdHTTP_ProtocolVersion; + //how many redirects were made in the last request + property RedirectCount: Integer read FRedirectCount; + {This is the maximum number of redirects we wish to handle, we limit this + to prevent stack overflow due to recursion. Recursion is safe ONLY if + prevented for continuing to infinity} + property RedirectMaximum: Integer read FRedirectMax write FRedirectMax default Id_TIdHTTP_RedirectMax; + // S.G. 6/4/2004: This is to prevent the server from responding with too many header lines + property MaxHeaderLines: integer read FMaxHeaderLines write FMaxHeaderLines default Id_TIdHTTP_MaxHeaderLines; + property ProxyParams: TIdProxyConnectionInfo read FProxyParameters write SetProxyParams; + property Request: TIdHTTPRequest read GetRequest write SetRequest; + property HTTPOptions: TIdHTTPOptions read FOptions write FOptions; + // + property OnHeadersAvailable: TIdHTTPOnHeadersAvailable read FOnHeadersAvailable write FOnHeadersAvailable; + // Fired when a rediretion is requested. + property OnRedirect: TIdHTTPOnRedirectEvent read FOnRedirect write FOnRedirect; + property OnSelectAuthorization: TIdOnSelectAuthorization read FOnSelectAuthorization write FOnSelectAuthorization; + property OnSelectProxyAuthorization: TIdOnSelectAuthorization read FOnSelectProxyAuthorization write FOnSelectProxyAuthorization; + property OnAuthorization: TIdOnAuthorization read FOnAuthorization write FOnAuthorization; + property OnProxyAuthorization: TIdOnAuthorization read FOnProxyAuthorization write FOnProxyAuthorization; + // Cookie stuff + property CookieManager: TIdCookieManager read FCookieManager write SetCookieManager; + // + property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager; + end; + + TIdHTTP = class(TIdCustomHTTP) + published + // number of Authentication retries permitted + property MaxAuthRetries; + property AllowCookies; + { Do we handle redirect requests or simply raise an exception and let the + developer deal with it } + property HandleRedirects; + property ProtocolVersion; + { This is the maximum number of redirects we wish to handle, we limit this + to prevent stack overflow due to recursion. Recursion is safe ONLY if + prevented for continuing to infinity } + property RedirectMaximum; + property ProxyParams; + property Request; + property HTTPOptions; + // + property OnHeadersAvailable; + // Fired when a rediretion is requested. + property OnRedirect; + property OnSelectAuthorization; + property OnSelectProxyAuthorization; + property OnAuthorization; + property OnProxyAuthorization; + // property Host; + // property Port default IdPORT_HTTP; + // Cookie stuff + property CookieManager; + // property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager; + // ZLib compression library object for use with deflate and gzip encoding + property Compressor; + end; + + EIdUnknownProtocol = class(EIdException); + EIdHTTPProtocolException = class( EIdReplyRFCError ) + protected + FErrorMessage: string; + public + constructor CreateError(const anErrCode: Integer; const asReplyMessage: string; + const asErrorMessage: string); reintroduce; virtual; + property ErrorMessage: string read FErrorMessage; + end; + +implementation + +uses + SysUtils, + IdAllAuthentications, IdComponent, IdCoderMIME, IdTCPConnection, + IdResourceStringsCore, IdResourceStringsProtocols, IdGlobalProtocols, + IdIOHandler, IdIOHandlerSocket; + +const + ProtocolVersionString: array[TIdHTTPProtocolVersion] of string = ('1.0', '1.1'); {do not localize} + +{ EIdHTTPProtocolException } + +constructor EIdHTTPProtocolException.CreateError(const anErrCode: Integer; + const asReplyMessage: string; const asErrorMessage: string); +begin + inherited CreateError(anErrCode, asReplyMessage); + FErrorMessage := asErrorMessage; +end; + +{ TIdHTTP } + +function IsContentTypeHtml(AInfo: TIdEntityHeaderInfo) : Boolean; +begin + Result := IsHeaderMediaTypes(AInfo.ContentType, ['text/html', 'text/html-sandboxed','application/xhtml+xml']); {do not localize} +end; + +function IsContentTypeAppXml(AInfo: TIdEntityHeaderInfo) : Boolean; +begin + Result := IsHeaderMediaTypes(AInfo.ContentType, + ['application/xml', 'application/xml-external-parsed-entity', 'application/xml-dtd'] {do not localize} + ); + if not Result then + begin + Result := not IsHeaderMediaType(AInfo.ContentType, 'text'); {do not localize} + if Result then begin + Result := TextEndsWith(ExtractHeaderMediaSubType(AInfo.ContentType), '+xml') {do not localize} + end; + end; +end; + +destructor TIdCustomHTTP.Destroy; +begin + FreeAndNil(FHTTPProto); + FreeAndNil(FURI); + FreeAndNil(FProxyParameters); + SetCookieManager(nil); + inherited Destroy; +end; + +procedure TIdCustomHTTP.Delete(AURL: string; AResponseContent: TStream); +begin + DoRequest(Id_HTTPMethodDelete, AURL, nil, AResponseContent, []); +end; + +function TIdCustomHTTP.Delete(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + DoRequest(Id_HTTPMethodDelete, AURL, nil, LStream, []); + LStream.Position := 0; + Result := ReadStringAsCharset(LStream, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LStream); + end; +end; + +procedure TIdCustomHTTP.Options(AURL: string; AResponseContent: TStream); +begin + DoRequest(Id_HTTPMethodOptions, AURL, nil, AResponseContent, []); +end; + +function TIdCustomHTTP.Options(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + DoRequest(Id_HTTPMethodOptions, AURL, nil, LStream, []); + LStream.Position := 0; + Result := ReadStringAsCharset(LStream, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LStream); + end; +end; + +procedure TIdCustomHTTP.Get(AURL: string; AResponseContent: TStream); +begin + Get(AURL, AResponseContent, []); +end; + +procedure TIdCustomHTTP.Trace(AURL: string; AResponseContent: TStream); +begin + DoRequest(Id_HTTPMethodTrace, AURL, nil, AResponseContent, []); +end; + +procedure TIdCustomHTTP.Head(AURL: string); +begin + DoRequest(Id_HTTPMethodHead, AURL, nil, nil, []); +end; + +procedure TIdCustomHTTP.Post(AURL: string; ASource, AResponseContent: TStream); +var + OldProtocol: TIdHTTPProtocolVersion; +begin + // PLEASE READ CAREFULLY + + // Currently when issuing a POST, IdHTTP will automatically set the protocol + // to version 1.0 independently of the value it had initially. This is because + // there are some servers that don't respect the RFC to the full extent. In + // particular, they don't respect sending/not sending the Expect: 100-Continue + // header. Until we find an optimum solution that does NOT break the RFC, we + // will restrict POSTS to version 1.0. + OldProtocol := FProtocolVersion; + try + // If hoKeepOrigProtocol is SET, is possible to assume that the developer + // is sure in operations of the server + if not (hoKeepOrigProtocol in FOptions) then begin + if Connected then begin + Disconnect; + end; + FProtocolVersion := pv1_0; + end; + DoRequest(Id_HTTPMethodPost, AURL, ASource, AResponseContent, []); + finally + FProtocolVersion := OldProtocol; + end; +end; + +// RLebeau 12/21/2010: this is based on W3's HTML standards: +// +// HTML 4.01 +// http://www.w3.org/TR/html401/ +// +// HTML 5 +// http://www.w3.org/TR/html5/ + +function WWWFormUrlEncode(const ASrc: string; AByteEncoding: IIdTextEncoding + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding{$ENDIF} + ): string; +const + // HTML 4.01 Section 17.13.4 ("Form content types") says: + // + // application/x-www-form-urlencoded + // + // Control names and values are escaped. Space characters are replaced by `+', + // and then reserved characters are escaped as described in [RFC1738], section + // 2.2: Non-alphanumeric characters are replaced by `%HH', a percent sign and + // two hexadecimal digits representing the ASCII code of the character. Line + // breaks are represented as "CR LF" pairs (i.e., `%0D%0A'). + // + // On the other hand, HTML 5 Section 4.10.16.4 ("URL-encoded form data") says: + // + // If the character isn't in the range U+0020, U+002A, U+002D, U+002E, + // U+0030 .. U+0039, U+0041 .. U+005A, U+005F, U+0061 .. U+007A then replace + // the character with a string formed as follows: Start with the empty string, + // and then, taking each byte of the character when expressed in the selected + // character encoding in turn, append to the string a U+0025 PERCENT SIGN + // character (%) followed by two characters in the ranges U+0030 DIGIT ZERO (0) + // to U+0039 DIGIT NINE (9) and U+0041 LATIN CAPITAL LETTER A to + // U+005A LATIN CAPITAL LETTER Z representing the hexadecimal value of the + // byte zero-padded if necessary). + // + // If the character is a U+0020 SPACE character, replace it with a single + // U+002B PLUS SIGN character (+). + // + // So, lets err on the side of caution and use the HTML 5.x definition, as it + // encodes some of the characters that HTML 4.01 allows unencoded... + // + SafeChars: TIdUnicodeString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*-._'; {do not localize} +var + I, J, CharLen, ByteLen: Integer; + Buf: TIdBytes; + {$IFDEF STRING_IS_ANSI} + LChars: TIdWideChars; + {$ENDIF} + LChar: WideChar; + Encoded: Boolean; +begin + Result := ''; {Do not Localize} + + // keep the compiler happy + Buf := nil; + {$IFDEF STRING_IS_ANSI} + LChars := nil; + {$ENDIF} + + if ASrc = '' then begin + Exit; + end; + + EnsureEncoding(AByteEncoding, encUTF8); + {$IFDEF STRING_IS_ANSI} + EnsureEncoding(ASrcEncoding, encOSDefault); + LChars := ASrcEncoding.GetChars(RawToBytes(ASrc[1], Length(ASrc))); + {$ENDIF} + + // 2 Chars to handle UTF-16 surrogates + SetLength(Buf, AByteEncoding.GetMaxByteCount(2)); + + I := 0; + while I < Length({$IFDEF STRING_IS_UNICODE}ASrc{$ELSE}LChars{$ENDIF}) do + begin + LChar := {$IFDEF STRING_IS_UNICODE}ASrc[I+1]{$ELSE}LChars[I]{$ENDIF}; + + // RLebeau 1/7/09: using Ord() for #128-#255 because in D2009 and later, the + // compiler may change characters >= #128 from their Ansi codepage value to + // their true Unicode codepoint value, depending on the codepage used for + // the source code. For instance, #128 may become #$20AC... + + if Ord(LChar) = 32 then {do not localize} + begin + Result := Result + '+'; {do not localize} + Inc(I); + end + else if WideCharIsInSet(SafeChars, LChar) then + begin + Result := Result + Char(LChar); + Inc(I); + end else + begin + // HTML 5 Section 4.10.16.4 says: + // + // For each character ... that cannot be expressed using the selected character + // encoding, replace the character by a string consisting of a U+0026 AMPERSAND + // character (&), a U+0023 NUMBER SIGN character (#), one or more characters in + // the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9) representing the + // Unicode code point of the character in base ten, and finally a U+003B + // SEMICOLON character (;). + // + CharLen := CalcUTF16CharLength( + {$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF} + ); // calculate length including surrogates + ByteLen := AByteEncoding.GetBytes( + {$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF}, + CharLen, Buf, 0); // explicit Unicode->Ansi conversion + + Encoded := (ByteLen > 0); + if Encoded and (LChar <> '?') then begin {do not localize} + for J := 0 to ByteLen-1 do begin + if Buf[J] = Ord('?') then begin {do not localize} + Encoded := False; + Break; + end; + end; + end; + + if Encoded then begin + for J := 0 to ByteLen-1 do begin + Result := Result + '%' + IntToHex(Ord(Buf[J]), 2); {do not localize} + end; + end else begin + J := GetUTF16Codepoint( + {$IFDEF STRING_IS_UNICODE}ASrc, I+1{$ELSE}LChars, I{$ENDIF}); + Result := Result + '&#' + IntToStr(J) + ';'; {do not localize} + end; + + Inc(I, CharLen); + end; + end; +end; + +function TIdCustomHTTP.SetRequestParams(ASource: TStrings; AByteEncoding: IIdTextEncoding + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding{$ENDIF} + ): string; +var + i: Integer; + LPos: integer; + LStr: string; + LTemp: TStringList; + + function EncodeLineBreaks(AStrings: TStrings): String; + begin + if AStrings.Count > 1 then begin + // break trailing CR&LF + Result := ReplaceAll(Trim(AStrings.Text), sLineBreak, '&'); {do not localize} + end else begin + Result := Trim(AStrings.Text); + end; + end; + +begin + if Assigned(ASource) then begin + if hoForceEncodeParams in FOptions then begin + // make a copy of ASource so the caller's TStrings object is not modified + LTemp := TStringList.Create; + try + LTemp.Assign(ASource); + for i := 0 to LTemp.Count - 1 do begin + LStr := LTemp[i]; + // TODO: use LTemp.NameValueSeparator on platforms that support it + LPos := IndyPos('=', LStr); {do not localize} + if LPos > 0 then begin + LTemp[i] := WWWFormUrlEncode(LTemp.Names[i], AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}) + + '=' {do not localize} + + WWWFormUrlEncode(IndyValueFromIndex(LTemp, i), AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + end else begin + LTemp[i] := WWWFormUrlEncode(LStr, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + end; + end; + Result := EncodeLineBreaks(LTemp); + finally + LTemp.Free; + end; + end else begin + Result := EncodeLineBreaks(ASource); + end; + end else begin + Result := ''; + end; +end; + +function TIdCustomHTTP.Post(AURL: string; const ASourceFile: String + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LSource: TIdReadFileExclusiveStream; +begin + LSource := TIdReadFileExclusiveStream.Create(ASourceFile); + try + Result := Post(AURL, LSource{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + finally + FreeAndNil(LSource); + end; +end; + +procedure TIdCustomHTTP.Post(AURL: string; const ASourceFile: String; AResponseContent: TStream); +var + LSource: TStream; +begin + LSource := TIdReadFileExclusiveStream.Create(ASourceFile); + try + Post(AURL, LSource, AResponseContent); + finally + FreeAndNil(LSource); + end; +end; + +procedure TIdCustomHTTP.Post(AURL: string; ASource: TStrings; AResponseContent: TStream; + AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ); +var + LParams: TMemoryStream; +begin + // Usual posting request have default ContentType is application/x-www-form-urlencoded + if (Request.ContentType = '') or IsContentTypeHtml(Request) then begin + Request.ContentType := 'application/x-www-form-urlencoded'; {do not localize} + end; + + if ASource <> nil then + begin + LParams := TMemoryStream.Create; + try + WriteStringToStream(LParams, SetRequestParams(ASource, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); + LParams.Position := 0; + Post(AURL, LParams, AResponseContent); + finally + FreeAndNil(LParams); + end; + end else begin + Post(AURL, TStream(nil), AResponseContent); + end; +end; + +function TIdCustomHTTP.Post(AURL: string; ASource: TStrings; AByteEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LResponse: TMemoryStream; +begin + LResponse := TMemoryStream.Create; + try + Post(AURL, ASource, LResponse, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + LResponse.Position := 0; + Result := ReadStringAsCharset(LResponse, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LResponse); + end; +end; + +function TIdCustomHTTP.Post(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LResponse: TMemoryStream; +begin + LResponse := TMemoryStream.Create; + try + Post(AURL, ASource, LResponse); + LResponse.Position := 0; + Result := ReadStringAsCharset(LResponse, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LResponse); + end; +end; + +procedure TIdCustomHTTP.Put(AURL: string; ASource, AResponseContent: TStream); +begin + DoRequest(Id_HTTPMethodPut, AURL, ASource, AResponseContent, []); +end; + +function TIdCustomHTTP.Put(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LResponse: TMemoryStream; +begin + LResponse := TMemoryStream.Create; + try + Put(AURL, ASource, LResponse); + LResponse.Position := 0; + Result := ReadStringAsCharset(LResponse, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LResponse); + end; +end; + +function TIdCustomHTTP.Get(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +begin + Result := Get(AURL, []{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); +end; + +function TIdCustomHTTP.Trace(AURL: string + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LResponse: TMemoryStream; +begin + LResponse := TMemoryStream.Create; + try + Trace(AURL, LResponse); + LResponse.Position := 0; + Result := ReadStringAsCharset(LResponse, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LResponse); + end; +end; + +function TIdCustomHTTP.DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean; +begin + // TODO: convert relative URLs to full URLs here... + Result := HandleRedirects; + if Assigned(FOnRedirect) then begin + FOnRedirect(Self, Location, RedirectCount, Result, VMethod); + end; +end; + +procedure TIdCustomHTTP.SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest); +var + // under ARC, convert a weak reference to a strong reference before working with it + LCookieManager: TIdCookieManager; +begin + LCookieManager := FCookieManager; + if Assigned(LCookieManager) and AllowCookies then + begin + // Send secure cookies only if we have Secured connection + LCookieManager.GenerateClientCookies( + AURL, + TextIsSame(AURL.Protocol, 'HTTPS'), {do not localize} + ARequest.RawHeaders); + end; +end; + +// This function sets the Host and Port and returns a boolean depending on +// whether a PROXY is being used or not. + +function TIdCustomHTTP.SetHostAndPort: TIdHTTPConnectionType; +var + LHost: string; + LPort: Integer; +begin + // First check to see if a Proxy has been specified. + if Length(ProxyParams.ProxyServer) > 0 then begin + if (not TextIsSame(FHost, ProxyParams.ProxyServer)) or (FPort <> ProxyParams.ProxyPort) then begin + if Connected then begin + Disconnect; + end; + end; + LHost := ProxyParams.ProxyServer; + LPort := ProxyParams.ProxyPort; + if TextIsSame(URL.Protocol, 'HTTPS') then begin {do not localize} + Result := ctSSLProxy; + end else begin + Result := ctProxy; + end; + end else begin + if Assigned(Socket) then begin + if Assigned(Socket.Binding) then begin + if URL.IPVersion <> Socket.Binding.IPVersion then begin + if Connected then begin + Disconnect; // get rid of current socket handle + end; + end; + end; + end; + LHost := URL.Host; + LPort := IndyStrToInt(URL.Port, IdPORT_HTTP); + if (not TextIsSame(FHost, LHost)) or (LPort <> FPort) then begin + if Connected then begin + Disconnect; + end; + end; + if TextIsSame(URL.Protocol, 'HTTPS') then begin {do not localize} + Result := ctSSL; + end else begin + Result := ctNormal; + end; + end; + Host := LHost; + Port := LPort; +end; + +procedure TIdCustomHTTP.ReadResult(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); +var + LS: TStream; + LOrigStream : TStream; + LParseHTML : Boolean; + LCreateTmpContent : Boolean; + LDecMeth : Integer; + //0 - no compression was used or we can't support that feature + //1 - deflate + //2 - gzip + // under ARC, convert a weak reference to a strong reference before working with it + LCompressor: TIdZLibCompressorBase; + + function CheckForPendingData(ATimeout: Integer): Boolean; + begin + if IOHandler.InputBufferIsEmpty then begin + IOHandler.CheckForDataOnSource(ATimeout); + end; + Result := not IOHandler.InputBufferIsEmpty; + end; + + function ShouldRead: Boolean; + var + CanRead: Boolean; + begin + Result := False; + if IndyPos('chunked', LowerCase(AResponse.TransferEncoding)) > 0 then begin {do not localize} + CanRead := True; + end + else if AResponse.HasContentLength then begin + CanRead := AResponse.ContentLength > 0; // If chunked then this is also 0 + end + else if IsHeaderMediaType(AResponse.ContentType, 'multipart') then begin {do not localize} + CanRead := not (hoNoReadMultipartMIME in FOptions); + end + else begin + CanRead := True; + end; + if CanRead then + begin + // DO NOT READ IF THE REQUEST IS HEAD!!! + // The server is supposed to send a 'Content-Length' header without sending + // the actual data. 1xx, 204, and 304 replies are not supposed to contain + // entity bodies, either... + if TextIsSame(ARequest.Method, Id_HTTPMethodHead) or + ({TextIsSame(ARequest.Method, Id_HTTPMethodPost) and} TextIsSame(ARequest.MethodOverride, Id_HTTPMethodHead)) or + // TODO: check for 'X-HTTP-Method' and 'X-METHOD-OVERRIDE' request headers as well... + ((AResponse.ResponseCode div 100) = 1) or + (AResponse.ResponseCode = 204) or + (AResponse.ResponseCode = 304) then + begin + // Have noticed one case where a non-conforming server did send an + // entity body in response to a HEAD request. If requested, ignore + // anything the server may send by accident + if not (hoWaitForUnexpectedData in FOptions) then begin + Exit; + end; + Result := CheckForPendingData(100); + end + else if (AResponse.ResponseCode div 100) = 3 then + begin + // This is a workaround for buggy HTTP 1.1 servers which + // does not return any body with 302 response code + Result := CheckForPendingData(5000); + end else begin + Result := True; + end; + end; + end; + + function ChunkSize: integer; + var + j: Integer; + s: string; + begin + s := InternalReadLn; + j := IndyPos(';', s); {do not localize} + if j > 0 then begin + s := Copy(s, 1, j - 1); + end; + Result := IndyStrToInt('$' + Trim(s), 0); {do not localize} + end; + + procedure ReadChunked; + var + LSize: Integer; + LTrailHeader: String; + begin + DoStatus(hsStatusText, [RSHTTPChunkStarted]); + BeginWork(wmRead); + try + LSize := ChunkSize; + while LSize <> 0 do begin + if Assigned(LS) then begin + IOHandler.ReadStream(LS, LSize); + end else begin + IOHandler.Discard(LSize); + end; + InternalReadLn; // CRLF at end of chunk data + LSize := ChunkSize; + end; + // read trailer headers + LTrailHeader := InternalReadLn; + while LTrailHeader <> '' do begin + AResponse.RawHeaders.Add(LTrailHeader); + LTrailHeader := InternalReadLn; + end; + finally + EndWork(wmRead); + end; + end; + + procedure ReadMIME; + var + LMIMEBoundary: TIdBytes; + LIndex: Integer; + LSize: Integer; + begin + LMIMEBoundary := ToBytes('--' + ExtractHeaderSubItem(AResponse.ContentType, 'boundary', QuoteHTTP) + '--'); + BeginWork(wmRead); + try + try + repeat + LIndex := IOHandler.InputBuffer.IndexOf(LMIMEBoundary); + if LIndex <> -1 then + begin + LSize := LIndex + Length(LMIMEBoundary); + if Assigned(LS) then begin + IOHandler.ReadStream(LS, LSize); + end else begin + IOHandler.Discard(LSize); + end; + InternalReadLn; // CRLF at end of boundary + Break; + end; + LSize := IOHandler.InputBuffer.Size - (Length(LMIMEBoundary)-1); + if LSize > 0 then begin + if Assigned(LS) then begin + IOHandler.ReadStream(LS, LSize); + end else begin + IOHandler.Discard(LSize); + end; + end; + IOHandler.CheckForDataOnSource; + IOHandler.CheckForDisconnect(True, True); + until False; + except + on E: EIdConnClosedGracefully do begin + if Assigned(LS) then begin + IOHandler.InputBuffer.ExtractToStream(LS); + end else begin + IOHandler.InputBuffer.Clear; + end; + end; + end; + finally + EndWork(wmRead); + end; + end; + +begin + if not ShouldRead then begin + Exit; + end; + + LDecMeth := 0; + + LParseHTML := Assigned(AResponse.ContentStream) and IsContentTypeHtml(AResponse) and not (hoNoParseMetaHTTPEquiv in FOptions); + LCreateTmpContent := LParseHTML and not (AResponse.ContentStream is TCustomMemoryStream); + + LOrigStream := AResponse.ContentStream; + if LCreateTmpContent then begin + AResponse.ContentStream := TMemoryStream.Create; + end; + + LCompressor := Compressor; + try + // we need to determine what type of decompression may need to be used + // before we read from the IOHandler. If there is compression, then we + // use a local stream to download the compressed data and decompress it. + // If no compression is used, ContentStream will be used directly + + if Assigned(AResponse.ContentStream) then begin + if Assigned(LCompressor) and LCompressor.IsReady then begin + LDecMeth := PosInStrArray(AResponse.ContentEncoding, ['deflate', 'gzip'], False) + 1; {do not localize} + end; + if LDecMeth > 0 then begin + LS := TMemoryStream.Create; + end else begin + LS := AResponse.ContentStream; + end; + end else + begin + LS := nil; + end; + + try + if IndyPos('chunked', LowerCase(AResponse.TransferEncoding)) > 0 then begin {do not localize} + ReadChunked; + end + else if AResponse.HasContentLength then begin + if AResponse.ContentLength > 0 then begin// If chunked then this is also 0 + try + if Assigned(LS) then begin + IOHandler.ReadStream(LS, AResponse.ContentLength); + end else begin + IOHandler.Discard(AResponse.ContentLength); + end; + except + // should this be caught here? We are being told the size, so a + // premature disconnect should be an error, right? + on E: EIdConnClosedGracefully do begin end; + end; + end; + end + else if IsHeaderMediaType(AResponse.ContentType, 'multipart') then begin {do not localize} + ReadMIME; + end else begin + if Assigned(LS) then begin + IOHandler.ReadStream(LS, -1, True); + end else begin + IOHandler.DiscardAll; + end; + end; + if LDecMeth > 0 then begin + LS.Position := 0; + case LDecMeth of + 1 : LCompressor.DecompressDeflateStream(LS, AResponse.ContentStream); + 2 : LCompressor.DecompressGZipStream(LS, AResponse.ContentStream); + end; + end; + finally + if LDecMeth > 0 then begin + FreeAndNil(LS); + end; + end; + if LParseHTML then begin + AResponse.ProcessMetaHTTPEquiv; + end; + finally + if LCreateTmpContent then + begin + try + LOrigStream.CopyFrom(AResponse.ContentStream, 0); + finally + {$IFNDEF USE_OBJECT_ARC} + AResponse.ContentStream.Free; + {$ENDIF} + AResponse.ContentStream := LOrigStream; + end; + end; + end; +end; + +// TODO: move the XML charset detector below to the IdGlobalProtocols unit so +// it can be used in other components, like TIdMessageClient and TIdIMAP4... + +type + XmlEncoding = (xmlUCS4BE, xmlUCS4BEOdd, xmlUCS4LE, xmlUCS4LEOdd, + xmlUTF16BE, xmlUTF16LE, xmlUTF8, xmlEBCDIC, xmlUnknown + ); + + XmlBomInfo = record + Charset: String; + BOMLen: Integer; + BOM: UInt32; + BOMMask: UInt32; + end; + + XmlNonBomInfo = record + CharLen: Integer; + FirstChar: UInt32; + LastChar: UInt32; + CharMask: UInt32; + end; + +const + XmlBOMs: array[xmlUCS4BE..xmlUTF8] of XmlBomInfo = ( + (Charset: 'UCS-4BE'; BOMLen: 4; BOM: $0000FEFF; BOMMask: $FFFFFFFF), {do not localize} + (Charset: ''; {UCS-4} BOMLen: 4; BOM: $0000FFFE; BOMMask: $FFFFFFFF), + (Charset: 'UCS-4LE'; BOMLen: 4; BOM: $FFFE0000; BOMMask: $FFFFFFFF), {do not localize} + (Charset: ''; {UCS-4} BOMLen: 4; BOM: $FEFF0000; BOMMask: $FFFFFFFF), + (Charset: 'UTF-16BE'; BOMLen: 2; BOM: $FEFF0000; BOMMask: $FFFF0000), {do not localize} + (Charset: 'UTF-16LE'; BOMLen: 2; BOM: $FFFE0000; BOMMask: $FFFF0000), {do not localize} + (Charset: 'UTF-8'; BOMLen: 3; BOM: $EFBBBF00; BOMMask: $FFFFFF00) {do not localize} + ); + + XmlNonBOMs: array[xmlUCS4BE..xmlEBCDIC] of XmlNonBomInfo = ( + (CharLen: 4; FirstChar: $0000003C; LastChar: $0000003E; CharMask: $FFFFFFFF), + (CharLen: 4; FirstChar: $00003C00; LastChar: $00003E00; CharMask: $FFFFFFFF), + (CharLen: 4; FirstChar: $3C000000; LastChar: $3E000000; CharMask: $FFFFFFFF), + (CharLen: 4; FirstChar: $003C0000; LastChar: $003E0000; CharMask: $FFFFFFFF), + (CharLen: 2; FirstChar: $003C003F; LastChar: $003E0000; CharMask: $FFFF0000), + (CharLen: 2; FirstChar: $3C003F00; LastChar: $3E000000; CharMask: $FFFF0000), + (CharLen: 1; FirstChar: $3C3F786D; LastChar: $3E000000; CharMask: $FF000000), + (CharLen: 1; FirstChar: $4C6FA794; LastChar: $6E000000; CharMask: $FF000000) + ); + + XmlUCS4AsciiIndex: array[xmlUCS4BE..xmlUCS4LEOdd] of Integer = (3, 2, 0, 1); + + // RLebeau: only interested in EBCDIC ASCII characters that are allowed in + // an XML declaration, we'll treat everything else as #01 for now... + XmlEBCDICTable: array[Byte] of Char = ( + { -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F } + {0-} #01, #01, #01, #01, #01, #09, #01, #01, #01, #01, #01, #01, #01, #13, #01, #01, {do not localize} + {1-} #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, {do not localize} + {2-} #01, #01, #01, #01, #01, #10, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, {do not localize} + {3-} #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, {do not localize} + {4-} ' ', #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, '.', '<', #01, #01, #01, {do not localize} + {5-} #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, {do not localize} + {6-} '-', #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, '_', '>', '?', {do not localize} + {7-} #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #27, '=', '"', {do not localize} + {8-} #01, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', #01, #01, #01, #01, #01, #01, {do not localize} + {9-} #01, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', #01, #01, #01, #01, #01, #01, {do not localize} + {A-} #01, #01, 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', #01, #01, #01, #01, #01, #01, {do not localize} + {B-} #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, #01, {do not localize} + {C-} #01, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', #01, #01, #01, #01, #01, #01, {do not localize} + {D-} #01, 'J', 'K', 'L', 'N', 'N', 'O', 'P', 'Q', 'R', #01, #01, #01, #01, #01, #01, {do not localize} + {E-} #01, #01, 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', #01, #01, #01, #01, #01, #01, {do not localize} + {F-} '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', #01, #01, #01, #01, #01, #01 {do not localize} + ); + +function DetectXmlCharset(AStream: TStream): String; +var + Buffer: TIdBytes; + InBuf, StreamPos, CurPos: TIdStreamSize; + XmlDec, XmlEnc: String; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} + I, Len: Integer; + Enc: XmlEncoding; + Signature: UInt32; + + function BufferToUInt32: UInt32; + begin + Result := (UInt32(Buffer[0]) shl 24) or + (UInt32(Buffer[1]) shl 16) or + (UInt32(Buffer[2]) shl 8) or + UInt32(Buffer[3]); + end; + +begin + // XML's default encoding is UTF-8 unless specified otherwise, either + // by a BOM or an explicit "encoding" in the XML's prolog... + + Result := 'UTF-8'; {do not localize} + + StreamPos := AStream.Position; + try + AStream.Position := 0; + + SetLength(Buffer, 4); + FillBytes(Buffer, 4, $00); + + InBuf := ReadTIdBytesFromStream(AStream, Buffer, 4); + if InBuf < 3 then begin + Exit; + end; + + Signature := BufferToUInt32; + + // check for known BOMs first... + + for Enc := Low(XmlBOMs) to High(XmlBOMs) do begin + if (Signature and XmlBOMs[Enc].BOMMask) = XmlBOMs[Enc].BOM then begin + Inc(StreamPos, XmlBOMs[Enc].BOMLen); + Result := XmlBOMs[Enc].Charset; + Exit; + end; + end; + + // check for non-BOM'ed encodings now... + + if InBuf <> 4 then begin + Exit; + end; + + XmlDec := ''; + + for Enc := Low(XmlNonBOMs) to High(XmlNonBOMs) do begin + if Signature = XmlNonBOMs[Enc].FirstChar then begin + FillBytes(Buffer, 4, $00); + while (AStream.Size - AStream.Position) >= XmlNonBOMs[Enc].CharLen do + begin + ReadTIdBytesFromStream(AStream, Buffer, XmlNonBOMs[Enc].CharLen); + Signature := BufferToUInt32; + if (Signature and XmlNonBOMs[Enc].CharMask) = XmlNonBOMs[Enc].LastChar then + begin + CurPos := AStream.Position; + AStream.Position := 0; + case Enc of + xmlUCS4BE, xmlUCS4LE, xmlUCS4BEOdd, xmlUCS4LEOdd: begin + // TODO: create UCS-4 IIdTextEncoding implementations... + Len := CurPos div XmlNonBOMs[Enc].CharLen; + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(Len); + {$ELSE} + SetLength(XmlDec, Len); + {$ENDIF} + for I := 1 to Len do begin + ReadTIdBytesFromStream(AStream, Buffer, XmlNonBOMs[Enc].CharLen); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char(Buffer[XmlUCS4AsciiIndex[Enc]])); + {$ELSE} + XmlDec[I] := Char(Buffer[XmlUCS4AsciiIndex[Enc]]); + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + XmlDec := LSB.ToString; + LSB := nil; + {$ENDIF} + end; + xmlUTF16BE: begin + XmlDec := ReadStringFromStream(AStream, CurPos, IndyTextEncoding_UTF16BE); + end; + xmlUTF16LE: begin + XmlDec := ReadStringFromStream(AStream, CurPos, IndyTextEncoding_UTF16LE); + end; + xmlUTF8: begin + XmlDec := ReadStringFromStream(AStream, CurPos, IndyTextEncoding_UTF8); + end; + xmlEBCDIC: begin + // TODO: create an EBCDIC IIdTextEncoding implementation... + {$IFDEF STRING_IS_IMMUTABLE} + Len := ReadTIdBytesFromStream(AStream, Buffer, CurPos); + LSB := TStringBuilder.Create(Len); + for I := 0 to Len-1 do begin + LSB.Append(XmlEBCDICTable[Buffer[I]]); + end; + XmlDec := LSB.ToString; + {$ELSE} + XmlDec := ReadStringFromStream(AStream, CurPos, IndyTextEncoding_8Bit); + for I := 1 to Length(XmlDec) do begin + XmlDec[I] := XmlEBCDICTable[Byte(XmlDec[I])]; + end; + {$ENDIF} + end; + end; + Break; + end; + end; + Break; + end; + end; + + if XmlDec = '' then begin + Exit; + end; + + I := Pos('encoding', XmlDec); {do not localize} + if I = 0 then begin + Exit; + end; + + XmlDec := TrimLeft(Copy(XmlDec, I+8, MaxInt)); + if not CharEquals(XmlDec, 1, '=') then begin {do not localize} + Exit; + end; + + XmlDec := TrimLeft(Copy(XmlDec, 2, MaxInt)); + if XmlDec = '' then begin + Exit; + end; + + if XmlDec[1] = #$27 then begin + XmlDec := Copy(XmlDec, 2, MaxInt); + XmlEnc := Fetch(XmlDec, #$27); + end + else if XmlDec[1] = '"' then begin + XmlDec := Copy(XmlDec, 2, MaxInt); + XmlEnc := Fetch(XmlDec, '"'); + end; + + XmlEnc := Trim(XmlEnc); + if XmlEnc = '' then begin + Exit; + end; + + Result := XmlEnc; + finally + AStream.Position := StreamPos; + end; +end; + +function TIdCustomHTTP.ResponseCharset: String; +begin + if IsContentTypeAppXml(Response) then begin + // the media type is not a 'text/...' based XML type, so ignore the + // charset from the headers, if present, and parse the XML itself... + Result := DetectXmlCharset(Response.ContentStream); + end + else begin + // RLebeau 1/30/2012: Response.CharSet is now updated at the time + // when HTML content is parsed for tags ... + + // TODO: if the Charset is not specified, return an appropriate value + // that is registered with IANA for the reported ContentType... + + Result := Response.CharSet; + end; +end; + +const + Requires_HTTP_1_1: array[0..4] of String = (Id_HTTPMethodTrace, Id_HTTPMethodPut, Id_HTTPMethodOptions, Id_HTTPMethodDelete, Id_HTTPMethodPatch); + +procedure TIdCustomHTTP.PrepareRequest(ARequest: TIdHTTPRequest); +var + LURI: TIdURI; + LHost: string; +begin + LURI := TIdURI.Create(ARequest.URL); + + try + if Length(LURI.Username) > 0 then begin + ARequest.Username := LURI.Username; + ARequest.Password := LURI.Password; + end; + + FURI.Username := ARequest.Username; + FURI.Password := ARequest.Password; + + FURI.Path := ProcessPath(FURI.Path, LURI.Path); + FURI.Document := LURI.Document; + FURI.Params := LURI.Params; + + if Length(LURI.Host) > 0 then begin + FURI.Host := LURI.Host; + end; + + if Length(LURI.Protocol) > 0 then begin + FURI.Protocol := LURI.Protocol; + end + // non elegant solution - to be recoded, only for pointing the bug / GREGOR + else if TextIsSame(FURI.Protocol, 'https') then begin {do not localize} + FURI.Protocol := 'https'; {do not localize} + end + else begin + FURI.Protocol := 'http'; {do not localize} + end; + + if Length(LURI.Port) > 0 then begin + FURI.Port := LURI.Port; + end + else if TextIsSame(LURI.Protocol, 'http') then begin {do not localize} + FURI.Port := IntToStr(IdPORT_HTTP); + end + else if TextIsSame(LURI.Protocol, 'https') then begin {do not localize} + FURI.Port := IntToStr(IdPORT_https); + end + else if Length(FURI.Port) = 0 then begin + raise EIdUnknownProtocol.Create(RSHTTPUnknownProtocol); + end; + + if (TextIsSame(ARequest.Method, Id_HTTPMethodOptions) or TextIsSame(ARequest.MethodOverride, Id_HTTPMethodOptions)) + and TextIsSame(LURI.Document, '*') then {do not localize} + begin + ARequest.URL := LURI.Document; + end else begin + // The URL part is not URL encoded at this place + ARequest.URL := URL.GetPathAndParams; + end; + + ARequest.IPVersion := LURI.IPVersion; + FURI.IPVersion := ARequest.IPVersion; + + // Check for valid HTTP request methods + if (PosInStrArray(ARequest.Method, Requires_HTTP_1_1, False) > -1) or + (PosInStrArray(ARequest.MethodOverride, Requires_HTTP_1_1, False) > -1) then + begin + if ProtocolVersion <> pv1_1 then begin + raise EIdException.Create(RSHTTPMethodRequiresVersion); + end; + end; + + if Assigned(ARequest.Source) then begin + ARequest.ContentLength := ARequest.Source.Size; + end else begin + ARequest.ContentLength := -1; + end; + + // RLebeau: wrap an IPv6 address in brackets, per RFC 2732, and RFC 3986 section 3.2.2... + if (FURI.IPVersion = Id_IPv6) and (MakeCanonicalIPv6Address(FURI.Host) <> '') then begin + LHost := '[' + FURI.Host + ']'; {do not localize} + end else begin + LHost := FURI.Host; + end; + + if (TextIsSame(FURI.Protocol, 'http') and (FURI.Port = IntToStr(IdPORT_HTTP))) or {do not localize} + (TextIsSame(FURI.Protocol, 'https') and (FURI.Port = IntToStr(IdPORT_https))) then {do not localize} + begin + ARequest.Host := LHost; + end else begin + ARequest.Host := LHost + ':' + FURI.Port; {do not localize} + end; + finally + FreeAndNil(LURI); // Free URI Object + end; +end; + +procedure TIdCustomHTTP.CheckAndConnect(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); +begin + if not AResponse.KeepAlive then begin + Disconnect; + end; + + if Assigned(IOHandler) then begin + IOHandler.InputBuffer.Clear; + end; + + CheckForGracefulDisconnect(False); + + if not Connected then try + IPVersion := FURI.IPVersion; + + case ARequest.UseProxy of + ctNormal, ctProxy: + begin + if (IOHandler is TIdSSLIOHandlerSocketBase) then begin + TIdSSLIOHandlerSocketBase(IOHandler).PassThrough := True; + TIdSSLIOHandlerSocketBase(IOHandler).URIToCheck := FURI.URI; + end; + end; + + ctSSL, ctSSLProxy: + begin + // if an IOHandler has not been assigned yet, try to create a default SSL IOHandler object + // + // TODO: if an IOHandler has been assigned, but is not an SSL IOHandler, + // release it and try to create a default SSL IOHandler object? + // + if IOHandler = nil then begin + IOHandler := TIdIOHandler.TryMakeIOHandler(TIdSSLIOHandlerSocketBase, Self); + if IOHandler = nil then begin + raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid); + end; + IOHandler.OnStatus := OnStatus; + ManagedIOHandler := True; + end + else if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin + raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid); + end; + TIdSSLIOHandlerSocketBase(IOHandler).URIToCheck := FURI.URI; + TIdSSLIOHandlerSocketBase(IOHandler).PassThrough := (ARequest.UseProxy = ctSSLProxy); + end; + end; + + Connect; + except + on E: EIdSSLProtocolReplyError do begin + Disconnect; + raise; + end; + end; +end; + +procedure TIdCustomHTTP.ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); +var + LLocalHTTP: TIdHTTPProtocol; + LUseConnectVerb: Boolean; + // under ARC, convert a weak reference to a strong reference before working with it + LCompressor: TIdZLibCompressorBase; + LOldProxy: TIdHTTPConnectionType; + LNewDest: string; +begin + LNewDest := URL.Host + ':' + URL.Port; + + LOldProxy := ARequest.FUseProxy; + ARequest.FUseProxy := SetHostAndPort; + + if ARequest.UseProxy <> LOldProxy then begin + if Connected then begin + Disconnect; + end; + end + else if (ARequest.UseProxy = ctSSLProxy) and (not TextIsSame(ARequest.Destination, LNewDest)) then begin + if Connected then begin + Disconnect; + end; + end; + + ARequest.Destination := LNewDest; + + LUseConnectVerb := False; + + case ARequest.UseProxy of + ctNormal: + begin + if (ProtocolVersion = pv1_0) and (Length(ARequest.Connection) = 0) then + begin + ARequest.Connection := 'keep-alive'; {do not localize} + end; + end; + ctSSL, ctSSLProxy: + begin + ARequest.Connection := ''; + if ARequest.UseProxy = ctSSLProxy then begin + // if already connected to an SSL proxy, DO NOT send another + // CONNECT request, as it will be sent directly to the target + // HTTP server and not to the proxy! + LUseConnectVerb := not Connected; + end; + end; + ctProxy: + begin + ARequest.URL := FURI.URI; + if (ProtocolVersion = pv1_0) and (Length(ARequest.Connection) = 0) then + begin + ARequest.ProxyConnection := 'keep-alive'; {do not localize} + end; + if hoNonSSLProxyUseConnectVerb in FOptions then begin + // if already connected to a proxy, DO NOT send another CONNECT + // request, as it will be sent directly to the target HTTP server + // and not to the proxy! + LUseConnectVerb := not Connected; + end; + end; + end; + + LCompressor := FCompressor; + if Assigned(LCompressor) and LCompressor.IsReady then begin + if IndyPos('deflate', ARequest.AcceptEncoding) = 0 then {do not localize} + begin + if ARequest.AcceptEncoding <> '' then begin {do not localize} + ARequest.AcceptEncoding := ARequest.AcceptEncoding + ', deflate'; {do not localize} + end else begin + ARequest.AcceptEncoding := 'deflate'; {do not localize} + end; + end; + if IndyPos('gzip', ARequest.AcceptEncoding) = 0 then {do not localize} + begin + if ARequest.AcceptEncoding <> '' then begin {do not localize} + ARequest.AcceptEncoding := ARequest.AcceptEncoding + ', gzip'; {do not localize} + end else begin + ARequest.AcceptEncoding := 'gzip'; {do not localize} + end; + end; + end; + {$IFDEF USE_OBJECT_ARC}LCompressor := nil;{$ENDIF} + + // TODO: if AcceptEncoding is blank, DON'T set it to 'identity'! Oddly, + // some faulty servers do not understand 'identity' when explicitly + // stated. It is the default behavior when no "Accept-Encoding" header + // is sent, so just let the server fallback to it normally... + if IndyPos('identity', ARequest.AcceptEncoding) = 0 then begin {do not localize} + if ARequest.AcceptEncoding <> '' then begin + ARequest.AcceptEncoding := ARequest.AcceptEncoding + ', identity'; {do not localize} + end else begin + ARequest.AcceptEncoding := 'identity'; {do not localize} + end; + end; + + if LUseConnectVerb then begin + LLocalHTTP := CreateProtocol; + try + LLocalHTTP.Request.UserAgent := ARequest.UserAgent; + LLocalHTTP.Request.Host := ARequest.Host; + LLocalHTTP.Request.Pragma := 'no-cache'; {do not localize} + LLocalHTTP.Request.URL := ARequest.Destination; + LLocalHTTP.Request.Method := Id_HTTPMethodConnect; + LLocalHTTP.Request.ProxyConnection := 'keep-alive'; {do not localize} + LLocalHTTP.Request.FUseProxy := ARequest.UseProxy; + + // leaving LLocalHTTP.Response.ContentStream set to nil so response data is discarded without wasting memory + try + repeat + CheckAndConnect(LLocalHTTP.Request, LLocalHTTP.Response); + LLocalHTTP.BuildAndSendRequest(nil); + + LLocalHTTP.Response.ResponseText := InternalReadLn; + if Length(LLocalHTTP.Response.ResponseText) = 0 then begin + // Support for HTTP responses without status line and headers + LLocalHTTP.Response.ResponseText := 'HTTP/1.0 200 OK'; {do not localize} + LLocalHTTP.Response.Connection := 'close'; {do not localize} + end else begin + LLocalHTTP.RetrieveHeaders(MaxHeaderLines); + ProcessCookies(LLocalHTTP.Request, LLocalHTTP.Response); + end; + + if (LLocalHTTP.Response.ResponseCode div 100) = 2 then begin + // Connection established + if (ARequest.UseProxy = ctSSLProxy) and (IOHandler is TIdSSLIOHandlerSocketBase) then begin + TIdSSLIOHandlerSocketBase(IOHandler).PassThrough := False; + end; + Break; + end else begin + LLocalHTTP.ProcessResponse([]); + end; + until False; + except + raise; + // TODO: Add property that will contain the error messages. + end; + finally + FreeAndNil(LLocalHTTP); + end; + end else begin + CheckAndConnect(ARequest, AResponse); + end; + + FHTTPProto.BuildAndSendRequest(URL); + + // RLebeau 1/31/2008: in order for TIdWebDAV to post data correctly, don't + // restrict which HTTP methods can post (except logically for GET and HEAD), + // especially since TIdCustomHTTP.PrepareRequest() does not differentiate when + // setting up the 'Content-Length' header ... + + // TODO: when sending an HTTP 1.1 request with an 'Expect: 100-continue' header, + // do not send the Source data until the server replies with a 100 response code, + // or until a timeout occurs if the server does not send a 100... + + if ARequest.Source <> nil then begin + IOHandler.Write(ARequest.Source, 0, False); + end; +end; + +procedure TIdCustomHTTP.SetAllowCookies(AValue: Boolean); +begin + FAllowCookies := AValue; +end; + +procedure TIdCustomHTTP.ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse); +var + LCookies: TStringList; + // under ARC, convert a weak reference to a strong reference before working with it + LCookieManager: TIdCookieManager; +begin + LCookieManager := FCookieManager; + + if (not Assigned(LCookieManager)) and AllowCookies then begin + LCookieManager := TIdCookieManager.Create(Self); + SetCookieManager(LCookieManager); + FImplicitCookieManager := True; + end; + + if Assigned(LCookieManager) and AllowCookies then begin + LCookies := TStringList.Create; + try + AResponse.RawHeaders.Extract('Set-Cookie', LCookies); {do not localize} + AResponse.MetaHTTPEquiv.RawHeaders.Extract('Set-Cookie', LCookies); {do not localize} + LCookieManager.AddServerCookies(LCookies, FURI); + finally + FreeAndNil(LCookies); + end; + end; +end; + +// under ARC, all weak references to a freed object get nil'ed automatically +// so this is mostly redundant +procedure TIdCustomHTTP.Notification(AComponent: TComponent; Operation: TOperation); +begin + if Operation = opRemove then begin + if (AComponent = FCookieManager) then begin + FCookieManager := nil; + FImplicitCookieManager := False; + end + {$IFNDEF USE_OBJECT_ARC} + else if (AComponent = FAuthenticationManager) then begin + FAuthenticationManager := nil; + end else if (AComponent = FCompressor) then begin + FCompressor := nil; + end + {$ENDIF} + ; + end; + inherited Notification(AComponent, Operation); +end; + +procedure TIdCustomHTTP.SetCookieManager(ACookieManager: TIdCookieManager); +var + // under ARC, convert a weak reference to a strong reference before working with it + LCookieManager: TIdCookieManager; +begin + LCookieManager := FCookieManager; + + if LCookieManager <> ACookieManager then begin + + // under ARC, all weak references to a freed object get nil'ed automatically + + if Assigned(LCookieManager) then begin + if FImplicitCookieManager then begin + FCookieManager := nil; + FImplicitCookieManager := False; + IdDisposeAndNil(LCookieManager); + end else begin + {$IFNDEF USE_OBJECT_ARC} + LCookieManager.RemoveFreeNotification(Self); + {$ENDIF} + end; + end; + + FCookieManager := ACookieManager; + FImplicitCookieManager := False; + + {$IFNDEF USE_OBJECT_ARC} + if Assigned(ACookieManager) then begin + ACookieManager.FreeNotification(Self); + end; + {$ENDIF} + end; +end; + +function TIdCustomHTTP.DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; +var + i: Integer; + S: string; + LAuthCls: TIdAuthenticationClass; + LAuth: TIdAuthentication; +begin + Inc(FAuthRetries); + if not Assigned(ARequest.Authentication) then begin + // Find wich Authentication method is supported from us. + LAuthCls := nil; + + for i := 0 to AResponse.WWWAuthenticate.Count - 1 do begin + S := AResponse.WWWAuthenticate[i]; + LAuthCls := FindAuthClass(Fetch(S)); + if Assigned(LAuthCls) then begin + Break; + end; + end; + + // let the user override us, if desired. + if Assigned(FOnSelectAuthorization) then begin + OnSelectAuthorization(Self, LAuthCls, AResponse.WWWAuthenticate); + end; + + if not Assigned(LAuthCls) then begin + Result := False; + Exit; + end; + + ARequest.Authentication := LAuthCls.Create; + end; + + { + this is commented out as it breaks SSPI and NTLM authentication. it is + normal and expected to get multiple 407 responses during negotiation. + + // Clear password and reset autorization if previous failed + if (AResponse.FResponseCode = 401) then begin + ARequest.Password := ''; + ARequest.Authentication.Reset; + end; + } + + // S.G. 20/10/2003: Added part about the password. Not testing user name as some + // S.G. 20/10/2003: web sites do not require user name, only password. + // + // RLebeau 11/18/2014: what about SSPI? It does not require an explicit + // username/password as it can use the identity of the user token associated + // with the calling thread! + // + Result := Assigned(FOnAuthorization) or (Trim(ARequest.Password) <> ''); + + if not Result then begin + Exit; + end; + + LAuth := ARequest.Authentication; + LAuth.Username := ARequest.Username; + LAuth.Password := ARequest.Password; + // S.G. 20/10/2003: ToDo: We need to have a marker here to prevent the code to test with the same username/password combo + // S.G. 20/10/2003: if they are picked up from properties. + LAuth.Params.Values['Authorization'] := ARequest.Authentication.Authentication; {do not localize} + LAuth.AuthParams := AResponse.WWWAuthenticate; + + Result := False; + + repeat + case LAuth.Next of + wnAskTheProgram: + begin // Ask the user porgram to supply us with authorization information + if Assigned(FOnAuthorization) then + begin + LAuth.UserName := ARequest.Username; + LAuth.Password := ARequest.Password; + + OnAuthorization(Self, LAuth, Result); + + if Result then begin + ARequest.BasicAuthentication := True; + ARequest.Username := LAuth.UserName; + ARequest.Password := LAuth.Password; + end else begin + Break; + end; + end; + end; + wnDoRequest: + begin + Result := True; + Break; + end; + wnFail: + begin + Result := False; + Break; + end; + end; + until False; +end; + +function TIdCustomHTTP.DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; +var + i: Integer; + S: string; + LAuthCls: TIdAuthenticationClass; + LAuth: TIdAuthentication; +begin + Inc(FAuthProxyRetries); + if not Assigned(ProxyParams.Authentication) then begin + // Find which Authentication method is supported from us. + LAuthCls := nil; + + for i := 0 to AResponse.ProxyAuthenticate.Count-1 do begin + S := AResponse.ProxyAuthenticate[i]; + LAuthCls := FindAuthClass(Fetch(S)); + if Assigned(LAuthCls) then begin + Break; + end; + end; + + // let the user override us, if desired. + if Assigned(FOnSelectProxyAuthorization) then begin + OnSelectProxyAuthorization(Self, LAuthCls, AResponse.ProxyAuthenticate); + end; + + if not Assigned(LAuthCls) then begin + Result := False; + Exit; + end; + + ProxyParams.Authentication := LAuthCls.Create; + end; + + { + this is commented out as it breaks SSPI and NTLM authentication. it is + normal and expected to get multiple 407 responses during negotiation. + + // Clear password and reset authorization if previous failed + if (AResponse.FResponseCode = 407) then begin + ProxyParams.ProxyPassword := ''; + ProxyParams.Authentication.Reset; + end; + } + + // RLebeau 11/18/2014: Added part about the password. Not testing user name + // as some proxies do not require user name, only password. + // + // RLebeau 11/18/2014: what about SSPI? It does not require an explicit + // username/password as it can use the identity of the user token associated + // with the calling thread! + // + Result := Assigned(OnProxyAuthorization) or (Trim(ProxyParams.ProxyPassword) <> ''); + + if not Result then begin + Exit; + end; + + LAuth := ProxyParams.Authentication; + LAuth.Username := ProxyParams.ProxyUsername; + LAuth.Password := ProxyParams.ProxyPassword; + // TODO: do we need to set this, like DoOnAuthorization does? + //LAuth.Params.Values['Authorization'] := ProxyParams.Authentication; {do not localize} + LAuth.AuthParams := AResponse.ProxyAuthenticate; + + Result := False; + + repeat + case LAuth.Next of + wnAskTheProgram: // Ask the user porgram to supply us with authorization information + begin + if Assigned(OnProxyAuthorization) then + begin + LAuth.Username := ProxyParams.ProxyUsername; + LAuth.Password := ProxyParams.ProxyPassword; + + OnProxyAuthorization(Self, LAuth, Result); + + if Result then begin + // TODO: do we need to set this, like DoOnAuthorization does? + //ProxyParams.BasicAuthentication := True; + ProxyParams.ProxyUsername := LAuth.Username; + ProxyParams.ProxyPassword := LAuth.Password; + end else begin + Break; + end; + end; + end; + wnDoRequest: + begin + Result := True; + Break; + end; + wnFail: + begin + Result := False; + Break; + end; + end; + until False; +end; + +function TIdCustomHTTP.GetResponseCode: Integer; +begin + Result := Response.ResponseCode; +end; + +function TIdCustomHTTP.GetResponseText: string; +begin + Result := Response.ResponseText; +end; + +function TIdCustomHTTP.GetResponse: TIdHTTPResponse; +begin + Result := FHTTPProto.Response; +end; + +function TIdCustomHTTP.GetRequest: TIdHTTPRequest; +begin + Result := FHTTPProto.Request; +end; + +function TIdCustomHTTP.GetMetaHTTPEquiv: TIdMetaHTTPEquiv; +begin + Result := Response.MetaHTTPEquiv; +end; + +procedure TIdCustomHTTP.DoOnDisconnected; +var + // under ARC, convert a weak reference to a strong reference before working with it + LAuthManager: TIdAuthenticationManager; +begin + inherited DoOnDisconnected; + + if Assigned(Request.Authentication) and + (Request.Authentication.CurrentStep = Request.Authentication.Steps) then + begin + LAuthManager := AuthenticationManager; + if Assigned(LAuthManager) then begin + LAuthManager.AddAuthentication(Request.Authentication, URL); + end; + {$IFNDEF USE_OBJECT_ARC} + Request.Authentication.Free; + {$ENDIF} + Request.Authentication := nil; + end; + + if Assigned(ProxyParams.Authentication) and + (ProxyParams.Authentication.CurrentStep = ProxyParams.Authentication.Steps) then begin + ProxyParams.Authentication.Reset; + end; +end; + +procedure TIdCustomHTTP.SetAuthenticationManager(Value: TIdAuthenticationManager); +begin + {$IFDEF USE_OBJECT_ARC} + // under ARC, all weak references to a freed object get nil'ed automatically + FAuthenticationManager := Value; + {$ELSE} + if FAuthenticationManager <> Value then begin + if Assigned(FAuthenticationManager) then begin + FAuthenticationManager.RemoveFreeNotification(self); + end; + FAuthenticationManager := Value; + if Assigned(FAuthenticationManager) then begin + FAuthenticationManager.FreeNotification(Self); + end; + end; + {$ENDIF} +end; + +{ +procedure TIdCustomHTTP.SetHost(const Value: string); +begin + inherited SetHost(Value); + URL.Host := Value; +end; + +procedure TIdCustomHTTP.SetPort(const Value: integer); +begin + inherited SetPort(Value); + URL.Port := IntToStr(Value); +end; +} +procedure TIdCustomHTTP.SetRequest(Value: TIdHTTPRequest); +begin + FHTTPProto.Request.Assign(Value); +end; + +procedure TIdCustomHTTP.SetProxyParams(AValue: TIdProxyConnectionInfo); +begin + FProxyParameters.Assign(AValue); +end; + +procedure TIdCustomHTTP.Post(AURL: string; ASource: TIdMultiPartFormDataStream; + AResponseContent: TStream); +begin + Assert(ASource<>nil); + Request.ContentType := ASource.RequestContentType; + // TODO: Request.CharSet := ASource.RequestCharSet; + Post(AURL, TStream(ASource), AResponseContent); +end; + +function TIdCustomHTTP.Post(AURL: string; ASource: TIdMultiPartFormDataStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} +): string; +begin + Assert(ASource<>nil); + Request.ContentType := ASource.RequestContentType; + // TODO: Request.CharSet := ASource.RequestCharSet; + Result := Post(AURL, TStream(ASource){$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); +end; + +{ TIdHTTPResponse } + +constructor TIdHTTPResponse.Create(AHTTP: TIdCustomHTTP); +begin + inherited Create(AHTTP); + FHTTP := AHTTP; + FResponseCode := -1; + FMetaHTTPEquiv := TIdMetaHTTPEquiv.Create(AHTTP); +end; + +destructor TIdHTTPResponse.Destroy; +begin + FreeAndNil(FMetaHTTPEquiv); + inherited Destroy; +end; + +procedure TIdHTTPResponse.Clear; +begin + inherited Clear; + FMetaHTTPEquiv.Clear; +end; + +procedure TIdHTTPResponse.ProcessMetaHTTPEquiv; +var + StdValues: TStringList; + I: Integer; + Name: String; +begin + FMetaHTTPEquiv.ProcessMetaHTTPEquiv(ContentStream); + if FMetaHTTPEquiv.RawHeaders.Count > 0 then begin + // TODO: optimize this + StdValues := TStringList.Create; + try + FMetaHTTPEquiv.RawHeaders.ConvertToStdValues(StdValues); + for I := 0 to StdValues.Count-1 do begin + Name := StdValues.Names[I]; + if Name <> '' then begin + RawHeaders.Values[Name] := IndyValueFromIndex(StdValues, I); + end; + end; + finally + StdValues.Free; + end; + ProcessHeaders; + end; + if FMetaHTTPEquiv.CharSet <> '' then begin + FCharSet := FMetaHTTPEquiv.CharSet; + end; +end; + +function TIdHTTPResponse.GetKeepAlive: Boolean; +begin + if FHTTP.Connected then begin + FHTTP.IOHandler.CheckForDisconnect(False); + end; + + FKeepAlive := FHTTP.Connected; + + if FKeepAlive then + begin + case FHTTP.ProtocolVersion of // TODO: use ResponseVersion instead? + pv1_1: + { By default we assume that keep-alive is by default and will close + the connection only there is "close" } + begin + FKeepAlive := not ( + TextIsSame(Trim(Connection), 'CLOSE') or {do not localize} + TextIsSame(Trim(ProxyConnection), 'CLOSE') {do not localize} + ); + end; + pv1_0: + { By default we assume that keep-alive is not by default and will keep + the connection only if there is "keep-alive" } + begin + FKeepAlive := + TextIsSame(Trim(Connection), 'KEEP-ALIVE') or {do not localize} + TextIsSame(Trim(ProxyConnection), 'KEEP-ALIVE') {do not localize} + { or ((ResponseVersion = pv1_1) and + (Length(Trim(Connection)) = 0) and + (Length(Trim(ProxyConnection)) = 0)) }; + end; + end; + end; + + Result := FKeepAlive; +end; + +function TIdHTTPResponse.GetResponseCode: Integer; +var + S, Tmp: string; +begin + if FResponseCode = -1 then + begin + S := FResponseText; + Fetch(S); + S := Trim(S); + // RLebeau: IIS supports status codes with decimals in them, but it is not supposed to + // transmit them to clients, which is a violation of RFC 2616. But have seen it happen, + // so check for it... + Tmp := Fetch(S, ' ', False); {do not localize} + S := Fetch(Tmp, '.', False); {do not localize} + FResponseCode := IndyStrToInt(S, -1); + end; + Result := FResponseCode; +end; + +procedure TIdHTTPResponse.SetResponseText(const AValue: String); +var + S: String; + i: TIdHTTPProtocolVersion; +begin + FResponseText := AValue; + FResponseCode := -1; // re-parse the next time it is accessed + ResponseVersion := pv1_0; // default until determined otherwise... + S := Copy(FResponseText, 6, 3); + for i := Low(TIdHTTPProtocolVersion) to High(TIdHTTPProtocolVersion) do begin + if TextIsSame(ProtocolVersionString[i], S) then begin + ResponseVersion := i; + Exit; + end; + end; +end; + +{ TIdHTTPRequest } + +constructor TIdHTTPRequest.Create(AHTTP: TIdCustomHTTP); +begin + inherited Create(AHTTP); + FHTTP := AHTTP; + FUseProxy := ctNormal; +end; + +{ TIdHTTPProtocol } + +constructor TIdHTTPProtocol.Create(AConnection: TIdCustomHTTP); +begin + inherited Create; + FHTTP := AConnection; + // Create the headers + FRequest := TIdHTTPRequest.Create(FHTTP); + FResponse := TIdHTTPResponse.Create(FHTTP); +end; + +destructor TIdHTTPProtocol.Destroy; +begin + FreeAndNil(FRequest); + FreeAndNil(FResponse); + + inherited Destroy; +end; + +procedure TIdHTTPProtocol.BuildAndSendRequest(AURI: TIdURI); +var + i: Integer; + LBufferingStarted: Boolean; +begin + // needed for Digest authentication, but maybe others as well... + if Assigned(Request.Authentication) then begin + // TODO: include entity body for Digest "auth-int" qop... + Request.Authentication.SetRequest(Request.Method, Request.URL); + end; + + // TODO: disable header folding for HTTP 1.0 requests + Request.SetHeaders; + FHTTP.ProxyParams.SetHeaders(Request.RawHeaders); + if Assigned(AURI) then begin + FHTTP.SetCookies(AURI, Request); + end; + + // This is a workaround for some HTTP servers which do not implement + // the HTTP protocol properly + LBufferingStarted := not FHTTP.IOHandler.WriteBufferingActive; + if LBufferingStarted then begin + FHTTP.IOHandler.WriteBufferOpen; + end; + try + FHTTP.IOHandler.WriteLn(Request.Method + ' ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize} + // write the headers + for i := 0 to Request.RawHeaders.Count - 1 do begin + if Length(Request.RawHeaders.Strings[i]) > 0 then begin + FHTTP.IOHandler.WriteLn(Request.RawHeaders.Strings[i]); + end; + end; + FHTTP.IOHandler.WriteLn(''); {do not localize} + if LBufferingStarted then begin + FHTTP.IOHandler.WriteBufferClose; + end; + except + if LBufferingStarted then begin + FHTTP.IOHandler.WriteBufferCancel; + end; + raise; + end; +end; + +procedure TIdHTTPProtocol.RetrieveHeaders(AMaxHeaderCount: integer); +var + s: string; + LHeaderCount: Integer; +begin + // Set the response headers + // Clear headers + // Don't use Capture. + // S.G. 6/4/2004: Added AmaxHeaderCount parameter to prevent the "header bombing" of the server + Response.Clear; + s := FHTTP.InternalReadLn; + try + LHeaderCount := 0; + while (s <> '') and ( (AMaxHeaderCount > 0) or (LHeaderCount < AMaxHeaderCount) ) do + begin + Response.RawHeaders.Add(S); + s := FHTTP.InternalReadLn; + Inc(LHeaderCount); + end; + except + on E: EIdConnClosedGracefully do begin + FHTTP.Disconnect; + end else begin + raise; + end; + end; + Response.ProcessHeaders; +end; + +function TIdHTTPProtocol.ProcessResponse(AIgnoreReplies: array of Int16): TIdHTTPWhatsNext; +var + LResponseCode, LResponseDigit: Integer; + + procedure CheckException; + var + i: Integer; + LTempResponse: TMemoryStream; + LTempStream: TStream; + begin + LTempResponse := TMemoryStream.Create; + try + LTempStream := Response.ContentStream; + Response.ContentStream := LTempResponse; + try + try + FHTTP.ReadResult(Request, Response); + except + on E: EIdConnClosedGracefully do begin + FHTTP.Disconnect; + end; + end; + if hoNoProtocolErrorException in FHTTP.HTTPOptions then begin + Exit; + end; + if High(AIgnoreReplies) > -1 then begin + for i := Low(AIgnoreReplies) to High(AIgnoreReplies) do begin + if LResponseCode = AIgnoreReplies[i] then begin + Exit; + end; + end; + end; + LTempResponse.Position := 0; + raise EIdHTTPProtocolException.CreateError(LResponseCode, FHTTP.ResponseText, + ReadStringAsCharset(LTempResponse, FHTTP.ResponseCharSet)); + finally + Response.ContentStream := LTempStream; + end; + finally + FreeAndNil(LTempResponse); + end; + end; + + procedure DiscardContent; + var + LOrigStream: TStream; + begin + LOrigStream := Response.ContentStream; + Response.ContentStream := nil; + try + try + FHTTP.ReadResult(Request, Response); + except + on E: EIdConnClosedGracefully do begin + FHTTP.Disconnect; + end; + end; + finally + Response.ContentStream := LOrigStream; + end; + end; + + function HeadersCanContinue: Boolean; + begin + Result := True; + if Assigned(FHTTP.OnHeadersAvailable) then begin + FHTTP.OnHeadersAvailable(FHTTP, Response.RawHeaders, Result); + end; + end; + +var + LLocation: string; + LMethod: TIdHTTPMethod; + LNeedAuth: Boolean; + //LTemp: Integer; +begin + + // provide the user with the headers and let the user decide + // whether the response processing should continue... + if not HeadersCanContinue then begin + Response.KeepAlive := False; // TODO: provide the user an option whether to force DoRequest() to disconnect the connection or not + Result := wnJustExit; + Exit; + end; + + // Cache this as ResponseCode calls GetResponseCode which parses it out + LResponseCode := Response.ResponseCode; + LResponseDigit := LResponseCode div 100; + LNeedAuth := False; + + // Handle Redirects + // RLebeau: All 3xx replies other than 304 are redirects. Reply 201 has a + // Location header but is NOT a redirect! + + // RLebeau 4/21/2011: Amazon S3 includes a Location header in its 200 reply + // to some PUT requests. Not sure if this is a bug or intentional, but we + // should NOT perform a redirect for any replies other than 3xx. Amazon S3 + // does NOT include a Location header in its 301 reply, though! This is + // intentional, per Amazon's documentation, as a way for developers to + // detect when URLs are addressed incorrectly... + + if (LResponseDigit = 3) and (LResponseCode <> 304) then + begin + if Response.Location = '' then begin + CheckException; + Result := wnJustExit; + Exit; + end; + + Inc(FHTTP.FRedirectCount); + + // LLocation := TIdURI.URLDecode(Response.Location); + LLocation := Response.Location; + LMethod := Request.Method; + + // fire the event + if not FHTTP.DoOnRedirect(LLocation, LMethod, FHTTP.FRedirectCount) then begin + CheckException; + Result := wnJustExit; + Exit; + end; + + if (FHTTP.FHandleRedirects) and (FHTTP.FRedirectCount < FHTTP.FRedirectMax) then begin + Result := wnGoToURL; + Request.URL := LLocation; + + // GDG 21/11/2003. If it's a 303, we should do a get this time + + // RLebeau 7/15/2004 - do a GET on 302 as well, as mentioned in RFC 2616 + + // RLebeau 1/11/2008 - turns out both situations are WRONG! RFCs 2068 and + // 2616 specifically state that changing the method to GET in response + // to 302 and 303 is errorneous. Indy 9 did it right by reusing the + // original method and source again and only changing the URL, so lets + // revert back to that same behavior! + + // RLebeau 12/28/2012 - one more time. RFCs 2068 and 2616 actually say that + // changing the method in response to 302 is erroneous, but changing the + // method to GET in response to 303 is intentional and why 303 was introduced + // in the first place. Erroneous clients treat 302 as 303, though. Now + // encountering servers that actually expect this 303 behavior, so we have + // to enable it again! Adding an optional HTTPOption flag so clients can + // enable the erroneous 302 behavior if they really need it. + + if ((LResponseCode = 302) and (hoTreat302Like303 in FHTTP.HTTPOptions)) or + (LResponseCode = 303) then + begin + Request.Source := nil; + Request.Method := Id_HTTPMethodGet; + end else begin + Request.Method := LMethod; + end; + Request.MethodOverride := ''; + end else begin + Result := wnJustExit; + Response.Location := LLocation; + end; + + if FHTTP.Connected then begin + // This is a workaround for buggy HTTP 1.1 servers which + // does not return any body with 302 response code + DiscardContent; // may wait a few seconds for any kind of content + end; + end else begin + //Ciaran, 30th Nov 2004: I commented out the following code. When a https server + //sends a disconnect immediately after sending the requested page in an SSL + //session (which they sometimes do to indicate a "session" is finished), the code + //below causes a "Connection closed gracefully" exception BUT the returned page + //is lost (IOHandler.Request is empty). If the code below is re-enabled by + //someone for whatever reason, they MUST test for this case. + // GREGOR Workaround + // if we get an error we disconnect if we use SSLIOHandler + //if Assigned(FHTTP.IOHandler) then + //begin + // Response.KeepAlive := not (FHTTP.Connected and (FHTTP.IOHandler is TIdSSLIOHandlerSocketBase) and Response.KeepAlive); + //end; + + // RLebeau 2/15/2006: RFC 1945 states the following: + // + // For response messages, whether or not an entity body is included with + // a message is dependent on both the request method and the response + // code. All responses to the HEAD request method must not include a + // body, even though the presence of entity header fields may lead one + // to believe they do. All 1xx (informational), 204 (no content), and + // 304 (not modified) responses must not include a body. All other + // responses must include an entity body or a Content-Length header + // field defined with a value of zero (0). + + if LResponseDigit <> 2 then begin + case LResponseCode of + 401: + begin // HTTP Server authorization required + if (FHTTP.AuthRetries >= FHTTP.MaxAuthRetries) or + (not FHTTP.DoOnAuthorization(Request, Response)) then begin + if Assigned(Request.Authentication) then begin + Request.Authentication.Reset; + end; + CheckException; + Result := wnJustExit; + Exit; + end else begin + LNeedAuth := hoInProcessAuth in FHTTP.HTTPOptions; + end; + end; + 407: + begin // Proxy Server authorization requered + if (FHTTP.AuthProxyRetries >= FHTTP.MaxAuthRetries) or + (not FHTTP.DoOnProxyAuthorization(Request, Response)) then + begin + if Assigned(FHTTP.ProxyParams.Authentication) then begin + FHTTP.ProxyParams.Authentication.Reset; + end; + CheckException; + Result := wnJustExit; + Exit; + end else begin + LNeedAuth := hoInProcessAuth in FHTTP.HTTPOptions; + end; + end; + else begin + CheckException; + Result := wnJustExit; + Exit; + end; + end; + end; + + if LNeedAuth then begin + // discard the content of Error message + DiscardContent; + Result := wnAuthRequest; + end else + begin + // RLebeau 6/30/2006: DO NOT READ IF THE REQUEST IS HEAD!!! + // The server is supposed to send a 'Content-Length' header + // without sending the actual data... + if TextIsSame(Request.Method, Id_HTTPMethodHead) or + TextIsSame(Request.MethodOverride, Id_HTTPMethodHead) or + (LResponseCode = 204) then + begin + // Have noticed one case where a non-conforming server did send an + // entity body in response to a HEAD request. If requested, ignore + // anything the server may send by accident + DiscardContent; + end else begin + FHTTP.ReadResult(Request, Response); + end; + Result := wnJustExit; + end; + end; +end; + +function TIdCustomHTTP.CreateProtocol: TIdHTTPProtocol; +begin + Result := TIdHTTPProtocol.Create(Self); +end; + +procedure TIdCustomHTTP.InitComponent; +begin + inherited; + FURI := TIdURI.Create(''); + + FAuthRetries := 0; + FAuthProxyRetries := 0; + AllowCookies := True; + FImplicitCookieManager := False; + FOptions := [hoForceEncodeParams]; + + FRedirectMax := Id_TIdHTTP_RedirectMax; + FHandleRedirects := Id_TIdHTTP_HandleRedirects; + // + FProtocolVersion := Id_TIdHTTP_ProtocolVersion; + + FHTTPProto := CreateProtocol; + FProxyParameters := TIdProxyConnectionInfo.Create; + FProxyParameters.Clear; + + FMaxAuthRetries := Id_TIdHTTP_MaxAuthRetries; + FMaxHeaderLines := Id_TIdHTTP_MaxHeaderLines; +end; + +function TIdCustomHTTP.InternalReadLn: String; +begin + Result := IOHandler.ReadLn; + if IOHandler.ReadLnTimedout then begin + raise EIdReadTimeout.Create(RSReadTimeout); + end; +end; + +function TIdCustomHTTP.Get(AURL: string; AIgnoreReplies: array of Int16 + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LStream: TMemoryStream; +begin + LStream := TMemoryStream.Create; + try + Get(AURL, LStream, AIgnoreReplies); + LStream.Position := 0; + Result := ReadStringAsCharset(LStream, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LStream); + end; +end; + +procedure TIdCustomHTTP.Get(AURL: string; AResponseContent: TStream; + AIgnoreReplies: array of Int16); +begin + DoRequest(Id_HTTPMethodGet, AURL, nil, AResponseContent, AIgnoreReplies); +end; + +procedure TIdCustomHTTP.DoRequest(const AMethod: TIdHTTPMethod; + AURL: string; ASource, AResponseContent: TStream; + AIgnoreReplies: array of Int16); +var + LResponseLocation: TIdStreamSize; +begin + //reset any counters + FRedirectCount := 0; + FAuthRetries := 0; + FAuthProxyRetries := 0; + + if Assigned(AResponseContent) then begin + LResponseLocation := AResponseContent.Position; + end else begin + LResponseLocation := 0; // Just to avoid the warning message + end; + + Request.URL := AURL; + Request.Method := AMethod; + Request.Source := ASource; + Response.ContentStream := AResponseContent; + + try + repeat + PrepareRequest(Request); + if IOHandler is TIdSSLIOHandlerSocketBase then begin + TIdSSLIOHandlerSocketBase(IOHandler).URIToCheck := FURI.URI; + end; + ConnectToHost(Request, Response); + + // Workaround for servers wich respond with 100 Continue on GET and HEAD + // This workaround is just for temporary use until we have final HTTP 1.1 + // realisation. HTTP 1.1 is ongoing because of all the buggy and conflicting servers. + repeat + Response.ResponseText := InternalReadLn; + FHTTPProto.RetrieveHeaders(MaxHeaderLines); + ProcessCookies(Request, Response); + until Response.ResponseCode <> 100; + + case FHTTPProto.ProcessResponse(AIgnoreReplies) of + wnAuthRequest: + begin + Request.URL := AURL; + end; + wnReadAndGo: + begin + ReadResult(Request, Response); + if Assigned(AResponseContent) then begin + AResponseContent.Position := LResponseLocation; + AResponseContent.Size := LResponseLocation; + end; + FAuthRetries := 0; + FAuthProxyRetries := 0; + end; + wnGoToURL: + begin + if Assigned(AResponseContent) then begin + AResponseContent.Position := LResponseLocation; + AResponseContent.Size := LResponseLocation; + end; + FAuthRetries := 0; + FAuthProxyRetries := 0; + end; + wnJustExit: + begin + Break; + end; + wnDontKnow: + begin + raise EIdException.Create(RSHTTPNotAcceptable); + end; + end; + until False; + finally + if not Response.KeepAlive then begin + // TODO: do not disconnect if hoNoReadMultipartMIME is in effect + Disconnect; + end; + end; +end; + +procedure TIdCustomHTTP.Patch(AURL: string; ASource, AResponseContent: TStream); +begin + DoRequest(Id_HTTPMethodPatch, AURL, ASource, AResponseContent, []); +end; + +function TIdCustomHTTP.Patch(AURL: string; ASource: TStream + {$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF} + ): string; +var + LResponse: TMemoryStream; +begin + LResponse := TMemoryStream.Create; + try + Patch(AURL, ASource, LResponse); + LResponse.Position := 0; + Result := ReadStringAsCharset(LResponse, ResponseCharset{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}); + // TODO: if the data is XML, add/update the declared encoding to 'UTF-16LE'... + finally + FreeAndNil(LResponse); + end; +end; + +end. + diff --git a/indy/Protocols/IdHTTPHeaderInfo.pas b/indy/Protocols/IdHTTPHeaderInfo.pas new file mode 100644 index 0000000..96d12c8 --- /dev/null +++ b/indy/Protocols/IdHTTPHeaderInfo.pas @@ -0,0 +1,1223 @@ + +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 2/16/2005 7:58:56 AM DSiders + Modified TIdRequestHeaderInfo to restore the Range property. + Modified TIdRequestHeaderInfo methods AssignTo, Clear, ProcessHeaders, and + SetHeaders to include Range property. + + Rev 1.8 11/11/2004 12:55:38 AM DSiders + Modified TIdEntityHeaderInfo to fix problems with content-range header + handling. + Added ContentRangeInstanceLength property. + Added HasContentRange property (read-ony). + Added HasContentRangeInstance property (read-only). + Moved reading and writing methods to ProcessHeaders and SetHeaders in + TIdEntityHeaderInfo. + + Rev 1.7 6/8/2004 10:35:46 AM BGooijen + fixed overflow + + Rev 1.6 2004.02.03 5:43:46 PM czhower + Name changes + + Rev 1.5 1/22/2004 7:10:08 AM JPMugaas + Tried to fix AnsiSameText depreciation. + + Rev 1.4 13.1.2004 . 17:17:44 DBondzhev + moved few methods into protected section to remove some warnings + + Rev 1.3 10/17/2003 12:09:28 AM DSiders + Added localization comments. + + Rev 1.2 20/4/2003 3:46:34 PM SGrobety + Fix to previous fix... (Dumb me) + + Rev 1.1 20/4/2003 3:33:58 PM SGrobety + Changed Content-type default in TIdEntityHeaderInfo back to empty string + and changed the default of the response object. Solved compatibility + issue with Netscape servers + + Rev 1.0 11/13/2002 07:54:24 AM JPMugaas +} + +unit IdHTTPHeaderInfo; + +{ + HTTP Header definition - RFC 2616 + Author: Doychin Bondzhev (doychin@dsoft-bg.com) +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAuthentication, + IdGlobal, + IdGlobalProtocols, + IdHeaderList; + +type + TIdEntityHeaderInfo = class(TPersistent) + protected + {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FOwner: TPersistent; + FCacheControl: String; + FRawHeaders: TIdHeaderList; + FCharSet: String; + FConnection: string; + FContentDisposition: string; + FContentEncoding: string; + FContentLanguage: string; + FContentLength: Int64; + FContentRangeEnd: Int64; + FContentRangeStart: Int64; + FContentRangeInstanceLength: Int64; + FContentRangeUnits: String; + FContentType: string; + FContentVersion: string; + FCustomHeaders: TIdHeaderList; + FDate: TDateTime; + FExpires: TDateTime; + FETag: string; + FLastModified: TDateTime; + FPragma: string; + FHasContentLength: Boolean; + FTransferEncoding: String; + // + procedure AssignTo(Destination: TPersistent); override; + procedure ProcessHeaders; virtual; + procedure SetHeaders; virtual; + function GetOwner: TPersistent; override; + function GetOwnerComponent: TComponent; + + procedure SetContentLength(const AValue: Int64); + procedure SetContentType(const AValue: String); + procedure SetCustomHeaders(const AValue: TIdHeaderList); + function GetHasContentRange: Boolean; + function GetHasContentRangeInstance: Boolean; + public + procedure AfterConstruction; override; + procedure Clear; virtual; + constructor Create(AOwner: TPersistent); virtual; + destructor Destroy; override; + // + property OwnerComponent: TComponent read GetOwnerComponent; + property HasContentLength: Boolean read FHasContentLength; + property HasContentRange: Boolean read GetHasContentRange; + property HasContentRangeInstance: Boolean read GetHasContentRangeInstance; + property RawHeaders: TIdHeaderList read FRawHeaders; + published + property CacheControl: String read FCacheControl write FCacheControl; + property CharSet: String read FCharSet write FCharSet; + property Connection: string read FConnection write FConnection; + property ContentDisposition: string read FContentDisposition write FContentDisposition; + property ContentEncoding: string read FContentEncoding write FContentEncoding; + property ContentLanguage: string read FContentLanguage write FContentLanguage; + property ContentLength: Int64 read FContentLength write SetContentLength; + property ContentRangeEnd: Int64 read FContentRangeEnd write FContentRangeEnd; + property ContentRangeStart: Int64 read FContentRangeStart write FContentRangeStart; + property ContentRangeInstanceLength: Int64 read FContentRangeInstanceLength write FContentRangeInstanceLength; + property ContentRangeUnits: String read FContentRangeUnits write FContentRangeUnits; + property ContentType: string read FContentType write SetContentType; + property ContentVersion: string read FContentVersion write FContentVersion; + property CustomHeaders: TIdHeaderList read FCustomHeaders write SetCustomHeaders; + property Date: TDateTime read FDate write FDate; + property ETag: string read FETag write FETag; + property Expires: TDateTime read FExpires write FExpires; + property LastModified: TDateTime read FLastModified write FLastModified; + property Pragma: string read FPragma write FPragma; + property TransferEncoding: string read FTransferEncoding write FTransferEncoding; + end; + + TIdProxyConnectionInfo = class(TPersistent) + protected + FAuthentication: TIdAuthentication; + FPassword: string; + FPort: Integer; + FServer: string; + FUsername: string; + FBasicByDefault: Boolean; + + procedure AssignTo(Destination: TPersistent); override; + procedure SetProxyPort(const Value: Integer); + procedure SetProxyServer(const Value: string); + public + procedure AfterConstruction; override; + constructor Create; + procedure Clear; + destructor Destroy; override; + procedure SetHeaders(Headers: TIdHeaderList); + // + property Authentication: TIdAuthentication read FAuthentication write FAuthentication; + published + + property BasicAuthentication: boolean read FBasicByDefault write FBasicByDefault; + property ProxyPassword: string read FPassword write FPassword; + property ProxyPort: Integer read FPort write SetProxyPort; + property ProxyServer: string read FServer write SetProxyServer; + property ProxyUsername: string read FUsername write FUserName; + end; + + TIdEntityRange = class(TCollectionItem) + protected + FStartPos: Int64; + FEndPos: Int64; + FSuffixLength: Int64; + function GetText: String; + procedure SetText(const AValue: String); + public + constructor Create(Collection: TCollection); override; + published + property StartPos: Int64 read FStartPos write FStartPos; + property EndPos: Int64 read FEndPos write FEndPos; + property SuffixLength: Int64 read FSuffixLength write FSuffixLength; + property Text: String read GetText write SetText; + end; + + TIdEntityRanges = class(TOwnedCollection) + protected + FUnits: String; + function GetRange(Index: Integer): TIdEntityRange; + procedure SetRange(Index: Integer; AValue: TIdEntityRange); + function GetText: String; + procedure SetText(const AValue: String); + procedure SetUnits(const AValue: String); + public + constructor Create(AOwner: TPersistent); reintroduce; + function Add: TIdEntityRange; reintroduce; + property Ranges[Index: Integer]: TIdEntityRange read GetRange write SetRange; default; + published + property Text: String read GetText write SetText; + property Units: String read FUnits write SetUnits; + end; + + TIdRequestHeaderInfo = class(TIdEntityHeaderInfo) + protected + FAccept: String; + FAcceptCharSet: String; + FAcceptEncoding: String; + FAcceptLanguage: String; + FExpect: String; + FFrom: String; + FPassword: String; + FReferer: String; + FUserAgent: String; + FUserName: String; + FHost: String; + FProxyConnection: String; + FRanges: TIdEntityRanges; + FBasicByDefault: Boolean; + FAuthentication: TIdAuthentication; + FMethodOverride: String; + // + procedure AssignTo(Destination: TPersistent); override; + procedure ProcessHeaders; override; + procedure SetHeaders; override; + function GetRange: String; + procedure SetRange(const AValue: String); + procedure SetRanges(AValue: TIdEntityRanges); + public + // + constructor Create(AOwner: TPersistent); override; + destructor Destroy; override; + procedure Clear; override; + property Authentication: TIdAuthentication read FAuthentication write FAuthentication; + published + property Accept: String read FAccept write FAccept; + property AcceptCharSet: String read FAcceptCharSet write FAcceptCharSet; + property AcceptEncoding: String read FAcceptEncoding write FAcceptEncoding; + property AcceptLanguage: String read FAcceptLanguage write FAcceptLanguage; + property BasicAuthentication: boolean read FBasicByDefault write FBasicByDefault; + property Host: String read FHost write FHost; + property From: String read FFrom write FFrom; + property Password: String read FPassword write FPassword; + property Referer: String read FReferer write FReferer; + property UserAgent: String read FUserAgent write FUserAgent; + property Username: String read FUsername write FUsername; + property ProxyConnection: String read FProxyConnection write FProxyConnection; + property Range: String read GetRange write SetRange; //deprecated 'Use Ranges property'; + property Ranges: TIdEntityRanges read FRanges write SetRanges; + property MethodOverride: String read FMethodOverride write FMethodOverride; + end; + + TIdResponseHeaderInfo = class(TIdEntityHeaderInfo) + protected + FAcceptPatch: string; + FAcceptRanges: string; + FLocation: string; + FServer: string; + FProxyConnection: string; + FProxyAuthenticate: TIdHeaderList; + FWWWAuthenticate: TIdHeaderList; + // + procedure SetProxyAuthenticate(const Value: TIdHeaderList); + procedure SetWWWAuthenticate(const Value: TIdHeaderList); + procedure SetAcceptPatch(const Value: string); + procedure SetAcceptRanges(const Value: string); + procedure ProcessHeaders; override; + procedure SetHeaders; override; + public + + procedure Clear; override; + constructor Create(AOwner: TPersistent); override; + destructor Destroy; override; + published + property AcceptPatch: string read FAcceptPatch write SetAcceptPatch; + property AcceptRanges: string read FAcceptRanges write SetAcceptRanges; + property Location: string read FLocation write FLocation; + property ProxyConnection: string read FProxyConnection write FProxyConnection; + property ProxyAuthenticate: TIdHeaderList read FProxyAuthenticate write SetProxyAuthenticate; + property Server: string read FServer write FServer; + property WWWAuthenticate: TIdHeaderList read FWWWAuthenticate write SetWWWAuthenticate; + end; + + TIdMetaHTTPEquiv = class(TIdEntityHeaderInfo) + public + procedure ProcessMetaHTTPEquiv(AStream: TStream); + end; + +var + GIdDefaultUserAgent: String = 'Mozilla/3.0 (compatible; Indy Library)'; {do not localize} + +implementation + +uses + SysUtils; + +{ TIdEntityHeaderInfo } + +constructor TIdEntityHeaderInfo.Create(AOwner: TPersistent); +begin + inherited Create; + FOwner := AOwner; + // HTTP does not fold headers based on line length + FRawHeaders := TIdHeaderList.Create(QuoteHTTP); + FRawHeaders.FoldLength := MaxInt; + FCustomHeaders := TIdHeaderList.Create(QuoteHTTP); + FCustomHeaders.FoldLength := MaxInt; +end; + +procedure TIdEntityHeaderInfo.AfterConstruction; +begin + inherited AfterConstruction; + Clear; +end; + +destructor TIdEntityHeaderInfo.Destroy; +begin + FreeAndNil(FRawHeaders); + FreeAndNil(FCustomHeaders); + inherited Destroy; +end; + +procedure TIdEntityHeaderInfo.AssignTo(Destination: TPersistent); +var + LDest: TIdEntityHeaderInfo; +begin + if Destination is TIdEntityHeaderInfo then + begin + LDest := TIdEntityHeaderInfo(Destination); + LDest.FRawHeaders.Assign(FRawHeaders); + LDest.FCacheControl := FCacheControl; + LDest.FCharSet := FCharSet; + LDest.FContentDisposition := FContentDisposition; + LDest.FContentEncoding := FContentEncoding; + LDest.FContentLanguage := FContentLanguage; + LDest.FContentType := FContentType; + LDest.FContentVersion := FContentVersion; + LDest.FContentLength := FContentLength; + LDest.FContentRangeEnd:= FContentRangeEnd; + LDest.FContentRangeStart:= FContentRangeStart; + LDest.FContentRangeInstanceLength := FContentRangeInstanceLength; + LDest.FContentRangeUnits := FContentRangeUnits; + LDest.FDate := FDate; + LDest.FETag := FETag; + LDest.FExpires := FExpires; + LDest.FLastModified := FLastModified; + end else + begin + inherited AssignTo(Destination); + end; +end; + +procedure TIdEntityHeaderInfo.Clear; +begin + FCacheControl := ''; + FCharSet := ''; + FConnection := ''; + FContentVersion := ''; + FContentDisposition := ''; + FContentEncoding := ''; + FContentLanguage := ''; + + { S.G. 20/4/2003 + + Was FContentType := 'Text/HTML' + + Shouldn't be set here but in response. + Requests, by default, have NO content-type. + This caused problems with some netscape servers + } + FContentType := ''; + + FContentLength := -1; + FContentRangeStart := -1; + FContentRangeEnd := -1; + FContentRangeInstanceLength := -1; + FContentRangeUnits := ''; + FDate := 0; + FLastModified := 0; + FETag := ''; + FExpires := 0; + FRawHeaders.Clear; +end; + +procedure TIdEntityHeaderInfo.ProcessHeaders; +var + LSecs: Int64; + lValue: string; + lCRange: string; + lILength: string; +begin + FCacheControl := FRawHeaders.Values['Cache-control']; {do not localize} + FConnection := FRawHeaders.Values['Connection']; {do not localize} + FContentVersion := FRawHeaders.Values['Content-Version']; {do not localize} + FContentDisposition := FRawHeaders.Values['Content-Disposition']; {do not localize} + FContentEncoding := FRawHeaders.Values['Content-Encoding']; {do not localize} + FContentLanguage := FRawHeaders.Values['Content-Language']; {do not localize} + ContentType := FRawHeaders.Values['Content-Type']; {do not localize} + FContentLength := IndyStrToInt64(FRawHeaders.Values['Content-Length'], -1); {do not localize} + FHasContentLength := FContentLength >= 0; + + FContentRangeStart := -1; + FContentRangeEnd := -1; + FContentRangeInstanceLength := -1; + FContentRangeUnits := ''; + + { + handle content-range headers, like: + + content-range: bytes 1-65536/102400 + content-range: bytes */102400 + content-range: bytes 1-65536/* + } + lValue := FRawHeaders.Values['Content-Range']; {do not localize} + if lValue <> '' then + begin + // strip the bytes unit, and keep the range and instance info + FContentRangeUnits := Fetch(lValue); + lCRange := Fetch(lValue, '/'); + lILength := Fetch(lValue); + + FContentRangeStart := IndyStrToInt64(Fetch(lCRange, '-'), -1); + FContentRangeEnd := IndyStrToInt64(lCRange, -1); + FContentRangeInstanceLength := IndyStrToInt64(lILength, -1); + end; + + // RLebeau 03/04/2009: RFC 2616 Section 14.18 says: + // + // "A received message that does not have a Date header field MUST be + // assigned one by the recipient if the message will be cached by that + // recipient or gatewayed via a protocol which requires a Date." + + lValue := FRawHeaders.Values['Date']; {do not localize} + if lValue <> '' then + begin + FDate := GMTToLocalDateTime(lValue); + end else + begin + FDate := Now; + end; + + FLastModified := GMTToLocalDateTime(FRawHeaders.Values['Last-Modified']); {do not localize} + + // RLebeau 01/23/2006 - IIS fix + lValue := FRawHeaders.Values['Expires']; {do not localize} + if IsNumeric(lValue) then + begin + // This is happening when expires is an integer number in seconds + LSecs := IndyStrToInt64(lValue); + // RLebeau 01/23/2005 - IIS sometimes sends an 'Expires: -1' header + // should we be handling it as actually meaning "Now minus 1 second" instead? + if LSecs >= 0 then begin + FExpires := Now + (LSecs / SecsPerDay); + end else begin + FExpires := 0.0; + end; + end else + begin + // RLebeau 03/04/2009: RFC 2616 Section 14.21 says: + // + // "The format is an absolute date and time as defined by HTTP-date in + // section 3.3.1; it MUST be in RFC 1123 date format: + // + // Expires = "Expires" ":" HTTP-date + // + // HTTP/1.1 clients and caches MUST treat other invalid date formats, + // especially including the value "0", as in the past (i.e., "already + // expired")." + + try + FExpires := GMTToLocalDateTime(lValue); + except + FExpires := Now - (1 / SecsPerDay); + end; + end; + + FETag := FRawHeaders.Values['ETag']; {do not localize} + FPragma := FRawHeaders.Values['Pragma']; {do not localize} + FTransferEncoding := FRawHeaders.Values['Transfer-Encoding']; {do not localize} +end; + +procedure TIdEntityHeaderInfo.SetHeaders; +begin + FRawHeaders.Clear; + if Length(FConnection) > 0 then + begin + FRawHeaders.Values['Connection'] := FConnection; {do not localize} + end; + if Length(FContentVersion) > 0 then + begin + FRawHeaders.Values['Content-Version'] := FContentVersion; {do not localize} + end; + if Length(FContentDisposition) > 0 then + begin + FRawHeaders.Values['Content-Disposition'] := FContentDisposition; {do not localize} + end; + if Length(FContentEncoding) > 0 then + begin + FRawHeaders.Values['Content-Encoding'] := FContentEncoding; {do not localize} + end; + if Length(FContentLanguage) > 0 then + begin + FRawHeaders.Values['Content-Language'] := FContentLanguage; {do not localize} + end; + if Length(FContentType) > 0 then + begin + FRawHeaders.Values['Content-Type'] := FContentType; {do not localize} + FRawHeaders.Params['Content-Type', 'charset'] := FCharSet; {do not localize} + end; + if FContentLength >= 0 then + begin + FRawHeaders.Values['Content-Length'] := IntToStr(FContentLength); {do not localize} + end; + + { removed setting Content-Range header for entities... deferred to response } + + if Length(FCacheControl) > 0 then + begin + FRawHeaders.Values['Cache-control'] := FCacheControl; {do not localize} + end; + if FDate > 0 then + begin + FRawHeaders.Values['Date'] := LocalDateTimeToHttpStr(FDate); {do not localize} + end; + if Length(FETag) > 0 then + begin + FRawHeaders.Values['ETag'] := FETag; {do not localize} + end; + if FExpires > 0 then + begin + FRawHeaders.Values['Expires'] := LocalDateTimeToHttpStr(FExpires); {do not localize} + end; + if Length(FPragma) > 0 then + begin + FRawHeaders.Values['Pragma'] := FPragma; {do not localize} + end; + if Length(FTransferEncoding) > 0 then + begin + FRawHeaders.Values['Transfer-Encoding'] := FTransferEncoding; {do not localize} + end; + if FCustomHeaders.Count > 0 then + begin + // append custom headers + // TODO: use AddStrings() instead? + FRawHeaders.Text := FRawHeaders.Text + FCustomHeaders.Text; + end; +end; + +procedure TIdEntityHeaderInfo.SetCustomHeaders(const AValue: TIdHeaderList); +begin + FCustomHeaders.Assign(AValue); +end; + +procedure TIdEntityHeaderInfo.SetContentLength(const AValue: Int64); +begin + FContentLength := AValue; + FHasContentLength := FContentLength >= 0; +end; + +procedure TIdEntityHeaderInfo.SetContentType(const AValue: String); +var + S, LCharSet: string; + LComp: TComponent; +begin + if AValue <> '' then begin + FContentType := RemoveHeaderEntry(AValue, 'charset', LCharSet, QuoteHTTP); {do not localize} + + {RLebeau: the ContentType property is streamed after the CharSet property, + so do not overwrite it during streaming} + LComp := OwnerComponent; + if Assigned(LComp) and (csReading in LComp.ComponentState) then begin + Exit; + end; + + // RLebeau: per RFC 2616 Section 3.7.1: + // + // The "charset" parameter is used with some media types to define the + // character set (section 3.4) of the data. When no explicit charset + // parameter is provided by the sender, media subtypes of the "text" + // type are defined to have a default charset value of "ISO-8859-1" when + // received via HTTP. Data in character sets other than "ISO-8859-1" or + // its subsets MUST be labeled with an appropriate charset value. See + // section 3.4.1 for compatibility problems. + + // RLebeau: per RFC 3023 Sections 3.1, 3.3, 3.6, and 8.5: + // + // Conformant with [RFC2046], if a text/xml entity is received with + // the charset parameter omitted, MIME processors and XML processors + // MUST use the default charset value of "us-ascii"[ASCII]. In cases + // where the XML MIME entity is transmitted via HTTP, the default + // charset value is still "us-ascii". (Note: There is an + // inconsistency between this specification and HTTP/1.1, which uses + // ISO-8859-1[ISO8859] as the default for a historical reason. Since + // XML is a new format, a new default should be chosen for better + // I18N. US-ASCII was chosen, since it is the intersection of UTF-8 + // and ISO-8859-1 and since it is already used by MIME.) + // + // ... + // + // The charset parameter of text/xml-external-parsed-entity is + // handled the same as that of text/xml as described in Section 3.1 + // + // ... + // + // The following list applies to text/xml, text/xml-external-parsed- + // entity, and XML-based media types under the top-level type "text" + // that define the charset parameter according to this specification: + // + // - If the charset parameter is not specified, the default is "us- + // ascii". The default of "iso-8859-1" in HTTP is explicitly + // overridden. + // + // ... + // + // Omitting the charset parameter is NOT RECOMMENDED for text/xml. For + // example, even if the contents of the XML MIME entity are UTF-16 or + // UTF-8, or the XML MIME entity has an explicit encoding declaration, + // XML and MIME processors MUST assume the charset is "us-ascii". + + if (LCharSet = '') and IsHeaderMediaType(FContentType, 'text') then begin {do not localize} + S := ExtractHeaderMediaSubType(FContentType); + if (PosInStrArray(S, ['xml', 'xml-external-parsed-entity'], False) >= 0) or TextEndsWith(S, '+xml') then begin {do not localize} + LCharSet := 'us-ascii'; {do not localize} + end else begin + LCharSet := 'ISO-8859-1'; {do not localize} + end; + end; + + {RLebeau: override the current CharSet only if the header specifies a new value} + if LCharSet <> '' then begin + FCharSet := LCharSet; + end; + end else begin + FContentType := ''; + FCharSet := ''; + end; +end; + +function TIdEntityHeaderInfo.GetHasContentRange: Boolean; +begin + Result := (FContentRangeEnd >= 0); +end; + +function TIdEntityHeaderInfo.GetHasContentRangeInstance: Boolean; +begin + Result := (FContentRangeInstanceLength >= 0); +end; + +function TIdEntityHeaderInfo.GetOwner: TPersistent; +begin + Result := FOwner; +end; + +type + TPersistentAccess = class(TPersistent) + end; + +function TIdEntityHeaderInfo.GetOwnerComponent: TComponent; +var + // under ARC, convert a weak reference to a strong reference before working with it + LOwner: TPersistent; +begin + Result := nil; + LOwner := GetOwner; + while LOwner <> nil do begin + if LOwner is TComponent then begin + Result := TComponent(LOwner); + Exit; + end; + LOwner := TPersistentAccess(LOwner).GetOwner; + end; +end; + +{ TIdProxyConnectionInfo } + +constructor TIdProxyConnectionInfo.Create; +begin + inherited Create; +end; + +procedure TIdProxyConnectionInfo.AfterConstruction; +begin + inherited AfterConstruction; + Clear; +end; + +destructor TIdProxyConnectionInfo.Destroy; +begin + FreeAndNil(FAuthentication); + inherited Destroy; +end; + +procedure TIdProxyConnectionInfo.AssignTo(Destination: TPersistent); +var + LDest: TIdProxyConnectionInfo; +begin + if Destination is TIdProxyConnectionInfo then + begin + LDest := TIdProxyConnectionInfo(Destination); + LDest.FPassword := FPassword; + LDest.FPort := FPort; + LDest.FServer := FServer; + LDest.FUsername := FUsername; + LDest.FBasicByDefault := FBasicByDefault; + end else + begin + inherited AssignTo(Destination); + end; +end; + +procedure TIdProxyConnectionInfo.Clear; +begin + FServer := ''; + FUsername := ''; + FPassword := ''; + FPort := 0; +end; + +procedure TIdProxyConnectionInfo.SetHeaders(Headers: TIdHeaderList); +var + S: String; +begin + if Assigned(Authentication) then begin + S := Authentication.Authentication; + end + // Use Basic authentication by default + else if FBasicByDefault then begin + FAuthentication := TIdBasicAuthentication.Create; + // TODO: use FAuthentication Username/Password properties instead + FAuthentication.Params.Values['Username'] := FUsername; {do not localize} + FAuthentication.Params.Values['Password'] := FPassword; {do not localize} + S := FAuthentication.Authentication; + end else begin + S := ''; + end; + if Length(S) > 0 then begin + Headers.Values['Proxy-Authorization'] := S; {do not localize} + end; +end; + +procedure TIdProxyConnectionInfo.SetProxyPort(const Value: Integer); +begin + if Value <> FPort then + begin + FreeAndNil(FAuthentication); + end; + FPort := Value; +end; + +procedure TIdProxyConnectionInfo.SetProxyServer(const Value: string); +begin + if not TextIsSame(Value, FServer) then + begin + FreeAndNil(FAuthentication); + end; + FServer := Value; +end; + +{ TIdEntityRange } + +constructor TIdEntityRange.Create(Collection: TCollection); +begin + inherited Create(Collection); + FStartPos := -1; + FEndPos := -1; + FSuffixLength := -1; +end; + +function TIdEntityRange.GetText: String; +begin + if (FStartPos >= 0) or (FEndPos >= 0) then + begin + if FEndPos >= 0 then + begin + Result := IntToStr(FStartPos) + '-' + IntToStr(FEndPos); {do not localize} + end else begin + Result := IntToStr(FStartPos) + '-'; {do not localize} + end; + end + else if FSuffixLength >= 0 then begin + Result := '-' + IntToStr(FSuffixLength); + end + else begin + Result := ''; + end; +end; + +procedure TIdEntityRange.SetText(const AValue: String); +var + LValue, S: String; +begin + LValue := Trim(AValue); + if LValue <> '' then + begin + S := Fetch(LValue, '-'); {do not localize} + if S <> '' then begin + FStartPos := StrToInt64Def(S, -1); + FEndPos := StrToInt64Def(Fetch(LValue), -1); + FSuffixLength := -1; + end else begin + FStartPos := -1; + FEndPos := -1; + FSuffixLength := StrToInt64Def(Fetch(LValue), -1); + end; + end else begin + FStartPos := -1; + FEndPos := -1; + FSuffixLength := -1; + end; +end; + +{ TIdEntityRanges } + +constructor TIdEntityRanges.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdEntityRange); + FUnits := 'bytes'; {do not localize} +end; + +function TIdEntityRanges.Add: TIdEntityRange; +begin + Result := TIdEntityRange(inherited Add); +end; + +function TIdEntityRanges.GetRange(Index: Integer): TIdEntityRange; +begin + Result := TIdEntityRange(inherited GetItem(Index)); +end; + +procedure TIdEntityRanges.SetRange(Index: Integer; AValue: TIdEntityRange); +begin + inherited SetItem(Index, AValue); +end; + +function TIdEntityRanges.GetText: String; +var + I: Integer; + S: String; +begin + Result := ''; + for I := 0 to Count-1 do begin + S := Ranges[I].Text; + if S <> '' then begin + if Result <> '' then begin + Result := Result + ','; {do not localize} + end; + Result := Result + S; + end; + end; + if Result <> '' then begin + Result := FUnits + '=' + Result; {do not localize} + end; +end; + +procedure TIdEntityRanges.SetText(const AValue: String); +var + LUnits, LTmp: String; + LRanges: TStringList; + I: Integer; + LRange: TIdEntityRange; +begin + LTmp := Trim(AValue); + BeginUpdate; + try + Clear; + if Pos('=', LTmp) > 0 then begin {do not localize} + LUnits := Fetch(LTmp, '='); {do not localize} + end; + SetUnits(LUnits); + LRanges := TStringList.Create; + try + SplitDelimitedString(LTmp, LRanges, True, ','); {do not localize} + for I := 0 to LRanges.Count-1 do begin + LTmp := Trim(LRanges[I]); + if LTmp <> '' then begin + LRange := Add; + try + LRange.Text := LTmp; + except + LRange.Free; + raise; + end; + end; + end; + finally + LRanges.Free; + end; + finally + EndUpdate; + end; +end; + +procedure TIdEntityRanges.SetUnits(const AValue: String); +var + LUnits: String; +begin + LUnits := Trim(AValue); + if LUnits <> '' then begin + FUnits := LUnits; + end else begin + FUnits := 'bytes'; {do not localize} + end; +end; + +{ TIdRequestHeaderInfo } + +constructor TIdRequestHeaderInfo.Create(AOwner: TPersistent); +begin + inherited Create(AOwner); + FRanges := TIdEntityRanges.Create(Self); +end; + +destructor TIdRequestHeaderInfo.Destroy; +begin + FreeAndNil(FAuthentication); + FreeAndNil(FRanges); + inherited Destroy; +end; + +procedure TIdRequestHeaderInfo.ProcessHeaders; +begin + inherited ProcessHeaders; + + FAccept := FRawHeaders.Values['Accept']; {do not localize} + FAcceptCharSet := FRawHeaders.Values['Accept-Charset']; {do not localize} + FAcceptEncoding := FRawHeaders.Values['Accept-Encoding']; {do not localize} + FAcceptLanguage := FRawHeaders.Values['Accept-Language']; {do not localize} + FHost := FRawHeaders.Values['Host']; {do not localize} + FFrom := FRawHeaders.Values['From']; {do not localize} + FReferer := FRawHeaders.Values['Referer']; {do not localize} + FUserAgent := FRawHeaders.Values['User-Agent']; {do not localize} + FRanges.Text := FRawHeaders.Values['Range']; {do not localize} + FMethodOverride := FRawHeaders.Values['X-HTTP-Method-Override']; {do not localize} +end; + +procedure TIdRequestHeaderInfo.AssignTo(Destination: TPersistent); +var + LDest: TIdRequestHeaderInfo; +begin + if Destination is TIdRequestHeaderInfo then + begin + LDest := TIdRequestHeaderInfo(Destination); + LDest.FAccept := FAccept; + LDest.FAcceptCharSet := FAcceptCharset; + LDest.FAcceptEncoding := FAcceptEncoding; + LDest.FAcceptLanguage := FAcceptLanguage; + + LDest.FFrom := FFrom; + LDest.FUsername := FUsername; + LDest.FPassword := FPassword; + LDest.FReferer := FReferer; + LDest.FUserAgent := FUserAgent; + LDest.FBasicByDefault := FBasicByDefault; + + LDest.FRanges.Assign(FRanges); + LDest.FMethodOverride := FMethodOverride; + + // TODO: omitted intentionally? + // LDest.FHost := FHost; + // LDest.FProxyConnection := FProxyConnection; + end else begin + inherited AssignTo(Destination); + end; +end; + +procedure TIdRequestHeaderInfo.Clear; +begin + FAccept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; // 'text/html, */*'; {do not localize} + FAcceptCharSet := ''; + FUserAgent := GIdDefaultUserAgent; + FBasicByDefault := false; + FRanges.Text := ''; + FMethodOverride := ''; + + // TODO: omitted intentionally? + // FAcceptEncoding := ''; + // FAcceptLanguage := ''; + // FHost := ''; + // FFrom := ''; + // FPassword := ''; + // FUsername := ''; + // FReferer := ''; + // FProxyConnection := ''; + + inherited Clear; +end; + +function TIdRequestHeaderInfo.GetRange: String; +begin + Result := FRanges.Text; +end; + +procedure TIdRequestHeaderInfo.SetRange(const AValue: String); +begin + FRanges.Text := AValue; +end; + +procedure TIdRequestHeaderInfo.SetRanges(AValue: TIdEntityRanges); +begin + FRanges.Assign(AValue); +end; + +procedure TIdRequestHeaderInfo.SetHeaders; +var + S: String; +begin + inherited SetHeaders; + + if Length(FProxyConnection) > 0 then + begin + FRawHeaders.Values['Proxy-Connection'] := FProxyConnection; {do not localize} + end; + if Length(FHost) > 0 then + begin + FRawHeaders.Values['Host'] := FHost; {do not localize} + end; + if Length(FAccept) > 0 then + begin + FRawHeaders.Values['Accept'] := FAccept; {do not localize} + end; + if Length(FAcceptCharset) > 0 then + begin + FRawHeaders.Values['Accept-Charset'] := FAcceptCharSet; {do not localize} + end; + if Length(FAcceptEncoding) > 0 then + begin + FRawHeaders.Values['Accept-Encoding'] := FAcceptEncoding; {do not localize} + end; + if Length(FAcceptLanguage) > 0 then + begin + FRawHeaders.Values['Accept-Language'] := FAcceptLanguage; {do not localize} + end; + if Length(FFrom) > 0 then + begin + FRawHeaders.Values['From'] := FFrom; {do not localize} + end; + if Length(FReferer) > 0 then + begin + FRawHeaders.Values['Referer'] := FReferer; {do not localize} + end; + if Length(FUserAgent) > 0 then + begin + FRawHeaders.Values['User-Agent'] := FUserAgent; {do not localize} + end; + S := FRanges.Text; + if Length(S) > 0 then + begin + FRawHeaders.Values['Range'] := S; {do not localize} + end; + + // use 'Last-Modified' entity header in the conditional request + if FLastModified > 0 then + begin + FRawHeaders.Values['If-Modified-Since'] := LocalDateTimeToHttpStr(FLastModified); {do not localize} + end; + + if Assigned(Authentication) then + begin + S := Authentication.Authentication; + end + else if FBasicByDefault then begin + FAuthentication := TIdBasicAuthentication.Create; + // TODO: use FAuthentication Username/Password properties instead + FAuthentication.Params.Values['Username'] := FUserName; {do not localize} + FAuthentication.Params.Values['Password'] := FPassword; {do not localize} + S := FAuthentication.Authentication; + end else begin + S := ''; + end; + if Length(S) > 0 then + begin + FRawHeaders.Values['Authorization'] := S; {do not localize} + end; + + if Length(FMethodOverride) > 0 then + begin + FRawHeaders.Values['X-HTTP-Method-Override'] := FMethodOverride; {Do not Localize} + end; +end; + +{ TIdResponseHeaderInfo } + +constructor TIdResponseHeaderInfo.Create(AOwner: TPersistent); +begin + inherited Create(AOwner); + // RLebeau 5/15/2012: don't set any default ContentType, make the user set it... + FContentType := ''; + FCharSet := ''; + FWWWAuthenticate := TIdHeaderList.Create(QuoteHTTP); + FProxyAuthenticate := TIdHeaderList.Create(QuoteHTTP); + FAcceptPatch := ''; + FAcceptRanges := ''; +end; + +destructor TIdResponseHeaderInfo.Destroy; +begin + FreeAndNil(FWWWAuthenticate); + FreeAndNil(FProxyAuthenticate); + inherited Destroy; +end; + +procedure TIdResponseHeaderInfo.SetProxyAuthenticate(const Value: TIdHeaderList); +begin + FProxyAuthenticate.Assign(Value); +end; + +procedure TIdResponseHeaderInfo.SetWWWAuthenticate(const Value: TIdHeaderList); +begin + FWWWAuthenticate.Assign(Value); +end; + +procedure TIdResponseHeaderInfo.ProcessHeaders; +begin + inherited ProcessHeaders; + FLocation := FRawHeaders.Values['Location']; {do not localize} + FServer := FRawHeaders.Values['Server']; {do not localize} + FProxyConnection := FRawHeaders.Values['Proxy-Connection']; {do not localize} + + FWWWAuthenticate.Clear; + FRawHeaders.Extract('WWW-Authenticate', FWWWAuthenticate); {do not localize} + + FProxyAuthenticate.Clear; + FRawHeaders.Extract('Proxy-Authenticate', FProxyAuthenticate);{do not localize} + + FAcceptPatch := FRawHeaders.Values['Accept-Patch']; {do not localize} + FAcceptRanges := FRawHeaders.Values['Accept-Ranges']; {do not localize} +end; + +procedure TIdResponseHeaderInfo.SetHeaders; +var + sUnits: String; + sCR: String; + sCI: String; +begin + inherited SetHeaders; + + { + setting the content-range header is allowed in server responses... + moved here TIdEntityHeaderInfo + } + if HasContentRange or HasContentRangeInstance then + begin + sUnits := iif(FContentRangeUnits <> '', + FContentRangeUnits, 'bytes'); {do not localize} + sCR := iif(HasContentRange, + IndyFormat('%d-%d', [FContentRangeStart, FContentRangeEnd]), '*'); {do not localize} + sCI := iif(HasContentRangeInstance, + IndyFormat('%d', [FContentRangeInstanceLength]), '*'); {do not localize} + + RawHeaders.Values['Content-Range'] := sUnits + ' ' + sCR + '/' + sCI; {do not localize} + end; + if Length(FAcceptPatch) > 0 then + begin + RawHeaders.Values['Accept-Patch'] := FAcceptPatch; {do not localize} + end; + if Length(FAcceptRanges) > 0 then + begin + RawHeaders.Values['Accept-Ranges'] := FAcceptRanges; {do not localize} + end; + if FLastModified > 0 then + begin + RawHeaders.Values['Last-Modified'] := DateTimeGMTToHttpStr(FLastModified); {do not localize} + end; +end; + +procedure TIdResponseHeaderInfo.Clear; +begin + inherited Clear; + + // RLebeau 5/15/2012: don't set any default ContentType, make the user set it... + FContentType := ''; + FCharSet := ''; + + FLocation := ''; + FServer := ''; + FAcceptPatch := ''; + FAcceptRanges := ''; + + if Assigned(FProxyAuthenticate) then + begin + FProxyAuthenticate.Clear; + end; + + if Assigned(FWWWAuthenticate) then + begin + FWWWAuthenticate.Clear; + end; +end; + +procedure TIdResponseHeaderInfo.SetAcceptPatch(const Value: string); +begin + FAcceptPatch := Value; +end; + +procedure TIdResponseHeaderInfo.SetAcceptRanges(const Value: string); +begin + FAcceptRanges := Value; +end; + +{ TIdMetaHTTPEquiv } + +procedure TIdMetaHTTPEquiv.ProcessMetaHTTPEquiv(AStream: TStream); +var + LCharSet: string; +begin + ParseMetaHTTPEquiv(AStream, RawHeaders, LCharSet); + if FRawHeaders.Count > 0 then begin + ProcessHeaders; + end; + if LCharSet <> '' then begin + FCharSet := LCharset; + end; +end; + +end. diff --git a/indy/Protocols/IdHTTPProxyServer.pas b/indy/Protocols/IdHTTPProxyServer.pas new file mode 100644 index 0000000..d2e3ac0 --- /dev/null +++ b/indy/Protocols/IdHTTPProxyServer.pas @@ -0,0 +1,499 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.24 10/14/2004 1:45:32 PM BGooijen + Beauty fixes ;) + + Rev 1.23 10/14/2004 1:05:48 PM BGooijen + set PerformReply to false, else "200 OK" was added behind the document body + + Rev 1.22 09.08.2004 09:30:00 OMonien + changed disconnect handling. Previous implementation failed when exceptions + ocured in command handler. + + Rev 1.21 08.08.2004 10:35:56 OMonien + Greeting removed + + Rev 1.20 6/11/2004 9:36:28 AM DSiders + Added "Do not Localize" comments. + + Rev 1.19 2004.05.20 1:39:24 PM czhower + Last of the IdStream updates + + Rev 1.18 2004.05.20 11:37:20 AM czhower + IdStreamVCL + + Rev 1.17 4/19/2004 7:07:38 PM BGooijen + the remote headers are now passed to the OnHTTPDocument event + + Rev 1.16 4/18/2004 11:31:26 PM BGooijen + Fixed POST + Build CONNECT + fixed some bugs where chars were replaced when that was not needed ( thus + causing corrupt data ) + + Rev 1.15 2004.04.13 10:24:24 PM czhower + Bug fix for when user changes stream. + + Rev 1.14 2004.02.03 5:45:12 PM czhower + Name changes + + Rev 1.13 1/21/2004 2:42:52 PM JPMugaas + InitComponent + + Rev 1.12 10/25/2003 06:52:12 AM JPMugaas + Updated for new API changes and tried to restore some functionality. + + Rev 1.11 2003.10.24 10:43:10 AM czhower + TIdSTream to dos + + Rev 1.10 10/17/2003 12:10:08 AM DSiders + Added localization comments. + + Rev 1.9 2003.10.12 3:50:44 PM czhower + Compile todos + + Rev 1.8 7/13/2003 7:57:38 PM SPerry + fixed problem with commandhandlers + + Rev 1.6 5/25/2003 03:54:42 AM JPMugaas + + Rev 1.5 2/24/2003 08:56:50 PM JPMugaas + + Rev 1.4 1/20/2003 1:15:44 PM BGooijen + Changed to TIdTCPServer / TIdCmdTCPServer classes + + Rev 1.3 1-14-2003 19:19:22 BGooijen + The first line of the header was sent to the server twice, fixed that. + + Rev 1.2 1-1-2003 21:52:06 BGooijen + Changed for TIdContext + + Rev 1.1 12-29-2002 13:00:02 BGooijen + - Works on Indy 10 now + - Cleaned up some code + + Rev 1.0 2002.11.22 8:37:50 PM czhower + + Rev 1.0 2002.11.22 8:37:16 PM czhower + + 10-May-2002: Created Unit. +} + +unit IdHTTPProxyServer; + +interface + +{$i IdCompilerDefines.inc} + +{ + Indy HTTP proxy Server + + Original Programmer: Bas Gooijen (bas_gooijen@yahoo.com) + Current Maintainer: Bas Gooijen + Code is given to the Indy Pit Crew. + + Modifications by Chad Z. Hower (Kudzu) +} + +uses + Classes, + IdAssignedNumbers, + IdGlobal, + IdHeaderList, + IdTCPConnection, + IdCustomTCPServer, //for TIdServerContext + IdCmdTCPServer, + IdCommandHandlers, + IdContext, + IdYarn; + +const + IdPORT_HTTPProxy = 8080; + +type + TIdHTTPProxyTransferMode = ( tmFullDocument, tmStreaming ); + TIdHTTPProxyTransferSource = ( tsClient, tsServer ); + + TIdHTTPProxyServerContext = class(TIdServerContext) + protected + FHeaders: TIdHeaderList; + FCommand: String; + FDocument: String; + FOutboundClient: TIdTCPConnection; + FTarget: String; + FTransferMode: TIdHTTPProxyTransferMode; + FTransferSource: TIdHTTPProxyTransferSource; + public + constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override; + destructor Destroy; override; + property Headers: TIdHeaderList read FHeaders; + property Command: String read FCommand; + property Document: String read FDocument; + property OutboundClient: TIdTCPConnection read FOutboundClient; + property Target: String read FTarget; + property TransferMode: TIdHTTPProxyTransferMode read FTransferMode write FTransferMode; + property TransferSource: TIdHTTPProxyTransferSource read FTransferSource; + end; + + TIdHTTPProxyServer = class; + + TOnHTTPContextEvent = procedure(AContext: TIdHTTPProxyServerContext) of object; + TOnHTTPDocument = procedure(AContext: TIdHTTPProxyServerContext; var VStream: TStream) of object; + + TIdHTTPProxyServer = class(TIdCmdTCPServer) + protected + FDefTransferMode: TIdHTTPProxyTransferMode; + FOnHTTPBeforeCommand: TOnHTTPContextEvent; + FOnHTTPResponse: TOnHTTPContextEvent; + FOnHTTPDocument: TOnHTTPDocument; + // CommandHandlers + procedure CommandPassThrough(ASender: TIdCommand); + procedure CommandCONNECT(ASender: TIdCommand); // for ssl + procedure DoHTTPBeforeCommand(AContext: TIdHTTPProxyServerContext); + procedure DoHTTPDocument(AContext: TIdHTTPProxyServerContext; var VStream: TStream); + procedure DoHTTPResponse(AContext: TIdHTTPProxyServerContext); + procedure InitializeCommandHandlers; override; + procedure TransferData(AContext: TIdHTTPProxyServerContext; ASrc, ADest: TIdTCPConnection); + procedure InitComponent; override; + published + property DefaultPort default IdPORT_HTTPProxy; + property DefaultTransferMode: TIdHTTPProxyTransferMode read FDefTransferMode write FDefTransferMode default tmFullDocument; + property OnHTTPBeforeCommand: TOnHTTPContextEvent read FOnHTTPBeforeCommand write FOnHTTPBeforeCommand; + property OnHTTPResponse: TOnHTTPContextEvent read FOnHTTPResponse write FOnHTTPResponse; + property OnHTTPDocument: TOnHTTPDocument read FOnHTTPDocument write FOnHTTPDocument; + end; + +implementation + +uses + IdResourceStrings, IdResourceStringsProtocols, IdReplyRFC, IdTCPClient, IdURI, + IdGlobalProtocols, IdStack, IdTCPStream, IdException, SysUtils; + +constructor TIdHTTPProxyServerContext.Create(AConnection: TIdTCPConnection; + AYarn: TIdYarn; AList: TIdContextThreadList = nil); +begin + inherited Create(AConnection, AYarn, AList); + FHeaders := TIdHeaderList.Create(QuoteHTTP); +end; + +destructor TIdHTTPProxyServerContext.Destroy; +begin + FreeAndNil(FHeaders); + inherited Destroy; +end; + +{ TIdHTTPProxyServer } + +procedure TIdHTTPProxyServer.InitializeCommandHandlers; +var + LCommandHandler: TIdCommandHandler; +begin + inherited; + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'GET'; {do not localize} + LCommandHandler.OnCommand := CommandPassThrough; + LCommandHandler.ParseParams := True; + LCommandHandler.Disconnect := True; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'POST'; {do not localize} + LCommandHandler.OnCommand := CommandPassThrough; + LCommandHandler.ParseParams := True; + LCommandHandler.Disconnect := True; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'HEAD'; {do not localize} + LCommandHandler.OnCommand := CommandPassThrough; + LCommandHandler.ParseParams := True; + LCommandHandler.Disconnect := True; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CONNECT'; {do not localize} + LCommandHandler.OnCommand := CommandCONNECT; + LCommandHandler.ParseParams := True; + LCommandHandler.Disconnect := True; + + //HTTP Servers/Proxies do not send a greeting + Greeting.Clear; +end; + +procedure TIdHTTPProxyServer.TransferData(AContext: TIdHTTPProxyServerContext; + ASrc, ADest: TIdTCPConnection); +var + LStream: TStream; + LSize: TIdStreamSize; + S: String; +begin + // RLebeau: TODO - support chunked, gzip, and deflate transfers. + + // RLebeau: determine how many bytes to read + S := AContext.Headers.Values['Content-Length']; {Do not Localize} + if S <> '' then + begin + LSize := IndyStrToStreamSize(S, -1) ; {Do not Localize} + if LSize < 0 then begin + // Write HTTP error status response + if AContext.TransferSource = tsClient then begin + ASrc.IOHandler.WriteLn('HTTP/1.0 400 Bad Request'); {Do not Localize} + end else begin + ASrc.IOHandler.WriteLn('HTTP/1.0 502 Bad Gateway'); {Do not Localize} + end; + ASrc.IOHandler.WriteLn; + Exit; + end; + end else begin + LSize := -1; + end; + + if AContext.TransferSource = tsClient then begin + ADest.IOHandler.WriteLn(AContext.Command + ' ' + AContext.Document + ' HTTP/1.0'); {Do not Localize} + end; + + if (AContext.TransferSource = tsServer) or (LSize > 0) then + begin + LStream := nil; + try + if AContext.TransferMode = tmFullDocument then + begin + //TODO: Have an event to let the user perform stream creation + LStream := TMemoryStream.Create; + // RLebeau: do not write the source headers until the OnHTTPDocument + // event has had a chance to update them if it alters the document data... + ASrc.IOHandler.ReadStream(LStream, LSize, LSize < 0); + LStream.Position := 0; + DoHTTPDocument(AContext, LStream); + ADest.IOHandler.Write(AContext.Headers); + ADest.IOHandler.WriteLn; + ADest.IOHandler.Write(LStream); + end else + begin + // RLebeau: direct pass-through, send everything as-is... + LStream := TIdTCPStream.Create(ADest); + ADest.IOHandler.Write(AContext.Headers); + ADest.IOHandler.WriteLn; + ASrc.IOHandler.ReadStream(LStream, LSize, LSize < 0); + end; + finally + FreeAndNil(LStream); + end; + end else + begin + // RLebeau: the client sent a document with no data in it, so just pass + // along the headers by themselves ... + ADest.IOHandler.Write(AContext.Headers); + ADest.IOHandler.WriteLn; + end; +end; + +procedure TIdHTTPProxyServer.CommandPassThrough(ASender: TIdCommand); +var + LURI: TIdURI; + LContext: TIdHTTPProxyServerContext; +begin + ASender.PerformReply := False; + + LContext := TIdHTTPProxyServerContext(ASender.Context); + LContext.FCommand := ASender.CommandHandler.Command; + LContext.FTarget := ASender.Params.Strings[0]; + + LContext.FOutboundClient := TIdTCPClient.Create(nil); + try + LURI := TIdURI.Create(LContext.Target); + try + TIdTCPClient(LContext.FOutboundClient).Host := LURI.Host; + + if LURI.Port <> '' then begin + TIdTCPClient(LContext.FOutboundClient).Port := IndyStrToInt(LURI.Port, 80); + end + else if TextIsSame(LURI.Protocol, 'http') then begin {do not localize} + TIdTCPClient(LContext.FOutboundClient).Port := IdPORT_HTTP; + end + else if TextIsSame(LURI.Protocol, 'https') then begin {do not localize} + TIdTCPClient(LContext.FOutboundClient).Port := IdPORT_https; + end else begin + raise EIdException.Create(RSHTTPUnknownProtocol); + end; + + //We have to remove the host and port from the request + LContext.FDocument := LURI.GetPathAndParams; + finally + FreeAndNil(LURI); + end; + + LContext.Headers.Clear; + LContext.Connection.IOHandler.Capture(LContext.Headers, '', False); + LContext.FTransferMode := FDefTransferMode; + LContext.FTransferSource := tsClient; + DoHTTPBeforeCommand(LContext); + + TIdTCPClient(LContext.FOutboundClient).Connect; + try + TransferData(LContext, LContext.Connection, LContext.FOutboundClient); + + LContext.Headers.Clear; + LContext.FOutboundClient.IOHandler.Capture(LContext.Headers, '', False); + LContext.FTransferMode := FDefTransferMode; + LContext.FTransferSource := tsServer; + DoHTTPResponse(LContext); + TransferData(LContext, LContext.FOutboundClient, LContext.Connection); + finally + LContext.FOutboundClient.Disconnect; + end; + finally + FreeAndNil(LContext.FOutboundClient); + end; +end; + +procedure TIdHTTPProxyServer.CommandCONNECT(ASender: TIdCommand); +var + LRemoteHost: string; + LContext: TIdHTTPProxyServerContext; + LReadList, LDataAvailList: TIdSocketList; + LClientToServerStream, LServerToClientStream: TStream; +begin + // RLebeau 7/31/09: we can't make any assumptions about the contents of + // the data being exchanged after the connection has been established. + // It may not (and likely will not) be HTTP data at all. We must pass + // it along as-is in both directions, in as near-realtime as we can... + + ASender.PerformReply := False; + + LContext := TIdHTTPProxyServerContext(ASender.Context); + LContext.FCommand := ASender.CommandHandler.Command; + LContext.FTarget := ASender.Params.Strings[0]; + + LContext.FOutboundClient := TIdTCPClient.Create(nil); + try + LClientToServerStream := nil; + LServerToClientStream := nil; + try + LClientToServerStream := TIdTCPStream.Create(LContext.FOutboundClient); + LServerToClientStream := TIdTCPStream.Create(LContext.Connection); + + LRemoteHost := LContext.Target; + TIdTCPClient(LContext.FOutboundClient).Host := Fetch(LRemoteHost, ':', True); + TIdTCPClient(LContext.FOutboundClient).Port := IndyStrToInt(LRemoteHost, 443); + + LContext.Headers.Clear; + LContext.Connection.IOHandler.Capture(LContext.Headers, '', False); + LContext.FTransferMode := FDefTransferMode; + LContext.FTransferSource := tsClient; + DoHTTPBeforeCommand(LContext); + + LReadList := nil; + LDataAvailList := nil; + try + LReadList := TIdSocketList.CreateSocketList; + LDataAvailList := TIdSocketList.CreateSocketList; + + TIdTCPClient(LContext.FOutboundClient).Connect; + try + LReadList.Add(LContext.Connection.Socket.Binding.Handle); + LReadList.Add(LContext.FOutboundClient.Socket.Binding.Handle); + + LContext.Connection.IOHandler.WriteLn('HTTP/1.0 200 Connection established'); {do not localize} + LContext.Connection.IOHandler.WriteLn('Proxy-agent: Indy-Proxy/1.1'); {do not localize} + LContext.Connection.IOHandler.WriteLn; + + LContext.Connection.IOHandler.ReadTimeout := 100; + LContext.FOutboundClient.IOHandler.ReadTimeout := 100; + + while LContext.Connection.Connected and LContext.FOutboundClient.Connected do + begin + if LReadList.SelectReadList(LDataAvailList, IdTimeoutInfinite) then + begin + if LDataAvailList.ContainsSocket(LContext.Connection.Socket.Binding.Handle) then + begin + LContext.Connection.IOHandler.CheckForDataOnSource(0); + end; + if LDataAvailList.ContainsSocket(LContext.FOutboundClient.Socket.Binding.Handle) then + begin + LContext.FOutboundClient.IOHandler.CheckForDataOnSource(0); + end; + + if not LContext.Connection.IOHandler.InputBufferIsEmpty then + begin + LContext.Connection.IOHandler.InputBuffer.ExtractToStream(LClientToServerStream); + end; + if not LContext.FOutboundClient.IOHandler.InputBufferIsEmpty then + begin + LContext.FOutboundClient.IOHandler.InputBuffer.ExtractToStream(LServerToClientStream); + end; + end; + end; + + if LContext.FOutboundClient.Connected and (not LContext.Connection.IOHandler.InputBufferIsEmpty) then + begin + LContext.Connection.IOHandler.InputBuffer.ExtractToStream(LClientToServerStream); + end; + if LContext.Connection.Connected and (not LContext.FOutboundClient.IOHandler.InputBufferIsEmpty) then + begin + LContext.FOutboundClient.IOHandler.InputBuffer.ExtractToStream(LServerToClientStream); + end; + finally + LContext.FOutboundClient.Disconnect; + end; + finally + FreeAndNil(LDataAvailList); + FreeAndNil(LReadList); + end; + finally + FreeAndNil(LClientToServerStream); + FreeAndNil(LServerToClientStream); + end; + finally + FreeAndNil(LContext.FOutboundClient); + end; +end; + +procedure TIdHTTPProxyServer.InitComponent; +begin + inherited InitComponent; + ContextClass := TIdHTTPProxyServerContext; + DefaultPort := IdPORT_HTTPProxy; + FDefTransferMode := tmFullDocument; + Greeting.Text.Text := ''; // RS + ReplyUnknownCommand.Text.Text := ''; // RS +end; + +procedure TIdHTTPProxyServer.DoHTTPBeforeCommand(AContext: TIdHTTPProxyServerContext); +begin + if Assigned(OnHTTPBeforeCommand) then begin + OnHTTPBeforeCommand(AContext); + end; +end; + +procedure TIdHTTPProxyServer.DoHTTPDocument(AContext: TIdHTTPProxyServerContext; + var VStream: TStream); +begin + if Assigned(OnHTTPDocument) then begin + OnHTTPDocument(AContext, VStream); + end; +end; + +procedure TIdHTTPProxyServer.DoHTTPResponse(AContext: TIdHTTPProxyServerContext); +begin + if Assigned(OnHTTPResponse) then begin + OnHTTPResponse(AContext); + end; +end; + +end. + + diff --git a/indy/Protocols/IdHTTPServer.pas b/indy/Protocols/IdHTTPServer.pas new file mode 100644 index 0000000..6d84cae --- /dev/null +++ b/indy/Protocols/IdHTTPServer.pas @@ -0,0 +1,53 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/13/2002 07:54:30 AM JPMugaas + + Aug-26-2001: + - New event (TOnCreateSession) - The user program can use it create objects from its own + descendant class of TIdHTTPSession. Thi s descendant class can be used to hold additional, + spcific to the user program data. +} + +unit IdHTTPServer; + +{ + Implementation of the HTTP server based on RFC 2616 + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. + + Author: Stephane Grobety (grobety@fulgan.com) + Additional chages and bug fixes - Doychin Bondzhev (doychin@dsoft-bg.com) +} + +interface +{$i IdCompilerDefines.inc} + +uses + IdCustomHTTPServer; + +type + TIdHTTPServer = class(TIdCustomHTTPServer) + published + property OnCreatePostStream; + property OnDoneWithPostStream; + property OnCommandGet; + end; + +implementation + +end. diff --git a/indy/Protocols/IdHTTPWebBrokerBridge.pas b/indy/Protocols/IdHTTPWebBrokerBridge.pas new file mode 100644 index 0000000..2aee4d6 --- /dev/null +++ b/indy/Protocols/IdHTTPWebBrokerBridge.pas @@ -0,0 +1,915 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 6/26/2004 12:11:16 AM BGooijen + updates for D8 + + Rev 1.6 4/8/2004 4:00:40 PM BGooijen + Fix for D8 + + Rev 1.5 07/04/2004 20:44:06 HHariri + Updates + + Rev 1.4 07/04/2004 20:07:50 HHariri + Updates for .NET + + Rev 1.3 10/19/2003 4:50:10 PM DSiders + Added localization comments. + + Rev 1.2 10/12/2003 1:49:48 PM BGooijen + Changed comment of last checkin + + Rev 1.1 10/12/2003 1:43:32 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.0 11/13/2002 07:54:34 AM JPMugaas +} + +unit IdHTTPWebBrokerBridge; + +{ +Original Author: Dave Nottage. +Modified by: Grahame Grieve +Modified by: Chad Z. Hower (Kudzu) +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + HTTPApp, + IdContext, IdCustomHTTPServer, IdException, IdTCPServer, IdIOHandlerSocket, + {$IFDEF CLR}System.Text,{$ENDIF} + WebBroker, WebReq; + +type + EWBBException = class(EIdException); + EWBBInvalidIdxGetDateVariable = class(EWBBException); + EWBBInvalidIdxSetDateVariable = class(EWBBException ); + EWBBInvalidIdxGetIntVariable = class(EWBBException ); + EWBBInvalidIdxSetIntVariable = class(EWBBException ); + EWBBInvalidIdxGetStrVariable = class(EWBBException); + EWBBInvalidIdxSetStringVar = class(EWBBException); + EWBBInvalidStringVar = class(EWBBException); + + TIdHTTPAppRequest = class(TWebRequest) + protected + FRequestInfo : TIdHTTPRequestInfo; + FResponseInfo : TIdHTTPResponseInfo; + FThread : TIdContext; + FContentStream : TStream; + FFreeContentStream : Boolean; + // + function GetDateVariable(Index: Integer): TDateTime; override; + function GetIntegerVariable(Index: Integer): Integer; override; + function GetStringVariable(Index: Integer): AnsiString; override; + {$IFDEF VCL_XE_OR_ABOVE} + function GetRemoteIP: string; override; + function GetRawPathInfo: AnsiString; override; + {$ENDIF} + public + constructor Create(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); + destructor Destroy; override; + function GetFieldByName(const Name: AnsiString): AnsiString; override; + function ReadClient(var Buffer{$IFDEF CLR}: TBytes{$ENDIF}; Count: Integer): Integer; override; + function ReadString(Count: Integer): AnsiString; override; + {function ReadUnicodeString(Count: Integer): string;} + function TranslateURI(const URI: string): string; override; + function WriteClient(var ABuffer; ACount: Integer): Integer; override; + + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE VCL_6_OR_ABOVE_OR_CLR} + {$ENDIF} + {$IFDEF CLR} + {$DEFINE VCL_6_OR_ABOVE_OR_CLR} + {$ENDIF} + {$IFDEF VCL_6_OR_ABOVE_OR_CLR} + function WriteHeaders(StatusCode: Integer; const ReasonString, Headers: AnsiString): Boolean; override; + {$ENDIF} + function WriteString(const AString: AnsiString): Boolean; override; + end; + + TIdHTTPAppResponse = class(TWebResponse) + protected + FContent: string; + FRequestInfo: TIdHTTPRequestInfo; + FResponseInfo: TIdHTTPResponseInfo; + FSent: Boolean; + FThread: TIdContext; + FContentType: AnsiString; // Workaround to preserve value of ContentType property + // + function GetContent: AnsiString; override; + function GetDateVariable(Index: Integer): TDateTime; override; + function GetStatusCode: Integer; override; + function GetIntegerVariable(Index: Integer): Integer; override; + function GetLogMessage: string; override; + function GetStringVariable(Index: Integer): AnsiString; override; + procedure SetContent(const AValue: AnsiString); override; + procedure SetContentStream(AValue: TStream); override; + procedure SetStatusCode(AValue: Integer); override; + procedure SetStringVariable(Index: Integer; const Value: AnsiString); override; + procedure SetDateVariable(Index: Integer; const Value: TDateTime); override; + procedure SetIntegerVariable(Index: Integer; Value: Integer); override; + procedure SetLogMessage(const Value: string); override; + procedure MoveCookiesAndCustomHeaders; + public + constructor Create(AHTTPRequest: TWebRequest; AThread: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); + procedure SendRedirect(const URI: AnsiString); override; + procedure SendResponse; override; + procedure SendStream(AStream: TStream); override; + function Sent: Boolean; override; + end; + + TIdHTTPWebBrokerBridge = class(TIdCustomHTTPServer) + private + procedure RunWebModuleClass(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); + protected + FWebModuleClass: TComponentClass; + // + procedure DoCommandGet(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); override; + procedure DoCommandOther(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; + AResponseInfo: TIdHTTPResponseInfo); override; + procedure InitComponent; override; + public + procedure RegisterWebModuleClass(AClass: TComponentClass); + end; + +implementation + +uses + IdResourceStringsProtocols, + IdBuffer, IdHTTPHeaderInfo, IdGlobal, IdGlobalProtocols, IdCookie, IdStream, + {$IFDEF STRING_IS_UNICODE}IdCharsets,{$ENDIF} + SysUtils, Math + {$IFDEF HAS_TNetEncoding} + , System.NetEncoding + {$ENDIF} + ; + +type + // Make HandleRequest accessible + TWebDispatcherAccess = class(TCustomWebDispatcher); + +const + INDEX_RESP_Version = 0; + INDEX_RESP_ReasonString = 1; + INDEX_RESP_Server = 2; + INDEX_RESP_WWWAuthenticate = 3; + INDEX_RESP_Realm = 4; + INDEX_RESP_Allow = 5; + INDEX_RESP_Location = 6; + INDEX_RESP_ContentEncoding = 7; + INDEX_RESP_ContentType = 8; + INDEX_RESP_ContentVersion = 9; + INDEX_RESP_DerivedFrom = 10; + INDEX_RESP_Title = 11; + // + INDEX_RESP_ContentLength = 0; + // + INDEX_RESP_Date = 0; + INDEX_RESP_Expires = 1; + INDEX_RESP_LastModified = 2; + // + //Borland coder didn't define constants in HTTPApp + INDEX_Method = 0; + INDEX_ProtocolVersion = 1; + INDEX_URL = 2; + INDEX_Query = 3; + INDEX_PathInfo = 4; + INDEX_PathTranslated = 5; + INDEX_CacheControl = 6; + INDEX_Date = 7; + INDEX_Accept = 8; + INDEX_From = 9; + INDEX_Host = 10; + INDEX_IfModifiedSince = 11; + INDEX_Referer = 12; + INDEX_UserAgent = 13; + INDEX_ContentEncoding = 14; + INDEX_ContentType = 15; + INDEX_ContentLength = 16; + INDEX_ContentVersion = 17; + INDEX_DerivedFrom = 18; + INDEX_Expires = 19; + INDEX_Title = 20; + INDEX_RemoteAddr = 21; + INDEX_RemoteHost = 22; + INDEX_ScriptName = 23; + INDEX_ServerPort = 24; + INDEX_Content = 25; + INDEX_Connection = 26; + INDEX_Cookie = 27; + INDEX_Authorization = 28; + +{ TIdHTTPAppRequest } + +constructor TIdHTTPAppRequest.Create(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +var + i: Integer; +begin + FThread := AThread; + FRequestInfo := ARequestInfo; + FResponseInfo := AResponseInfo; + inherited Create; + for i := 0 to ARequestInfo.Cookies.Count - 1 do begin + CookieFields.Add(ARequestInfo.Cookies[i].ClientCookie); + end; + if Assigned(FRequestInfo.PostStream) then + begin + FContentStream := FRequestInfo.PostStream; + FFreeContentStream := False; + end else + begin + if FRequestInfo.FormParams <> '' then begin {do not localize} + // an input form that was submitted as "application/www-url-encoded"... + FContentStream := TStringStream.Create(FRequestInfo.FormParams); + end else + begin + // anything else for now... + FContentStream := TStringStream.Create(FRequestInfo.UnparsedParams); + end; + FFreeContentStream := True; + end; +end; + +destructor TIdHTTPAppRequest.Destroy; +begin + if FFreeContentStream then begin + FreeAndNil(FContentStream); + end; + inherited; +end; + +function TIdHTTPAppRequest.GetDateVariable(Index: Integer): TDateTime; +var + LValue: string; +begin + LValue := string(GetStringVariable(Index)); + if Length(LValue) > 0 then begin + Result := ParseDate(LValue); + end else begin + Result := -1; + end; +end; + +function TIdHTTPAppRequest.GetIntegerVariable(Index: Integer): Integer; +begin + Result := StrToIntDef(string(GetStringVariable(Index)), -1) +end; + +{$IFDEF VCL_XE_OR_ABOVE} +function TIdHTTPAppRequest.GetRawPathInfo: AnsiString; +begin + Result := AnsiString(FRequestInfo.URI); +end; + +function TIdHTTPAppRequest.GetRemoteIP: string; +begin + Result := String(FRequestInfo.RemoteIP); +end; +{$ENDIF} + +function TIdHTTPAppRequest.GetStringVariable(Index: Integer): AnsiString; +var + s: string; + LPos: TIdStreamSize; + LBytes: TIdBytes; +begin + LBytes := nil; + case Index of + INDEX_Method : Result := AnsiString(FRequestInfo.Command); + INDEX_ProtocolVersion : Result := AnsiString(FRequestInfo.Version); + //INDEX_URL : Result := AnsiString(FRequestInfo.Document); + INDEX_URL : Result := AnsiString(''); // Root - consistent with ISAPI which return path to root + INDEX_Query : Result := AnsiString(FRequestInfo.QueryParams); + INDEX_PathInfo : Result := AnsiString(FRequestInfo.Document); + INDEX_PathTranslated : Result := AnsiString(FRequestInfo.Document); // it's not clear quite what should be done here - we can't translate to a path + INDEX_CacheControl : Result := GetFieldByName('Cache-Control'); {do not localize} + INDEX_Date : Result := GetFieldByName('Date'); {do not localize} + INDEX_Accept : Result := AnsiString(FRequestInfo.Accept); + INDEX_From : Result := AnsiString(FRequestInfo.From); + INDEX_Host: begin + s := FRequestInfo.Host; + Result := AnsiString(Fetch(s, ':')); + end; + INDEX_IfModifiedSince : Result := GetFieldByName('If-Modified-Since'); {do not localize} + INDEX_Referer : Result := AnsiString(FRequestInfo.Referer); + INDEX_UserAgent : Result := AnsiString(FRequestInfo.UserAgent); + INDEX_ContentEncoding : Result := AnsiString(FRequestInfo.ContentEncoding); + INDEX_ContentType : Result := AnsiString(FRequestInfo.ContentType); + INDEX_ContentLength : Result := AnsiString(IntToStr(FContentStream.Size)); + INDEX_ContentVersion : Result := GetFieldByName('CONTENT_VERSION'); {do not localize} + INDEX_DerivedFrom : Result := GetFieldByName('Derived-From'); {do not localize} + INDEX_Expires : Result := GetFieldByName('Expires'); {do not localize} + INDEX_Title : Result := GetFieldByName('Title'); {do not localize} + INDEX_RemoteAddr : Result := AnsiString(FRequestInfo.RemoteIP); + INDEX_RemoteHost : Result := GetFieldByName('REMOTE_HOST'); {do not localize} + INDEX_ScriptName : Result := ''; + INDEX_ServerPort: begin + s := FRequestInfo.Host; + Fetch(s, ':'); + if Length(s) = 0 then begin + s := IntToStr(FThread.Connection.Socket.Binding.Port); + // Result := '80'; + end; + Result := AnsiString(s); + end; + INDEX_Content: begin + if FFreeContentStream then + begin + Result := AnsiString(TStringStream(FContentStream).DataString); + end else + begin + LPos := FContentStream.Position; + FContentStream.Position := 0; + try + // RLebeau 2/21/2009: not using ReadStringAsCharSet() anymore. Since + // this method returns an AnsiString, the stream data should not be + // decoded to Unicode and then converted to Ansi. That can lose + // characters. Also, for D2009+, the AnsiString payload should have + // the proper codepage assigned to it as well so it can be converted + // correctly if assigned to other string variables later on... + + // Result := ReadStringAsCharSet(FContentStream, FRequestInfo.CharSet); + TIdStreamHelper.ReadBytes(FContentStream, LBytes); + {$IFDEF DOTNET} + // RLebeau: how to handle this correctly in .NET? + Result := AnsiString(BytesToStringRaw(LBytes)); + {$ELSE} + SetString(Result, PAnsiChar(LBytes), Length(LBytes)); + {$IFDEF VCL_2009_OR_ABOVE} + SetCodePage(PRawByteString(@Result)^, CharsetToCodePage(FRequestInfo.CharSet), False); + {$ENDIF} + {$ENDIF} + finally + FContentStream.Position := LPos; + end; + end; + end; + INDEX_Connection : Result := GetFieldByName('Connection'); {do not localize} + INDEX_Cookie : Result := ''; // not available at present. FRequestInfo.Cookies....; + INDEX_Authorization : Result := GetFieldByName('Authorization'); {do not localize} + else + Result := ''; + end; +end; + +function TIdHTTPAppRequest.GetFieldByName(const Name: AnsiString): AnsiString; +begin + Result := AnsiString(FRequestInfo.RawHeaders.Values[string(Name)]); +end; + +function TIdHTTPAppRequest.ReadClient(var Buffer{$IFDEF CLR}: TBytes{$ENDIF}; + Count: Integer): Integer; +begin + {$IFDEF CLR} + Result := TIdStreamHelper.ReadBytes(FContentStream, Buffer, Count); + {$ELSE} + Result := FContentStream.Read(Buffer, Count); + {$ENDIF} + // well, it shouldn't be less than 0. but let's not take chances + if Result < 0 then begin + Result := 0; + end; +end; + +function TIdHTTPAppRequest.ReadString(Count: Integer): AnsiString; +var + LBytes: TIdBytes; +begin + // RLebeau 2/21/2009: not using ReadStringAsCharSet() anymore. Since + // this method returns an AnsiString, the stream data should not be + // decoded to Unicode and then converted to Ansi. That can lose + // characters. + + // Result := AnsiString(ReadStringFromStream(FContentStream, Count)); + LBytes := nil; + TIdStreamHelper.ReadBytes(FContentStream, LBytes, Count); + {$IFDEF DOTNET} + // RLebeau: how to handle this correctly in .NET? + Result := AnsiString(BytesToStringRaw(LBytes)); + {$ELSE} + SetString(Result, PAnsiChar(LBytes), Length(LBytes)); + {$IFDEF VCL_2009_OR_ABOVE} + SetCodePage(PRawByteString(@Result)^, CharsetToCodePage(FRequestInfo.CharSet), False); + {$ENDIF} + {$ENDIF} +end; + +function TIdHTTPAppRequest.TranslateURI(const URI: string): string; +begin + // we don't have the concept of a path translation. It's not quite clear + // what to do about this. Comments welcome (grahame@kestral.com.au) + Result := URI; +end; + +{$IFDEF VCL_6_OR_ABOVE_OR_CLR} +function TIdHTTPAppRequest.WriteHeaders(StatusCode: Integer; const ReasonString, Headers: AnsiString): Boolean; +begin + FResponseInfo.ResponseNo := StatusCode; + FResponseInfo.ResponseText := string(ReasonString); + FResponseInfo.CustomHeaders.Add(string(Headers)); + FResponseInfo.WriteHeader; + Result := True; +end; +{$ENDIF} + +function TIdHTTPAppRequest.WriteString(const AString: AnsiString): Boolean; +begin + FThread.Connection.IOHandler.Write(string(AString)); + Result := True; +end; + +function TIdHTTPAppRequest.WriteClient(var ABuffer; ACount: Integer): Integer; +var + LBuffer: TIdBytes; +begin + SetLength(LBuffer, ACount); +{$IFNDEF CLR} + Move(ABuffer, LBuffer[0], ACount); +{$ELSE} + // RLebeau: this can't be right? It is interpretting the source as a + // null-terminated character string, which is likely not the case... + CopyTIdBytes(ToBytes(string(ABuffer)), 0, LBuffer, 0, ACount); +{$ENDIF} + FThread.Connection.IOHandler.Write(LBuffer); + Result := ACount; +end; + +{ TIdHTTPAppResponse } + +constructor TIdHTTPAppResponse.Create(AHTTPRequest: TWebRequest; AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +begin + FThread := AThread; + FRequestInfo := ARequestInfo; + FResponseInfo := AResponseInfo; + inherited Create(AHTTPRequest); + if Length(FHTTPRequest.ProtocolVersion) = 0 then begin + Version := '1.0'; {do not localize} + end; + StatusCode := 200; + LastModified := -1; + Expires := -1; + Date := -1; + + // RLebeau 8/13/2015: no longer setting a default ContentType here. Doing so + // sets a default CharSet, which would get carried over if the user assigns a + // new *non-text* ContentType without an explicit charset. TAppResponse does + // not expose access to the FResponseInfo.CharSet property. For example, if + // the user sets TAppResponse.ContentType to 'image/jpeg', the resulting + // Content-Type header woud be 'image/jpeg; charset=ISO-8859-1', which can + // cause problems for some clients. Besides, TIdHTTPResponseInfo.WriteHeader() + // sets the ContentType to 'text/html; charset=ISO-8859-1' if no ContentType + // has been provided but there is ContentText/ContentStream data, so this is + // redundant here anyway... + // + // ContentType := 'text/html'; {do not localize} +end; + +function TIdHTTPAppResponse.GetContent: AnsiString; +{$IFDEF STRING_IS_UNICODE} +var + LEncoding: IIdTextEncoding; + LBytes: TIdBytes; +{$ENDIF} +begin + {$IFDEF STRING_IS_UNICODE} + // RLebeau 2/21/2009: encode the content using the specified charset. + // Also, the AnsiString payload should have the proper codepage assigned + // to it as well so it can be converted correctly if assigned to other + // string variables later on... + Result := ''; + LEncoding := CharsetToEncoding(FResponseInfo.CharSet); + LBytes := LEncoding.GetBytes(FResponseInfo.ContentText); + {$IFDEF DOTNET} + // RLebeau: how to handle this correctly in .NET? + Result := AnsiString(BytesToStringRaw(LBytes)); + {$ELSE} + SetString(Result, PAnsiChar(LBytes), Length(LBytes)); + {$IFDEF VCL_2009_OR_ABOVE} + SetCodePage(PRawByteString(@Result)^, CharsetToCodePage(FResponseInfo.CharSet), False); + {$ENDIF} + {$ENDIF} + {$ELSE} + Result := FResponseInfo.ContentText; + {$ENDIF} +end; + +function TIdHTTPAppResponse.GetLogMessage: string; +begin + Result := ''; +end; + +function TIdHTTPAppResponse.GetStatusCode: Integer; +begin + Result := FResponseInfo.ResponseNo; +end; + +function TIdHTTPAppResponse.GetDateVariable(Index: Integer): TDateTime; + // WebBroker apps are responsible for conversion to GMT, Indy HTTP server expects apps to pas local time + function ToGMT(ADateTime: TDateTime): TDateTime; + begin + Result := ADateTime; + if Result <> -1 then + Result := Result - OffsetFromUTC; + end; +begin + //TODO: resource string these + case Index of + INDEX_RESP_Date : Result := ToGMT(FResponseInfo.Date); + INDEX_RESP_Expires : Result := ToGMT(FResponseInfo.Expires); + INDEX_RESP_LastModified : Result := ToGMT(FResponseInfo.LastModified); + else + raise EWBBInvalidIdxGetDateVariable.Create( Format( RSWBBInvalidIdxGetDateVariable,[inttostr(Index)])); + end; +end; + +procedure TIdHTTPAppResponse.SetDateVariable(Index: Integer; const Value: TDateTime); + // WebBroker apps are responsible for conversion to GMT, Indy HTTP server expects apps to pas local time + function ToLocal(ADateTime: TDateTime): TDateTime; + begin + Result := ADateTime; + if Result <> -1 then + Result := Result + OffsetFromUTC; + end; +begin + //TODO: resource string these + case Index of + INDEX_RESP_Date : FResponseInfo.Date := ToLocal(Value); + INDEX_RESP_Expires : FResponseInfo.Expires := ToLocal(Value); + INDEX_RESP_LastModified : FResponseInfo.LastModified := ToLocal(Value); + else + raise EWBBInvalidIdxSetDateVariable.Create(Format(RSWBBInvalidIdxSetDateVariable,[inttostr(Index) ])); + end; +end; + +function TIdHTTPAppResponse.GetIntegerVariable(Index: Integer): Integer; +begin + //TODO: resource string these + case Index of + INDEX_RESP_ContentLength: Result := FResponseInfo.ContentLength; + else + raise EWBBInvalidIdxGetIntVariable.Create( Format( RSWBBInvalidIdxGetIntVariable,[inttostr(Index)])); + end; +end; + +procedure TIdHTTPAppResponse.SetIntegerVariable(Index, Value: Integer); +begin + //TODO: resource string these + case Index of + INDEX_RESP_ContentLength: FResponseInfo.ContentLength := Value; + else + raise EWBBInvalidIdxSetIntVariable.Create( Format(RSWBBInvalidIdxSetIntVariable,[inttostr(Index)])); {do not localize} + end; +end; + +function TIdHTTPAppResponse.GetStringVariable(Index: Integer): AnsiString; +begin + //TODO: resource string these + case Index of + INDEX_RESP_Version :Result := AnsiString(FRequestInfo.Version); + INDEX_RESP_ReasonString :Result := AnsiString(FResponseInfo.ResponseText); + INDEX_RESP_Server :Result := AnsiString(FResponseInfo.Server); + INDEX_RESP_WWWAuthenticate :Result := AnsiString(FResponseInfo.WWWAuthenticate.Text); + INDEX_RESP_Realm :Result := AnsiString(FResponseInfo.AuthRealm); + INDEX_RESP_Allow :Result := AnsiString(FResponseInfo.CustomHeaders.Values['Allow']); {do not localize} + INDEX_RESP_Location :Result := AnsiString(FResponseInfo.Location); + INDEX_RESP_ContentEncoding :Result := AnsiString(FResponseInfo.ContentEncoding); + INDEX_RESP_ContentType : + begin + if FContentType <> '' then begin + Result := FContentType; + end else begin + Result := AnsiString(FResponseInfo.ContentType); + end; + end; + INDEX_RESP_ContentVersion :Result := AnsiString(FResponseInfo.ContentVersion); + INDEX_RESP_DerivedFrom :Result := AnsiString(FResponseInfo.CustomHeaders.Values['Derived-From']); {do not localize} + INDEX_RESP_Title :Result := AnsiString(FResponseInfo.CustomHeaders.Values['Title']); {do not localize} + else + raise EWBBInvalidIdxGetStrVariable.Create(Format(RSWBBInvalidIdxGetStrVariable,[ IntToStr(Index)])); + end; +end; + +procedure TIdHTTPAppResponse.SetStringVariable(Index: Integer; const Value: AnsiString); +begin + //TODO: resource string these + case Index of + INDEX_RESP_Version :EWBBInvalidStringVar.Create(RSWBBInvalidStringVar); + INDEX_RESP_ReasonString :FResponseInfo.ResponseText := string(Value); + INDEX_RESP_Server :FResponseInfo.Server := string(Value); + INDEX_RESP_WWWAuthenticate :FResponseInfo.WWWAuthenticate.Text := string(Value); + INDEX_RESP_Realm :FResponseInfo.AuthRealm := string(Value); + INDEX_RESP_Allow :FResponseInfo.CustomHeaders.Values['Allow'] := string(Value); {do not localize} + INDEX_RESP_Location :FResponseInfo.Location := string(Value); + INDEX_RESP_ContentEncoding :FResponseInfo.ContentEncoding := string(Value); + INDEX_RESP_ContentType : + begin + FResponseInfo.ContentType := string(Value); + FContentType := Value; + end; + INDEX_RESP_ContentVersion :FResponseInfo.ContentVersion := string(Value); + INDEX_RESP_DerivedFrom :FResponseInfo.CustomHeaders.Values['Derived-From'] := string(Value); {do not localize} + INDEX_RESP_Title :FResponseInfo.CustomHeaders.Values['Title'] := string(Value); {do not localize} + else + raise EWBBInvalidIdxSetStringVar.Create( Format(RSWBBInvalidIdxSetStringVar,[IntToStr(Index)])); {do not localize} + end; +end; + +procedure TIdHTTPAppResponse.SendRedirect(const URI: AnsiString); +begin + FSent := True; + MoveCookiesAndCustomHeaders; + FResponseInfo.Redirect(string(URI)); +end; + +procedure TIdHTTPAppResponse.SendResponse; +begin + FSent := True; + // Reset to -1 so Indy will auto set it + FResponseInfo.ContentLength := -1; + MoveCookiesAndCustomHeaders; + FResponseInfo.WriteContent; +end; + +procedure TIdHTTPAppResponse.SendStream(AStream: TStream); +begin + FThread.Connection.IOHandler.Write(AStream); +end; + +function TIdHTTPAppResponse.Sent: Boolean; +begin + Result := FSent; +end; + +procedure TIdHTTPAppResponse.SetContent(const AValue: AnsiString); +var + LValue : string; +begin + {$IFDEF STRING_IS_UNICODE} + // RLebeau 3/28/2013: decode the content using the specified charset. + if FResponseInfo.CharSet <> '' then begin + // AValue contains Encoded bytes + if AValue <> '' then begin + LValue := CharsetToEncoding(FResponseInfo.CharSet).GetString(RawToBytes(PAnsiChar(AValue)^, Length(AValue))); + end; + end else begin + LValue := string(AValue); + end; + {$ELSE} + LValue := string(AValue); + {$ENDIF} + FResponseInfo.ContentText := LValue; + FResponseInfo.ContentLength := Length(LValue); +end; + +procedure TIdHTTPAppResponse.SetLogMessage(const Value: string); +begin + // logging not supported +end; + +procedure TIdHTTPAppResponse.SetStatusCode(AValue: Integer); +begin + FResponseInfo.ResponseNo := AValue; +end; + +procedure TIdHTTPAppResponse.SetContentStream(AValue: TStream); +begin + inherited; + FResponseInfo.ContentStream := AValue; +end; + +function DoHTTPEncode(const AStr: AnsiString): String; +begin + {$IFDEF HAS_TNetEncoding} + Result := TNetEncoding.URL.Encode(string(AStr)); + {$ELSE} + Result := String(HTTPEncode(AStr)); + {$ENDIF} +end; + +procedure TIdHTTPAppResponse.MoveCookiesAndCustomHeaders; +var + i: Integer; + LSrcCookie: TCookie; + LDestCookie: TIdCookie; +begin + for i := 0 to Cookies.Count - 1 do begin + LSrcCookie := Cookies[i]; + LDestCookie := FResponseInfo.Cookies.Add; + LDestCookie.CookieName := DoHTTPEncode(LSrcCookie.Name); + LDestCookie.Value := DoHTTPEncode(LSrcCookie.Value); + LDestCookie.Domain := String(LSrcCookie.Domain); + LDestCookie.Path := String(LSrcCookie.Path); + LDestCookie.Expires := LSrcCookie.Expires; + LDestCookie.Secure := LSrcCookie.Secure; + // TODO: LDestCookie.HttpOnly := LSrcCookie.HttpOnly; + end; + FResponseInfo.CustomHeaders.Clear; + FResponseInfo.CustomHeaders.AddStdValues(CustomHeaders); +end; + +{ TIdHTTPWebBrokerBridge } + +procedure TIdHTTPWebBrokerBridge.DoCommandOther(AThread: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +begin + DoCommandGet(AThread, ARequestInfo, AResponseInfo); + +end; + +procedure TIdHTTPWebBrokerBridge.InitComponent; +begin + inherited InitComponent; + // FOkToProcessCommand := True; +end; + +type + TIdHTTPWebBrokerBridgeRequestHandler = class(TWebRequestHandler) + {$IFDEF HAS_CLASSVARS} + private + class var FWebRequestHandler: TIdHTTPWebBrokerBridgeRequestHandler; + {$ENDIF} + public + constructor Create(AOwner: TComponent); override; + {$IFDEF HAS_CLASSVARS} + {$IFDEF HAS_CLASSDESTRUCTOR} + class destructor Destroy; + {$ENDIF} + {$ENDIF} + destructor Destroy; override; + procedure Run(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); + end; + +{$IFNDEF HAS_CLASSVARS} +var + IndyWebRequestHandler: TIdHTTPWebBrokerBridgeRequestHandler = nil; +{$ENDIF} + +{ TIdHTTPWebBrokerBridgeRequestHandler } + +procedure TIdHTTPWebBrokerBridgeRequestHandler.Run(AThread: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +var + LRequest: TIdHTTPAppRequest; + LResponse: TIdHTTPAppResponse; +begin + try + LRequest := TIdHTTPAppRequest.Create(AThread, ARequestInfo, AResponseInfo); + try + LResponse := TIdHTTPAppResponse.Create(LRequest, AThread, ARequestInfo, AResponseInfo); + try + // WebBroker will free it and we cannot change this behaviour + AResponseInfo.FreeContentStream := False; + HandleRequest(LRequest, LResponse); + finally + FreeAndNil(LResponse); + end; + finally + FreeAndNil(LRequest); + end; + except + // Let Indy handle this exception + raise; + end; +end; + +constructor TIdHTTPWebBrokerBridgeRequestHandler.Create(AOwner: TComponent); +begin + inherited; + Classes.ApplicationHandleException := HandleException; +end; + +destructor TIdHTTPWebBrokerBridgeRequestHandler.Destroy; +begin + Classes.ApplicationHandleException := nil; + inherited; +end; + +{$IFDEF HAS_CLASSVARS} + {$IFDEF HAS_CLASSDESTRUCTOR} +class destructor TIdHTTPWebBrokerBridgeRequestHandler.Destroy; +begin + FreeAndNil(FWebRequestHandler); +end; + {$ENDIF} +{$ENDIF} + +function IdHTTPWebBrokerBridgeRequestHandler: TWebRequestHandler; +begin + {$IFDEF HAS_CLASSVARS} + if not Assigned(TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler) then + TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler := TIdHTTPWebBrokerBridgeRequestHandler.Create(nil); + Result := TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler; + {$ELSE} + if not Assigned(IndyWebRequestHandler) then + IndyWebRequestHandler := TIdHTTPWebBrokerBridgeRequestHandler.Create(nil); + Result := IndyWebRequestHandler; + {$ENDIF} +end; + +procedure TIdHTTPWebBrokerBridge.DoCommandGet(AThread: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +begin + if FWebModuleClass <> nil then begin + // FWebModuleClass, RegisterWebModuleClass supported for backward compatability + RunWebModuleClass(AThread, ARequestInfo, AResponseInfo) + end else + begin + {$IFDEF HAS_CLASSVARS} + TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler.Run(AThread, ARequestInfo, AResponseInfo); + {$ELSE} + IndyWebRequestHandler.Run(AThread, ARequestInfo, AResponseInfo); + {$ENDIF} + end; +end; + +procedure TIdHTTPWebBrokerBridge.RunWebModuleClass(AThread: TIdContext; + ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); +var + LRequest: TIdHTTPAppRequest; + LResponse: TIdHTTPAppResponse; + LWebModule: TCustomWebDispatcher; + {$IFDEF VCL_6_OR_ABOVE} + WebRequestHandler: IWebRequestHandler; + {$ENDIF} + Handled: Boolean; +begin + LRequest := TIdHTTPAppRequest.Create(AThread, ARequestInfo, AResponseInfo); + try + LResponse := TIdHTTPAppResponse.Create(LRequest, AThread, ARequestInfo, AResponseInfo); + try + // WebBroker will free it and we cannot change this behaviour + AResponseInfo.FreeContentStream := False; + // There are better ways in D6, but this works in D5 + LWebModule := FWebModuleClass.Create(nil) as TCustomWebDispatcher; + try + {$IFDEF VCL_6_OR_ABOVE} + if Supports(LWebModule, IWebRequestHandler, WebRequestHandler) then begin + try + Handled := WebRequestHandler.HandleRequest(LRequest, LResponse); + finally + WebRequestHandler := nil; + end; + end else begin + Handled := TWebDispatcherAccess(LWebModule).DispatchAction(LRequest, LResponse); + end; + {$ELSE} + Handled := TWebDispatcherAccess(LWebModule).DispatchAction(LRequest, LResponse); + {$ENDIF} + if Handled and (not LResponse.Sent) then begin + LResponse.SendResponse; + end; + finally + FreeAndNil(LWebModule); + end; + finally + FreeAndNil(LResponse); + end; + finally + FreeAndNil(LRequest); + end; +end; + +// FWebModuleClass, RegisterWebModuleClass supported for backward compatability +// Instead set WebModuleClass using: WebReq.WebRequestHandler.WebModuleClass := TWebModule1; +procedure TIdHTTPWebBrokerBridge.RegisterWebModuleClass(AClass: TComponentClass); +begin + FWebModuleClass := AClass; +end; + +initialization + WebReq.WebRequestHandlerProc := IdHTTPWebBrokerBridgeRequestHandler; +{$IFDEF HAS_CLASSVARS} + {$IFNDEF HAS_CLASSDESTRUCTOR} +finalization + FreeAndNil(TIdHTTPWebBrokerBridgeRequestHandler.FWebRequestHandler); + {$ENDIF} +{$ELSE} +finalization + FreeAndNil(IndyWebRequestHandler); +{$ENDIF} + +end. + + diff --git a/indy/Protocols/IdHash.pas b/indy/Protocols/IdHash.pas new file mode 100644 index 0000000..3f23760 --- /dev/null +++ b/indy/Protocols/IdHash.pas @@ -0,0 +1,482 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.10 7/24/04 12:54:32 PM RLebeau + Compiler fix for TIdHash128.HashValue() + + Rev 1.9 7/23/04 7:09:12 PM RLebeau + Added extra exception handling to various HashValue() methods + + Rev 1.8 2004.05.20 11:37:06 AM czhower + IdStreamVCL + + Rev 1.7 2004.03.03 11:54:30 AM czhower + IdStream change + + Rev 1.6 2004.02.03 5:44:48 PM czhower + Name changes + + Rev 1.5 1/27/2004 4:00:08 PM SPerry + StringStream ->IdStringStream + + Rev 1.4 11/10/2003 7:39:22 PM BGooijen + Did all todo's ( TStream to TIdStream mainly ) + + Rev 1.3 2003.10.24 10:43:08 AM czhower + TIdSTream to dos + + Rev 1.2 10/18/2003 4:28:30 PM BGooijen + Removed the pchar for DotNet + + Rev 1.1 10/8/2003 10:15:10 PM GGrieve + replace TIdReadMemoryStream (might be fast, but not compatible with DotNet) + + Rev 1.0 11/13/2002 08:30:24 AM JPMugaas + Initial import from FTP VC. +} + +unit IdHash; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFIPS, + IdGlobal; + +type + TIdHash = class(TObject) + protected + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; virtual; abstract; + function HashToHex(const AHash: TIdBytes): String; virtual; abstract; + function WordHashToHex(const AHash: TIdBytes; const ACount: Integer): String; + function LongWordHashToHex(const AHash: TIdBytes; const ACount: Integer): String; + public + constructor Create; virtual; + class function IsAvailable : Boolean; virtual; + function HashString(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): TIdBytes; + function HashStringAsHex(const AStr: String; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): String; + function HashBytes(const ASrc: TIdBytes): TIdBytes; + function HashBytesAsHex(const ASrc: TIdBytes): String; + function HashStream(AStream: TStream): TIdBytes; overload; + function HashStreamAsHex(AStream: TStream): String; overload; + function HashStream(AStream: TStream; const AStartPos, ASize: TIdStreamSize): TIdBytes; overload; + function HashStreamAsHex(AStream: TStream; const AStartPos, ASize: TIdStreamSize): String; overload; + end; + + TIdHash16 = class(TIdHash) + protected + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + public + function HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): UInt16; overload; + function HashValue(const ASrc: TIdBytes): UInt16; overload; + function HashValue(AStream: TStream): UInt16; overload; + function HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt16; overload; + procedure HashStart(var VRunningHash : UInt16); virtual; abstract; + procedure HashEnd(var VRunningHash : UInt16); virtual; + procedure HashByte(var VRunningHash : UInt16; const AByte : Byte); virtual; abstract; + end; + + TIdHash32 = class(TIdHash) + protected + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + public + function HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}): UInt32; overload; + function HashValue(const ASrc: TIdBytes): UInt32; overload; + function HashValue(AStream: TStream): UInt32; overload; + function HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt32; overload; + procedure HashStart(var VRunningHash : UInt32); virtual; abstract; + procedure HashEnd(var VRunningHash : UInt32); virtual; + procedure HashByte(var VRunningHash : UInt32; const AByte : Byte); virtual; abstract; + end; + + TIdHashClass = class of TIdHash; + + TIdHashIntF = class(TIdHash) + protected + function HashToHex(const AHash: TIdBytes): String; override; + function InitHash : TIdHashIntCtx; virtual; abstract; + procedure UpdateHash(ACtx : TIdHashIntCtx; const AIn : TIdBytes); + function FinalHash(ACtx : TIdHashIntCtx) : TIdBytes; + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + public + class function IsAvailable : Boolean; override; + class function IsIntfAvailable : Boolean; virtual; + end; + TIdHashNativeAndIntF = class(TIdHashIntF) + protected + function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; virtual; + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + + end; + + {$IFDEF DOTNET} + EIdSecurityAPIException = class(EIdException); + EIdSHA224NotSupported = class(EIdSecurityAPIException); + {$ENDIF} + +implementation + +uses + {$IFDEF DOTNET} + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + IdGlobalProtocols, SysUtils; + +{ TIdHash } + +constructor TIdHash.Create; +begin + inherited Create; +end; + +function TIdHash.HashString(const ASrc: string; ADestEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): TIdBytes; +var + LStream: TStream; // not TIdStringStream - Unicode on DotNet! +begin + LStream := TMemoryStream.Create; try + WriteStringToStream(LStream, ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}); + LStream.Position := 0; + Result := HashStream(LStream); + finally FreeAndNil(LStream); end; +end; + +function TIdHash.HashStringAsHex(const AStr: String; ADestEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): String; +begin + Result := HashToHex(HashString(AStr, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +function TIdHash.HashBytes(const ASrc: TIdBytes): TIdBytes; +var + LStream: TStream; +begin + // TODO: use TBytesStream on versions that support it + LStream := TMemoryStream.Create; try + WriteTIdBytesToStream(LStream, ASrc); + LStream.Position := 0; + Result := HashStream(LStream); + finally FreeAndNil(LStream); end; +end; + +function TIdHash.HashBytesAsHex(const ASrc: TIdBytes): String; +begin + Result := HashToHex(HashBytes(ASrc)); +end; + +function TIdHash.HashStream(AStream: TStream): TIdBytes; +begin + Result := HashStream(AStream, -1, -1); +end; + +function TIdHash.HashStreamAsHex(AStream: TStream): String; +begin + Result := HashToHex(HashStream(AStream)); +end; + +function TIdHash.HashStream(AStream: TStream; const AStartPos, ASize: TIdStreamSize): TIdBytes; +var + LSize, LAvailable: TIdStreamSize; +begin + if AStartPos >= 0 then begin + AStream.Position := AStartPos; + end; + LAvailable := AStream.Size - AStream.Position; + if ASize < 0 then begin + LSize := LAvailable; + end else begin + LSize := IndyMin(LAvailable, ASize); + end; + Result := GetHashBytes(AStream, LSize); +end; + +function TIdHash.HashStreamAsHex(AStream: TStream; const AStartPos, ASize: TIdStreamSize): String; +begin + Result := HashToHex(HashStream(AStream, AStartPos, ASize)); +end; + +function TIdHash.WordHashToHex(const AHash: TIdBytes; const ACount: Integer): String; +var + LValue: UInt16; + I: Integer; +begin + Result := ''; + for I := 0 to ACount-1 do begin + LValue := BytesToUInt16(AHash, SizeOf(UInt16)*I); + Result := Result + IntToHex(LValue, 4); + end; +end; + +function TIdHash.LongWordHashToHex(const AHash: TIdBytes; const ACount: Integer): String; +begin + Result := ToHex(AHash, ACount*SizeOf(UInt32)); +end; + +class function TIdHash.IsAvailable : Boolean; +begin + Result := True; +end; + +{ TIdHash16 } + +function TIdHash16.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +const + cBufSize = 1024; // Keep it small for dotNet +var + I: Integer; + LBuffer: TIdBytes; + LSize: Integer; + LHash: UInt16; +begin + Result := nil; + HashStart(LHash); + + SetLength(LBuffer, cBufSize); + + while ASize > 0 do + begin + LSize := ReadTIdBytesFromStream(AStream, LBuffer, IndyMin(cBufSize, ASize)); + if LSize < 1 then begin + Break; // TODO: throw a stream read exception instead? + end; + for i := 0 to LSize - 1 do begin + HashByte(LHash, LBuffer[i]); + end; + Dec(ASize, LSize); + end; + + HashEnd(LHash); + + SetLength(Result, SizeOf(UInt16)); + CopyTIdUInt16(LHash, Result, 0); +end; + +function TIdHash16.HashToHex(const AHash: TIdBytes): String; +begin + Result := IntToHex(BytesToUInt16(AHash), 4); +end; + +procedure TIdHash16.HashEnd(var VRunningHash : UInt16); +begin +end; + +function TIdHash16.HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): UInt16; +begin + Result := BytesToUInt16(HashString(ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +function TIdHash16.HashValue(const ASrc: TIdBytes): UInt16; +begin + Result := BytesToUInt16(HashBytes(ASrc)); +end; + +function TIdHash16.HashValue(AStream: TStream): UInt16; +begin + Result := BytesToUInt16(HashStream(AStream)); +end; + +function TIdHash16.HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize): UInt16; +begin + Result := BytesToUInt16(HashStream(AStream, AStartPos, ASize)); +end; + +{ TIdHash32 } + +function TIdHash32.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +const + cBufSize = 1024; // Keep it small for dotNet +var + I: Integer; + LBuffer: TIdBytes; + LSize: Integer; + LHash: UInt32; +begin + Result := nil; + HashStart(LHash); + + SetLength(LBuffer, cBufSize); + + while ASize > 0 do + begin + LSize := ReadTIdBytesFromStream(AStream, LBuffer, IndyMin(cBufSize, ASize)); + if LSize < 1 then begin + Break; // TODO: throw a stream read exception instead? + end; + for i := 0 to LSize - 1 do begin + HashByte(LHash, LBuffer[i]); + end; + Dec(ASize, LSize); + end; + + HashEnd(LHash); // RLebeau: TIdHashCRC32 uses this to XOR the hash with $FFFFFFFF + + SetLength(Result, SizeOf(UInt32)); + CopyTIdUInt32(LHash, Result, 0); +end; + +function TIdHash32.HashToHex(const AHash: TIdBytes): String; +begin + Result := UInt32ToHex(BytesToUInt32(AHash)); +end; + +procedure TIdHash32.HashEnd(var VRunningHash : UInt32); +begin +end; + +function TIdHash32.HashValue(const ASrc: string; ADestEncoding: IIdTextEncoding = nil + {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF} + ): UInt32; +begin + Result := BytesToUInt32(HashString(ASrc, ADestEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF})); +end; + +function TIdHash32.HashValue(const ASrc: TIdBytes): UInt32; +begin + Result := BytesToUInt32(HashBytes(ASrc)); +end; + +function TIdHash32.HashValue(AStream: TStream) : UInt32; +begin + Result := BytesToUInt32(HashStream(AStream)); +end; + +function TIdHash32.HashValue(AStream: TStream; const AStartPos, ASize: TIdStreamSize) : UInt32; +begin + Result := BytesToUInt32(HashStream(AStream, AStartPos, ASize)); +end; + + +{ TIdHashIntf } + +function TIdHashIntf.FinalHash(ACtx: TIdHashIntCtx): TIdBytes; +{$IFDEF DOTNET} +var + LDummy : TIdBytes; +{$ENDIF} +begin + {$IFDEF DOTNET} + //This is a funny way of coding. I have to pass a dummy value to + //TransformFinalBlock so that things can work similarly to the OpenSSL + //Crypto API. You can't pass nul to TransformFinalBlock without an exception. + SetLength(LDummy,0); + ACtx.TransformFinalBlock(LDummy,0,0); + Result := ACtx.Hash; + {$ELSE} + Result := IdFIPS.FinalHashInst(ACtx); + {$ENDIF} +end; + +function TIdHashIntf.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +var + LBuf : TIdBytes; + LSize : Int64; + LCtx : TIdHashIntCtx; +begin + LCtx := InitHash; + try + if ASize > 0 then begin + SetLength(LBuf, 2048); + repeat + LSize := ReadTIdBytesFromStream(AStream,LBuf,IndyMin(ASize, 2048)); + if LSize < 1 then begin + break; + end; + if LSize < 2048 then begin + SetLength(LBuf,LSize); + UpdateHash(LCtx,LBuf); + break; + end; + UpdateHash(LCtx,LBuf); + Dec(ASize, LSize); + until ASize = 0; + end; + finally + Result := FinalHash(LCtx); + end; +end; + +function TIdHashIntf.HashToHex(const AHash: TIdBytes): String; +begin + Result := ToHex(AHash); +end; + +{$IFDEF DOTNET} +class function TIdHashIntf.IsAvailable: Boolean; +begin + Result := True; +end; + +class function TIdHashIntF.IsIntfAvailable: Boolean; +begin + Result := False; +end; +{$ELSE} +//done this way so we can override IsAvailble if there is a native +//implementation. + + +class function TIdHashIntf.IsAvailable: Boolean; +begin + Result := IsIntfAvailable; +end; + +class function TIdHashIntF.IsIntfAvailable: Boolean; +begin + Result := IsHashingIntfAvail; +end; +{$ENDIF} + +procedure TIdHashIntf.UpdateHash(ACtx: TIdHashIntCtx; const AIn: TIdBytes); +begin + UpdateHashInst(ACtx,AIn); + {$IFDEF DOTNET} + ACtx.TransformBlock(AIn,0,Length(AIn),AIn,0); + {$ELSE} + {$ENDIF} +end; + +{ TIdHashNativeAndIntF } + +function TIdHashNativeAndIntF.GetHashBytes(AStream: TStream; + ASize: TIdStreamSize): TIdBytes; +begin + if IsIntfAvailable then begin + Result := inherited GetHashBytes(AStream, ASize); + end else begin + Result := NativeGetHashBytes(AStream, ASize); + end; +end; + +function TIdHashNativeAndIntF.NativeGetHashBytes(AStream: TStream; + ASize: TIdStreamSize): TIdBytes; +begin + Result := nil; +end; + +end. diff --git a/indy/Protocols/IdHashAdler32.pas b/indy/Protocols/IdHashAdler32.pas new file mode 100644 index 0000000..506face --- /dev/null +++ b/indy/Protocols/IdHashAdler32.pas @@ -0,0 +1,87 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 11/13/2002 08:30:30 AM JPMugaas + Initial import from FTP VC. +} + +{*===========================================================================*} +{* DESCRIPTION *} +{*****************************************************************************} +{* PROJECT : Indy 10 *} +{* AUTHOR : Bas Gooijen *} +{* MAINTAINER : Bas Gooijen *} +{*...........................................................................*} +{* DESCRIPTION *} +{* *} +{* Implementation of the Adler 32 hash algoritm *} +{* Adler 32 is almost as reliable as CRC32, but faster *} +{* *} +{*...........................................................................*} +{* HISTORY *} +{* DATE VERSION AUTHOR REASONS *} +{* *} +{* 17/10/2002 1.0 Bas Gooijen Initial start *} +{*****************************************************************************} + +unit IdHashAdler32; + +interface +{$i IdCompilerDefines.inc} + +uses + IdHash; + +type + TIdHashAdler32 = class(TIdHash32) + public + procedure HashStart(var VRunningHash : LongWord); override; + procedure HashByte(var VRunningHash : LongWord; const AByte : Byte); override; + end; + +implementation + +const + BASE = 65521; { largest prime smaller than 65536 } + NMAX = 5552; { NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 } + +{ TIdHashAdler32 } + +procedure TIdHashAdler32.HashStart(var VRunningHash : LongWord); +begin + VRunningHash := 1; +end; + +procedure TIdHashAdler32.HashByte(var VRunningHash : LongWord; const AByte : Byte); +var + s1, s2: LongWord; +begin + s1 := VRunningHash and $FFFF; + s2 := (VRunningHash shr 16) and $FFFF; + + Inc(s1, AByte); + Inc(s2, s1); + + s1 := s1 mod BASE; + s2 := s2 mod BASE; + + VRunningHash := (s2 shl 16) or s1; +end; + +end. + diff --git a/indy/Protocols/IdHashCRC.pas b/indy/Protocols/IdHashCRC.pas new file mode 100644 index 0000000..2ede7d6 --- /dev/null +++ b/indy/Protocols/IdHashCRC.pas @@ -0,0 +1,171 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.4 28.09.2004 21:37:20 Andreas Hausladen + Added Typecast to surpress Delphi 5 compiler warning + + Rev 1.3 2004.02.03 5:44:48 PM czhower + Name changes + + Rev 1.2 24/01/2004 19:20:52 CCostelloe + Cleaned up warnings + + Rev 1.1 2003-10-16 11:06:28 HHellstrm + Fixed for dotNET + + Rev 1.0 11/13/2002 08:30:40 AM JPMugaas + Initial import from FTP VC. +} + +unit IdHashCRC; + +interface +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdHash; + +type + TIdHashCRC16 = class(TIdHash16) + public + procedure HashStart(var VRunningHash : UInt16); override; + procedure HashByte(var VRunningHash : UInt16; const AByte : Byte); override; + end; + + TIdHashCRC32 = class(TIdHash32) + public + procedure HashStart(var VRunningHash : UInt32); override; + procedure HashEnd(var VRunningHash : UInt32); override; + procedure HashByte(var VRunningHash : UInt32; const AByte : Byte); override; + end; + +implementation + +const + CRC16Table: array[0..255] of UInt16 = + ( $0000, $C0C1, $C181, $0140, $C301, $03C0, $0280, $C241, $C601, $06C0, $0780, + $C741, $0500, $C5C1, $C481, $0440, $CC01, $0CC0, $0D80, $CD41, $0F00, $CFC1, + $CE81, $0E40, $0A00, $CAC1, $CB81, $0B40, $C901, $09C0, $0880, $C841, $D801, + $18C0, $1980, $D941, $1B00, $DBC1, $DA81, $1A40, $1E00, $DEC1, $DF81, $1F40, + $DD01, $1DC0, $1C80, $DC41, $1400, $D4C1, $D581, $1540, $D701, $17C0, $1680, + $D641, $D201, $12C0, $1380, $D341, $1100, $D1C1, $D081, $1040, $F001, $30C0, + $3180, $F141, $3300, $F3C1, $F281, $3240, $3600, $F6C1, $F781, $3740, $F501, + $35C0, $3480, $F441, $3C00, $FCC1, $FD81, $3D40, $FF01, $3FC0, $3E80, $FE41, + $FA01, $3AC0, $3B80, $FB41, $3900, $F9C1, $F881, $3840, $2800, $E8C1, $E981, + $2940, $EB01, $2BC0, $2A80, $EA41, $EE01, $2EC0, $2F80, $EF41, $2D00, $EDC1, + $EC81, $2C40, $E401, $24C0, $2580, $E541, $2700, $E7C1, $E681, $2640, $2200, + $E2C1, $E381, $2340, $E101, $21C0, $2080, $E041, $A001, $60C0, $6180, $A141, + $6300, $A3C1, $A281, $6240, $6600, $A6C1, $A781, $6740, $A501, $65C0, $6480, + $A441, $6C00, $ACC1, $AD81, $6D40, $AF01, $6FC0, $6E80, $AE41, $AA01, $6AC0, + $6B80, $AB41, $6900, $A9C1, $A881, $6840, $7800, $B8C1, $B981, $7940, $BB01, + $7BC0, $7A80, $BA41, $BE01, $7EC0, $7F80, $BF41, $7D00, $BDC1, $BC81, $7C40, + $B401, $74C0, $7580, $B541, $7700, $B7C1, $B681, $7640, $7200, $B2C1, $B381, + $7340, $B101, $71C0, $7080, $B041, $5000, $90C1, $9181, $5140, $9301, $53C0, + $5280, $9241, $9601, $56C0, $5780, $9741, $5500, $95C1, $9481, $5440, $9C01, + $5CC0, $5D80, $9D41, $5F00, $9FC1, $9E81, $5E40, $5A00, $9AC1, $9B81, $5B40, + $9901, $59C0, $5880, $9841, $8801, $48C0, $4980, $8941, $4B00, $8BC1, $8A81, + $4A40, $4E00, $8EC1, $8F81, $4F40, $8D01, $4DC0, $4C80, $8C41, $4400, $84C1, + $8581, $4540, $8701, $47C0, $4680, $8641, $8201, $42C0, $4380, $8341, $4100, + $81C1, $8081, $4040 ) ; + + CRC32Table: array[0..255] of UInt32 = ( + $00000000, $77073096, $EE0E612C, $990951BA, $076DC419, + $706AF48F, $E963A535, $9E6495A3, $0EDB8832, $79DCB8A4, + $E0D5E91E, $97D2D988, $09B64C2B, $7EB17CBD, $E7B82D07, + $90BF1D91, $1DB71064, $6AB020F2, $F3B97148, $84BE41DE, + $1ADAD47D, $6DDDE4EB, $F4D4B551, $83D385C7, $136C9856, + $646BA8C0, $FD62F97A, $8A65C9EC, $14015C4F, $63066CD9, + $FA0F3D63, $8D080DF5, $3B6E20C8, $4C69105E, $D56041E4, + $A2677172, $3C03E4D1, $4B04D447, $D20D85FD, $A50AB56B, + $35B5A8FA, $42B2986C, $DBBBC9D6, $ACBCF940, $32D86CE3, + $45DF5C75, $DCD60DCF, $ABD13D59, $26D930AC, $51DE003A, + $C8D75180, $BFD06116, $21B4F4B5, $56B3C423, $CFBA9599, + $B8BDA50F, $2802B89E, $5F058808, $C60CD9B2, $B10BE924, + $2F6F7C87, $58684C11, $C1611DAB, $B6662D3D, $76DC4190, + $01DB7106, $98D220BC, $EFD5102A, $71B18589, $06B6B51F, + $9FBFE4A5, $E8B8D433, $7807C9A2, $0F00F934, $9609A88E, + $E10E9818, $7F6A0DBB, $086D3D2D, $91646C97, $E6635C01, + $6B6B51F4, $1C6C6162, $856530D8, $F262004E, $6C0695ED, + $1B01A57B, $8208F4C1, $F50FC457, $65B0D9C6, $12B7E950, + $8BBEB8EA, $FCB9887C, $62DD1DDF, $15DA2D49, $8CD37CF3, + $FBD44C65, $4DB26158, $3AB551CE, $A3BC0074, $D4BB30E2, + $4ADFA541, $3DD895D7, $A4D1C46D, $D3D6F4FB, $4369E96A, + $346ED9FC, $AD678846, $DA60B8D0, $44042D73, $33031DE5, + $AA0A4C5F, $DD0D7CC9, $5005713C, $270241AA, $BE0B1010, + $C90C2086, $5768B525, $206F85B3, $B966D409, $CE61E49F, + $5EDEF90E, $29D9C998, $B0D09822, $C7D7A8B4, $59B33D17, + $2EB40D81, $B7BD5C3B, $C0BA6CAD, $EDB88320, $9ABFB3B6, + $03B6E20C, $74B1D29A, $EAD54739, $9DD277AF, $04DB2615, + $73DC1683, $E3630B12, $94643B84, $0D6D6A3E, $7A6A5AA8, + $E40ECF0B, $9309FF9D, $0A00AE27, $7D079EB1, $F00F9344, + $8708A3D2, $1E01F268, $6906C2FE, $F762575D, $806567CB, + $196C3671, $6E6B06E7, $FED41B76, $89D32BE0, $10DA7A5A, + $67DD4ACC, $F9B9DF6F, $8EBEEFF9, $17B7BE43, $60B08ED5, + $D6D6A3E8, $A1D1937E, $38D8C2C4, $4FDFF252, $D1BB67F1, + $A6BC5767, $3FB506DD, $48B2364B, $D80D2BDA, $AF0A1B4C, + $36034AF6, $41047A60, $DF60EFC3, $A867DF55, $316E8EEF, + $4669BE79, $CB61B38C, $BC66831A, $256FD2A0, $5268E236, + $CC0C7795, $BB0B4703, $220216B9, $5505262F, $C5BA3BBE, + $B2BD0B28, $2BB45A92, $5CB36A04, $C2D7FFA7, $B5D0CF31, + $2CD99E8B, $5BDEAE1D, $9B64C2B0, $EC63F226, $756AA39C, + $026D930A, $9C0906A9, $EB0E363F, $72076785, $05005713, + $95BF4A82, $E2B87A14, $7BB12BAE, $0CB61B38, $92D28E9B, + $E5D5BE0D, $7CDCEFB7, $0BDBDF21, $86D3D2D4, $F1D4E242, + $68DDB3F8, $1FDA836E, $81BE16CD, $F6B9265B, $6FB077E1, + $18B74777, $88085AE6, $FF0F6A70, $66063BCA, $11010B5C, + $8F659EFF, $F862AE69, $616BFFD3, $166CCF45, $A00AE278, + $D70DD2EE, $4E048354, $3903B3C2, $A7672661, $D06016F7, + $4969474D, $3E6E77DB, $AED16A4A, $D9D65ADC, $40DF0B66, + $37D83BF0, $A9BCAE53, $DEBB9EC5, $47B2CF7F, $30B5FFE9, + $BDBDF21C, $CABAC28A, $53B39330, $24B4A3A6, $BAD03605, + $CDD70693, $54DE5729, $23D967BF, $B3667A2E, $C4614AB8, + $5D681B02, $2A6F2B94, $B40BBE37, $C30C8EA1, $5A05DF1B, + $2D02EF8D ) ; + +{ TIdHashCRC16 } + +procedure TIdHashCRC16.HashStart(var VRunningHash: UInt16); +begin + VRunningHash := 0; +end; + +procedure TIdHashCRC16.HashByte(var VRunningHash: UInt16; const AByte: Byte); +begin + VRunningHash := (VRunningHash shr 8) xor CRC16Table[AByte xor (VRunningHash and $FF)]; +end; + +{ TIdHashCRC32 } + +procedure TIdHashCRC32.HashStart(var VRunningHash: UInt32); +begin + VRunningHash := $FFFFFFFF; +end; + +procedure TIdHashCRC32.HashEnd(var VRunningHash : UInt32); +begin + VRunningHash := VRunningHash xor $FFFFFFFF; +end; + +procedure TIdHashCRC32.HashByte(var VRunningHash: UInt32; const AByte: Byte); +begin + VRunningHash := ((VRunningHash shr 8) and $00FFFFFF) xor CRC32Table[(VRunningHash xor AByte) and $FF]; +end; + +end. + diff --git a/indy/Protocols/IdHashElf.pas b/indy/Protocols/IdHashElf.pas new file mode 100644 index 0000000..98ecc93 --- /dev/null +++ b/indy/Protocols/IdHashElf.pas @@ -0,0 +1,64 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.1 2003-10-16 11:22:42 HHellstrm + Fixed for dotNET + + Rev 1.0 11/13/2002 07:53:32 AM JPMugaas +} + +unit IdHashElf; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, + IdHash; + +type + TIdHashElf = class(TIdHash32) + public + procedure HashStart(var VRunningHash : UInt32); override; + procedure HashByte(var VRunningHash : UInt32; const AByte : Byte); override; + end; + +implementation + +{ TIdHashElf } + +procedure TIdHashElf.HashStart(var VRunningHash: UInt32); +begin + VRunningHash := 0; +end; + +procedure TIdHashElf.HashByte(var VRunningHash: UInt32; const AByte: Byte); +var + LTemp: UInt32; +begin + VRunningHash := (VRunningHash shl 4) + AByte; + LTemp := VRunningHash and $F0000000; + if LTemp <> 0 then begin + VRunningHash := VRunningHash xor (LTemp shr 24); + end; + VRunningHash := VRunningHash and (not LTemp); +end; + +end. + diff --git a/indy/Protocols/IdHashIntf.pas b/indy/Protocols/IdHashIntf.pas new file mode 100644 index 0000000..80e08b2 --- /dev/null +++ b/indy/Protocols/IdHashIntf.pas @@ -0,0 +1,254 @@ +unit IdHashIntf; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFIPS, + IdGlobal, IdHash, + {$IFDEF DOTNET} + System.Security.Cryptography, + IdException + {$ELSE} + IdStreamVCL + {$ENDIF} + ; + +type + TIdHashInt = class(TIdHash) + protected + function HashToHex(const AHash: TIdBytes): String; override; + function GetHashInst : TIdHashInst; virtual; abstract; + function InitHash : TIdHashIntCtx; virtual; + procedure UpdateHash(ACtx : TIdHashIntCtx; const AIn : TIdBytes); + function FinalHash(ACtx : TIdHashIntCtx) : TIdBytes; + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + {$IFNDEF DOTNET} + public + class function IsAvailable : Boolean; override; + {$ENDIF} + end; + TIdHashSHA224 = class(TIdHashInt) + protected + function GetHashInst : TIdHashInst; override; + {$IFNDEF DOTNET} + public + class function IsAvailable : Boolean; override; + {$ENDIF} + end; + TIdHashSHA256 = class(TIdHashInt) + protected + function GetHashInst : TIdHashInst; override; + {$IFNDEF DOTNET} + public + class function IsAvailable : Boolean; override; + {$ENDIF} + end; + TIdHashSHA386 = class(TIdHashInt) + protected + function GetHashInst : TIdHashInst; override; + {$IFNDEF DOTNET} + public + class function IsAvailable : Boolean; override; + {$ENDIF} + end; + TIdHashSHA512 = class(TIdHashInt) + protected + function GetHashInst : TIdHashInst; override; + {$IFNDEF DOTNET} + public + class function IsAvailable : Boolean; override; + {$ENDIF} + end; + {$IFDEF DOTNET} + EIdSecurityAPIException = class(EIdException); + EIdSHA224NotSupported = class(EIdSecurityAPIException); + {$ELSE} + EIdDigestError = class(EIdOpenSSLAPICryptoError); + EIdDigestFinalEx = class(EIdDigestError); + EIdDigestInitEx = class(EIdDigestError); + EIdDigestUpdate = class(EIdDigestError); + {$ENDIF} + +implementation +{$IFNDEF DOTNET} +uses IdCTypes; +{$ENDIF} + +{ TIdHashInt } + +function TIdHashInt.FinalHash(ACtx: TIdHashIntCtx): TIdBytes; +var +{$IFDEF DOTNET} + LDummy : TIdBytes; +{$ELSE} + LLen, LRet : TIdC_UInt; +{$ENDIF} +begin + {$IFDEF DOTNET} + //This is a funny way of coding. I have to pass a dummy value to + //TransformFinalBlock so that things can work similarly to the OpenSSL + //Crypto API. You can't pass nul to TransformFinalBlock without an exception. + SetLength(LDummy,0); + ACtx.TransformFinalBlock(LDummy,0,0); + Result := ACtx.Hash; + {$ELSE} + SetLength(Result,OPENSSL_EVP_MAX_MD_SIZE); + LRet := IdSslEvpDigestFinalEx(@ACtx,@Result[0],LLen); + if LRet <> 1 then begin + EIdDigestFinalEx.RaiseException('EVP_DigestFinal_ex error'); + end; + SetLength(Result,LLen); + IdSslEvpMDCtxCleanup(@ACtx); + {$ENDIF} +end; + +function TIdHashInt.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +var LBuf : TIdBytes; + LSize : Int64; + LCtx : TIdHashIntCtx; +begin + LCtx := InitHash; + try + SetLength(LBuf,2048); + repeat + LSize := ReadTIdBytesFromStream(AStream,LBuf,2048); + if LSize = 0 then begin + break; + end; + if LSize < 2048 then begin + SetLength(LBuf,LSize); + UpdateHash(LCtx,LBuf); + break; + end else begin + UpdateHash(LCtx,LBuf); + end; + until False; + finally + Result := FinalHash(LCtx); + end; +end; + +function TIdHashInt.HashToHex(const AHash: TIdBytes): String; +begin + Result := ToHex(AHash); +end; + +function TIdHashInt.InitHash: TIdHashIntCtx; + {$IFNDEF DOTNET} +var + LHash : TIdHashInst; + LRet : TIdC_Int; + {$ENDIF} +begin + {$IFDEF DOTNET} + Result := GetHashInst; + {$ELSE} + LHash := GetHashInst; + IdSslEvpMDCtxInit(@Result); + LRet := IdSslEvpDigestInitEx(@Result, LHash, nil); + if LRet <> 1 then begin + EIdDigestInitEx.RaiseException('EVP_DigestInit_ex error'); + end; + {$ENDIF} +end; + +{$IFNDEF DOTNET} +class function TIdHashInt.IsAvailable: Boolean; +begin + Result := Assigned(IdSslEvpDigestInitEx) and + Assigned(IdSslEvpDigestUpdate) and + Assigned(IdSslEvpDigestFinalEx); +end; +{$ENDIF} + +procedure TIdHashInt.UpdateHash(ACtx: TIdHashIntCtx; const AIn: TIdBytes); +{$IFNDEF DOTNET} +var LRet : TIdC_Int; +{$ENDIF} +begin + {$IFDEF DOTNET} + ACtx.TransformBlock(AIn,0,Length(AIn),AIn,0); + {$ELSE} + LRet := IdSslEvpDigestUpdate(@ACtx,@Ain[0],Length(AIn)); + if LRet <> 1 then begin + EIdDigestInitEx.RaiseException('EVP_DigestUpdate error'); + end; + {$ENDIF} +end; + +{ TIdHashSHA224 } + +function TIdHashSHA224.GetHashInst: TIdHashInst; +begin + {$IFDEF DOTNET} + Result := nil; + Raise EIdSHA224NotSupported.Create('SHA224 not supported.'); + {$ELSE} + Result := IdSslEvpSHA224; + {$ENDIF} +end; + +{$IFNDEF DOTNET} +class function TIdHashSHA224.IsAvailable: Boolean; +begin + Result := Assigned(IdSslEvpSHA224) and inherited IsAvailable; +end; +{$ENDIF} + +{ TIdHashSHA256 } + +function TIdHashSHA256.GetHashInst: TIdHashInst; +begin + {$IFDEF DOTNET} + Result := System.Security.Cryptography.SHA256Managed.Create; + {$ELSE} + Result := IdSslEvpSHA256; + {$ENDIF} +end; + +{$IFNDEF DOTNET} +class function TIdHashSHA256.IsAvailable: Boolean; +begin + Result := Assigned(IdSslEvpSHA256) and inherited IsAvailable; +end; +{$ENDIF} + +{ TIdHashSHA386 } + +function TIdHashSHA386.GetHashInst: TIdHashInst; +begin + {$IFDEF DOTNET} + Result := System.Security.Cryptography.SHA384Managed.Create; + {$ELSE} + Result := IdSslEvpSHA384; + {$ENDIF} +end; + +{$IFNDEF DOTNET} +class function TIdHashSHA386.IsAvailable: Boolean; +begin + Result := Assigned(IdSslEvpSHA384) and inherited IsAvailable; +end; +{$ENDIF} + +{ TIdHashSHA512 } + +function TIdHashSHA512.GetHashInst: TIdHashInst; +begin + {$IFDEF DOTNET} + Result := System.Security.Cryptography.SHA512Managed.Create; + {$ELSE} + Result := IdSslEvpSHA512; + {$ENDIF} +end; + +{$IFNDEF DOTNET} +class function TIdHashSHA512.IsAvailable: Boolean; +begin + Result := Assigned(IdSslEvpSHA512) and inherited IsAvailable; +end; +{$ENDIF} + +end. diff --git a/indy/Protocols/IdHashMessageDigest.pas b/indy/Protocols/IdHashMessageDigest.pas new file mode 100644 index 0000000..e3acd40 --- /dev/null +++ b/indy/Protocols/IdHashMessageDigest.pas @@ -0,0 +1,567 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.3 24/01/2004 19:21:36 CCostelloe + Cleaned up warnings + + Rev 1.2 1/15/2004 2:32:50 AM JPMugaas + Attempt to add MD5 coder support for partial streams. THis is needed for the + XMD5 command in the FTP Server. + + Rev 1.1 2003-10-12 22:36:40 HHellstrm + Reimplemented, optimized and tested for both Win32 and dotNET. + + Rev 1.0 11/13/2002 07:53:40 AM JPMugaas +} +{ + Implementation of the MD2, MD4 and MD5 Message-Digest Algorithm + as specified in RFC 1319 (1115), 1320 (1186), 1321 + + Author: Henrick Hellstrm + + Original Intellectual Property Statement: + Author: Pete Mee + Port to Indy 8.1 Doychin Bondzhev (doychin@dsoft-bg.com) + Copyright: (c) Chad Z. Hower and The Winshoes Working Group. +} + +unit IdHashMessageDigest; + +interface +{$i IdCompilerDefines.inc} + +uses + IdFIPS, IdGlobal, IdHash, Classes; + +type + T4x4LongWordRecord = array[0..3] of UInt32; + T16x4LongWordRecord = array[0..15] of UInt32; + T4x4x4LongWordRecord = array[0..3] of T4x4LongWordRecord; + + T512BitRecord = array[0..63] of Byte; + T384BitRecord = array[0..47] of Byte; + T128BitRecord = array[0..15] of Byte; + + TIdHashMessageDigest = class(TIdHashNativeAndIntF) + protected + FCBuffer: TIdBytes; + procedure MDCoder; virtual; abstract; + procedure Reset; virtual; + end; + + TIdHashMessageDigest2 = class(TIdHashMessageDigest) + protected + FX: T384BitRecord; + FCheckSum: T128BitRecord; + + procedure MDCoder; override; + procedure Reset; override; + + function InitHash : TIdHashIntCtx; override; + function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + public + constructor Create; override; + class function IsIntfAvailable: Boolean; override; + end; + + TIdHashMessageDigest4 = class(TIdHashMessageDigest) + protected + FState: T4x4LongWordRecord; + + function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + + procedure MDCoder; override; + + function InitHash : TIdHashIntCtx; override; + + public + constructor Create; override; + class function IsIntfAvailable: Boolean; override; + end; + + TIdHashMessageDigest5 = class(TIdHashMessageDigest4) + protected + procedure MDCoder; override; + + function InitHash : TIdHashIntCtx; override; + public + class function IsIntfAvailable : Boolean; override; + end; + +implementation +uses + {$IFDEF DOTNET} + System.Security.Cryptography, + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + IdGlobalProtocols; + +{ TIdHashMessageDigest } + +procedure TIdHashMessageDigest.Reset; +begin + FillBytes(FCBuffer, Length(FCBuffer), 0); +end; + +{ TIdHashMessageDigest2 } + +const + MD2_PI_SUBST : array [0..255] of Byte = ( + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, + 6, 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, + 188, 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, + 111, 24, 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, + 160, 251, 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, + 7, 63, 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, + 90, 144, 50, 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, + 48, 179, 72, 165, 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, + 79, 184, 56, 210, 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, + 4, 241, 69, 157, 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, + 230, 45, 168, 2, 27, 96, 37, 173, 174, 176, 185, 246, 28, 70, + 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221, 81, 175, 58, + 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110, 133, 40, + 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55, 200, + 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136, + 149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, + 242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, + 75, 10, 49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, + 159, 17, 131, 20); + +constructor TIdHashMessageDigest2.Create; +begin + inherited Create; + SetLength(FCBuffer, 16); +end; + +procedure TIdHashMessageDigest2.MDCoder; +const + NumRounds = 18; +var + x: Byte; + i, j: Integer; + T: UInt16; + LCheckSumScore: Byte; +begin + // Move the next 16 bytes into the second 16 bytes of X. + for i := 0 to 15 do begin + x := FCBuffer[i]; + FX[i + 16] := x; + FX[i + 32] := x xor FX[i]; + end; + + { Do 18 rounds. } + T := 0; + for i := 0 to NumRounds - 1 do begin + for j := 0 to 47 do + begin + T := FX[j] xor MD2_PI_SUBST[T]; + FX[j] := T and $FF; + end; + T := (T + i) and $FF; + end; + + LCheckSumScore := FChecksum[15]; + for i := 0 to 15 do begin + x := FCBuffer[i] xor LCheckSumScore; + LCheckSumScore := FChecksum[i] xor MD2_PI_SUBST[x]; + FChecksum[i] := LCheckSumScore; + end; +end; + +// Clear Buffer and Checksum arrays +procedure TIdHashMessageDigest2.Reset; +var + I: Integer; +begin + inherited Reset; + for I := 0 to 15 do begin + FCheckSum[I] := 0; + FX[I] := 0; + FX[I+16] := 0; + FX[I+32] := 0; + end; +end; + +function TIdHashMessageDigest2.NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +var + LStartPos: Integer; + LSize: Integer; + Pad: Byte; + I: Integer; +begin + Result := nil; + Reset; + + // Code the entire file in complete 16-byte chunks. + while ASize >= 16 do begin + LSize := ReadTIdBytesFromStream(AStream, FCBuffer, 16); + // TODO: handle stream read error + MDCoder; + Dec(ASize, LSize); + end; + + // Read the last set of bytes. + LStartPos := ReadTIdBytesFromStream(AStream, FCBuffer, ASize); + // TODO: handle stream read error + Pad := 16 - LStartPos; + + // Step 1 + for I := LStartPos to 15 do begin + FCBuffer[I] := Pad; + end; + MDCoder; + + // Step 2 + for I := 0 to 15 do begin + FCBuffer[I] := FCheckSum[I]; + end; + MDCoder; + + SetLength(Result, SizeOf(UInt32)*4); + for I := 0 to 3 do + begin + CopyTIdUInt32( + FX[I*4] + (FX[I*4+1] shl 8) + (FX[I*4+2] shl 16) + (FX[I*4+3] shl 24), + Result, SizeOf(UInt32)*I); + end; +end; + +function TIdHashMessageDigest2.HashToHex(const AHash: TIdBytes): String; +begin + Result := LongWordHashToHex(AHash, 4); +end; + +function TIdHashMessageDigest2.InitHash: TIdHashIntCtx; +begin + Result := GetMD2HashInst; +end; + +class function TIdHashMessageDigest2.IsIntfAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsMD2HashIntfAvail; +end; + +{ TIdHashMessageDigest4 } + +const + MD4_INIT_VALUES: T4x4LongWordRecord = ( + $67452301, $EFCDAB89, $98BADCFE, $10325476); + +{$Q-} // Arithmetic operations performed modulo $100000000 + +constructor TIdHashMessageDigest4.Create; +begin + inherited Create; + SetLength(FCBuffer, 64); +end; + +procedure TIdHashMessageDigest4.MDCoder; +var + A, B, C, D, i : UInt32; + buff : T16x4LongWordRecord; // 64-byte buffer +begin + A := FState[0]; + B := FState[1]; + C := FState[2]; + D := FState[3]; + + for i := 0 to 15 do + begin + buff[i] := FCBuffer[i*4+0] + + (FCBuffer[i*4+1] shl 8) + + (FCBuffer[i*4+2] shl 16) + + (FCBuffer[i*4+3] shl 24); + end; + + // Round 1 + { Note: + (x and y) or ( (not x) and z) + is equivalent to + (((z xor y) and x) xor z) + -HHellstrm } + for i := 0 to 3 do + begin + A := ROL((((D xor C) and B) xor D) + A + buff[i*4+0], 3); + D := ROL((((C xor B) and A) xor C) + D + buff[i*4+1], 7); + C := ROL((((B xor A) and D) xor B) + C + buff[i*4+2], 11); + B := ROL((((A xor D) and C) xor A) + B + buff[i*4+3], 19); + end; + + // Round 2 + { Note: + (x and y) or (x and z) or (y and z) + is equivalent to + ((x and y) or (z and (x or y))) + -HHellstrm } + for i := 0 to 3 do + begin + A := ROL(((B and C) or (D and (B or C))) + A + buff[0*4+i] + $5A827999, 3); + D := ROL(((A and B) or (C and (A or B))) + D + buff[1*4+i] + $5A827999, 5); + C := ROL(((D and A) or (B and (D or A))) + C + buff[2*4+i] + $5A827999, 9); + B := ROL(((C and D) or (A and (C or D))) + B + buff[3*4+i] + $5A827999, 13); + end; + + // Round 3 + A := ROL((B xor C xor D) + A + buff[ 0] + $6ED9EBA1, 3); + D := ROL((A xor B xor C) + D + buff[ 8] + $6ED9EBA1, 9); + C := ROL((D xor A xor B) + C + buff[ 4] + $6ED9EBA1, 11); + B := ROL((C xor D xor A) + B + buff[12] + $6ED9EBA1, 15); + A := ROL((B xor C xor D) + A + buff[ 2] + $6ED9EBA1, 3); + D := ROL((A xor B xor C) + D + buff[10] + $6ED9EBA1, 9); + C := ROL((D xor A xor B) + C + buff[ 6] + $6ED9EBA1, 11); + B := ROL((C xor D xor A) + B + buff[14] + $6ED9EBA1, 15); + A := ROL((B xor C xor D) + A + buff[ 1] + $6ED9EBA1, 3); + D := ROL((A xor B xor C) + D + buff[ 9] + $6ED9EBA1, 9); + C := ROL((D xor A xor B) + C + buff[ 5] + $6ED9EBA1, 11); + B := ROL((C xor D xor A) + B + buff[13] + $6ED9EBA1, 15); + A := ROL((B xor C xor D) + A + buff[ 3] + $6ED9EBA1, 3); + D := ROL((A xor B xor C) + D + buff[11] + $6ED9EBA1, 9); + C := ROL((D xor A xor B) + C + buff[ 7] + $6ED9EBA1, 11); + B := ROL((C xor D xor A) + B + buff[15] + $6ED9EBA1, 15); + + Inc(FState[0], A); + Inc(FState[1], B); + Inc(FState[2], C); + Inc(FState[3], D); +end; +{$Q+} + +function TIdHashMessageDigest4.NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TidBytes; +var + LStartPos: Integer; + LSize: TIdStreamSize; + LBitSize: Int64; + I, LReadSize: Integer; +begin + Result := nil; + + LSize := ASize; + + // A straight assignment would be by ref on dotNET. + for I := 0 to 3 do begin + FState[I] := MD4_INIT_VALUES[I]; + end; + + while LSize >= 64 do + begin + LReadSize := ReadTIdBytesFromStream(AStream, FCBuffer, 64); + // TODO: handle stream read error + MDCoder; + Dec(LSize, LReadSize); + end; + + // Read the last set of bytes. + LStartPos := ReadTIdBytesFromStream(AStream, FCBuffer, LSize); + // TODO: handle stream read error + + // Append one bit with value 1 + FCBuffer[LStartPos] := $80; + Inc(LStartPos); + + // Must have sufficient space to insert the 64-bit size value + if LStartPos > 56 then + begin + for I := LStartPos to 63 do begin + FCBuffer[I] := 0; + end; + MDCoder; + LStartPos := 0; + end; + + // Pad with zeroes. Leave room for the 64 bit size value. + for I := LStartPos to 55 do begin + FCBuffer[I] := 0; + end; + + // Append the Number of bits processed. + LBitSize := ASize * 8; + for I := 56 to 63 do + begin + FCBuffer[I] := LBitSize and $FF; + LBitSize := LBitSize shr 8; + end; + MDCoder; + + SetLength(Result, SizeOf(UInt32)*4); + for I := 0 to 3 do begin + CopyTIdUInt32(FState[I], Result, SizeOf(UInt32)*I); + end; +end; + +function TIdHashMessageDigest4.InitHash : TIdHashIntCtx; +begin + Result := GetMD4HashInst; +end; + +function TIdHashMessageDigest4.HashToHex(const AHash: TIdBytes): String; +begin + Result := LongWordHashToHex(AHash, 4); +end; + +class function TIdHashMessageDigest4.IsIntfAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsMD4HashIntfAvail ; +end; + +{ TIdHashMessageDigest5 } + +const + MD5_SINE : array[1..64] of UInt32 = ( + { Round 1. } + $d76aa478, $e8c7b756, $242070db, $c1bdceee, $f57c0faf, $4787c62a, + $a8304613, $fd469501, $698098d8, $8b44f7af, $ffff5bb1, $895cd7be, + $6b901122, $fd987193, $a679438e, $49b40821, + { Round 2. } + $f61e2562, $c040b340, $265e5a51, $e9b6c7aa, $d62f105d, $02441453, + $d8a1e681, $e7d3fbc8, $21e1cde6, $c33707d6, $f4d50d87, $455a14ed, + $a9e3e905, $fcefa3f8, $676f02d9, $8d2a4c8a, + { Round 3. } + $fffa3942, $8771f681, $6d9d6122, $fde5380c, $a4beea44, $4bdecfa9, + $f6bb4b60, $bebfbc70, $289b7ec6, $eaa127fa, $d4ef3085, $04881d05, + $d9d4d039, $e6db99e5, $1fa27cf8, $c4ac5665, + { Round 4. } + $f4292244, $432aff97, $ab9423a7, $fc93a039, $655b59c3, $8f0ccc92, + $ffeff47d, $85845dd1, $6fa87e4f, $fe2ce6e0, $a3014314, $4e0811a1, + $f7537e82, $bd3af235, $2ad7d2bb, $eb86d391 + ); + +{$Q-} // Arithmetic operations performed modulo $100000000 + + +function TIdHashMessageDigest5.InitHash: TIdHashIntCtx; +begin + Result := GetMD5HashInst; +end; + +class function TIdHashMessageDigest5.IsIntfAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsMD5HashIntfAvail ; +end; + +procedure TIdHashMessageDigest5.MDCoder; +var + A, B, C, D : UInt32; + i: Integer; + x : T16x4LongWordRecord; // 64-byte buffer +begin + A := FState[0]; + B := FState[1]; + C := FState[2]; + D := FState[3]; + + for i := 0 to 15 do + begin + x[i] := FCBuffer[i*4+0] + + (FCBuffer[i*4+1] shl 8) + + (FCBuffer[i*4+2] shl 16) + + (FCBuffer[i*4+3] shl 24); + end; + { Round 1 } + { Note: + (x and y) or ( (not x) and z) + is equivalent to + (((z xor y) and x) xor z) + -HHellstrm } + A := ROL(A + (((D xor C) and B) xor D) + x[ 0] + MD5_SINE[ 1], 7) + B; + D := ROL(D + (((C xor B) and A) xor C) + x[ 1] + MD5_SINE[ 2], 12) + A; + C := ROL(C + (((B xor A) and D) xor B) + x[ 2] + MD5_SINE[ 3], 17) + D; + B := ROL(B + (((A xor D) and C) xor A) + x[ 3] + MD5_SINE[ 4], 22) + C; + A := ROL(A + (((D xor C) and B) xor D) + x[ 4] + MD5_SINE[ 5], 7) + B; + D := ROL(D + (((C xor B) and A) xor C) + x[ 5] + MD5_SINE[ 6], 12) + A; + C := ROL(C + (((B xor A) and D) xor B) + x[ 6] + MD5_SINE[ 7], 17) + D; + B := ROL(B + (((A xor D) and C) xor A) + x[ 7] + MD5_SINE[ 8], 22) + C; + A := ROL(A + (((D xor C) and B) xor D) + x[ 8] + MD5_SINE[ 9], 7) + B; + D := ROL(D + (((C xor B) and A) xor C) + x[ 9] + MD5_SINE[10], 12) + A; + C := ROL(C + (((B xor A) and D) xor B) + x[10] + MD5_SINE[11], 17) + D; + B := ROL(B + (((A xor D) and C) xor A) + x[11] + MD5_SINE[12], 22) + C; + A := ROL(A + (((D xor C) and B) xor D) + x[12] + MD5_SINE[13], 7) + B; + D := ROL(D + (((C xor B) and A) xor C) + x[13] + MD5_SINE[14], 12) + A; + C := ROL(C + (((B xor A) and D) xor B) + x[14] + MD5_SINE[15], 17) + D; + B := ROL(B + (((A xor D) and C) xor A) + x[15] + MD5_SINE[16], 22) + C; + + { Round 2 } + { Note: + (x and z) or (y and (not z) ) + is equivalent to + (((y xor x) and z) xor y) + -HHellstrm } + A := ROL(A + (C xor (D and (B xor C))) + x[ 1] + MD5_SINE[17], 5) + B; + D := ROL(D + (B xor (C and (A xor B))) + x[ 6] + MD5_SINE[18], 9) + A; + C := ROL(C + (A xor (B and (D xor A))) + x[11] + MD5_SINE[19], 14) + D; + B := ROL(B + (D xor (A and (C xor D))) + x[ 0] + MD5_SINE[20], 20) + C; + A := ROL(A + (C xor (D and (B xor C))) + x[ 5] + MD5_SINE[21], 5) + B; + D := ROL(D + (B xor (C and (A xor B))) + x[10] + MD5_SINE[22], 9) + A; + C := ROL(C + (A xor (B and (D xor A))) + x[15] + MD5_SINE[23], 14) + D; + B := ROL(B + (D xor (A and (C xor D))) + x[ 4] + MD5_SINE[24], 20) + C; + A := ROL(A + (C xor (D and (B xor C))) + x[ 9] + MD5_SINE[25], 5) + B; + D := ROL(D + (B xor (C and (A xor B))) + x[14] + MD5_SINE[26], 9) + A; + C := ROL(C + (A xor (B and (D xor A))) + x[ 3] + MD5_SINE[27], 14) + D; + B := ROL(B + (D xor (A and (C xor D))) + x[ 8] + MD5_SINE[28], 20) + C; + A := ROL(A + (C xor (D and (B xor C))) + x[13] + MD5_SINE[29], 5) + B; + D := ROL(D + (B xor (C and (A xor B))) + x[ 2] + MD5_SINE[30], 9) + A; + C := ROL(C + (A xor (B and (D xor A))) + x[ 7] + MD5_SINE[31], 14) + D; + B := ROL(B + (D xor (A and (C xor D))) + x[12] + MD5_SINE[32], 20) + C; + + { Round 3. } + A := ROL(A + (B xor C xor D) + x[ 5] + MD5_SINE[33], 4) + B; + D := ROL(D + (A xor B xor C) + x[ 8] + MD5_SINE[34], 11) + A; + C := ROL(C + (D xor A xor B) + x[11] + MD5_SINE[35], 16) + D; + B := ROL(B + (C xor D xor A) + x[14] + MD5_SINE[36], 23) + C; + A := ROL(A + (B xor C xor D) + x[ 1] + MD5_SINE[37], 4) + B; + D := ROL(D + (A xor B xor C) + x[ 4] + MD5_SINE[38], 11) + A; + C := ROL(C + (D xor A xor B) + x[ 7] + MD5_SINE[39], 16) + D; + B := ROL(B + (C xor D xor A) + x[10] + MD5_SINE[40], 23) + C; + A := ROL(A + (B xor C xor D) + x[13] + MD5_SINE[41], 4) + B; + D := ROL(D + (A xor B xor C) + x[ 0] + MD5_SINE[42], 11) + A; + C := ROL(C + (D xor A xor B) + x[ 3] + MD5_SINE[43], 16) + D; + B := ROL(B + (C xor D xor A) + x[ 6] + MD5_SINE[44], 23) + C; + A := ROL(A + (B xor C xor D) + x[ 9] + MD5_SINE[45], 4) + B; + D := ROL(D + (A xor B xor C) + x[12] + MD5_SINE[46], 11) + A; + C := ROL(C + (D xor A xor B) + x[15] + MD5_SINE[47], 16) + D; + B := ROL(B + (C xor D xor A) + x[ 2] + MD5_SINE[48], 23) + C; + + { Round 4. } + A := ROL(A + ((B or not D) xor C) + x[ 0] + MD5_SINE[49], 6) + B; + D := ROL(D + ((A or not C) xor B) + x[ 7] + MD5_SINE[50], 10) + A; + C := ROL(C + ((D or not B) xor A) + x[14] + MD5_SINE[51], 15) + D; + B := ROL(B + ((C or not A) xor D) + x[ 5] + MD5_SINE[52], 21) + C; + A := ROL(A + ((B or not D) xor C) + x[12] + MD5_SINE[53], 6) + B; + D := ROL(D + ((A or not C) xor B) + x[ 3] + MD5_SINE[54], 10) + A; + C := ROL(C + ((D or not B) xor A) + x[10] + MD5_SINE[55], 15) + D; + B := ROL(B + ((C or not A) xor D) + x[ 1] + MD5_SINE[56], 21) + C; + A := ROL(A + ((B or not D) xor C) + x[ 8] + MD5_SINE[57], 6) + B; + D := ROL(D + ((A or not C) xor B) + x[15] + MD5_SINE[58], 10) + A; + C := ROL(C + ((D or not B) xor A) + x[ 6] + MD5_SINE[59], 15) + D; + B := ROL(B + ((C or not A) xor D) + x[13] + MD5_SINE[60], 21) + C; + A := ROL(A + ((B or not D) xor C) + x[ 4] + MD5_SINE[61], 6) + B; + D := ROL(D + ((A or not C) xor B) + x[11] + MD5_SINE[62], 10) + A; + C := ROL(C + ((D or not B) xor A) + x[ 2] + MD5_SINE[63], 15) + D; + B := ROL(B + ((C or not A) xor D) + x[ 9] + MD5_SINE[64], 21) + C; + + Inc(FState[0], A); + Inc(FState[1], B); + Inc(FState[2], C); + Inc(FState[3], D); +end; +{$Q+} + +end. diff --git a/indy/Protocols/IdHashSHA.pas b/indy/Protocols/IdHashSHA.pas new file mode 100644 index 0000000..382a465 --- /dev/null +++ b/indy/Protocols/IdHashSHA.pas @@ -0,0 +1,522 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 2003-10-12 15:25:50 HHellstrm + Comments added + + Rev 1.5 2003-10-12 03:08:24 HHellstrm + New implementation; copyright changed. The source code formatting has been + adjusted to fit the margins. The new implementation is faster on dotNet + compared to the old one, but is slightly slower on Win32. + + Rev 1.4 2003-10-11 18:44:54 HHellstrm + Range checking and overflow checking disabled in the Coder method only. The + purpose of this setting is to force the arithmetic operations performed on + UInt32 variables to be modulo $100000000. This hack entails reasonable + performance on both Win32 and dotNet. + + Rev 1.3 10/10/2003 2:20:56 PM GGrieve + turn range checking off + + Rev 1.2 2003-09-21 17:31:02 HHellstrm Version: 1.2 + DotNET compatibility + + Rev 1.1 2/16/2003 03:19:18 PM JPMugaas + Should now compile on D7 better. + + Rev 1.0 11/13/2002 07:53:48 AM JPMugaas +} + +unit IdHashSHA; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdFIPS, + IdGlobal, IdHash; + +{ +Microsoft.NET notes!!!! + +In Microsoft.NET, there are some limitations that you need to be aware of. + +1) In Microsoft.NET 1.1, 2.0, and 3.0, only the CryptoService SHA1 class is +FIPS-complient. Unfortunately, SHA1 will not be permitted after 2010. +2) In Microsoft.NET 3.5,There are more classes ending in CryptoServiceProvider" or +"Cng" that are complient. +3) SHA224 is not exposed. +} +type + T5x4LongWordRecord = array[0..4] of UInt32; + T512BitRecord = array [0..63] of Byte; + {$IFNDEF DOTNET} + TIdHashSHA1 = class(TIdHashNativeAndIntF) + {$ELSE} + TIdHashSHA1 = class(TIdHashIntF) + {$ENDIF} + protected + + {$IFNDEF DOTNET} + FCheckSum: T5x4LongWordRecord; + FCBuffer: TIdBytes; + procedure Coder; + function NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + + {$ENDIF} + function InitHash : TIdHashIntCtx; override; + public + {$IFDEF DOTNET} + class function IsAvailable : Boolean; override; + {$ELSE} + constructor Create; override; + {$ENDIF} + class function IsIntfAvailable: Boolean; override; + end; + {$IFNDEF DOTNET} + TIdHashSHA224 = class(TIdHashIntF) + protected + function InitHash : TIdHashIntCtx; override; + public + class function IsAvailable : Boolean; override; + end; + {$ENDIF} + TIdHashSHA256 = class(TIdHashIntF) + protected + function InitHash : TIdHashIntCtx; override; + public + class function IsAvailable : Boolean; override; + end; + TIdHashSHA384 = class(TIdHashIntF) + protected + function InitHash : TIdHashIntCtx; override; + public + class function IsAvailable : Boolean; override; + end; + TIdHashSHA512 = class(TIdHashIntF) + protected + function InitHash : TIdHashIntCtx; override; + public + class function IsAvailable : Boolean; override; + end; + +implementation +uses + {$IFDEF DOTNET} + IdStreamNET; + {$ELSE} + IdStreamVCL; + {$ENDIF} + +{ TIdHashSHA1 } + +{$IFDEF DOTNET} + +function TIdHashSHA1.GetHashInst : TIdHashInst; +begin +//You can not use SHA256Managed for FIPS complience. + Result := System.Security.Cryptography.SHA1CryptoServiceProvider.Create; +end; + +class function TIdHashSHA1.IsIntfAvailable : Boolean; +begin + Result := True; +end; + +class function TIdHashSHA1.IsAvailable : Boolean; +begin + Result := True; +end; + +{$ELSE} + +function SwapLongWord(const AValue: UInt32): UInt32; +begin + Result := ((AValue and $FF) shl 24) or ((AValue and $FF00) shl 8) or ((AValue and $FF0000) shr 8) or ((AValue and $FF000000) shr 24); +end; + +constructor TIdHashSHA1.Create; +begin + inherited Create; + SetLength(FCBuffer, 64); +end; + +function TIdHashSHA1.InitHash: TIdHashIntCtx; +begin + Result := GetSHA1HashInst; +end; + +class function TIdHashSHA1.IsIntfAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsSHA1HashIntfAvail; +end; + +{$Q-,R-} // Operations performed modulo $100000000 +procedure TIdHashSHA1.Coder; +var + T, A, B, C, D, E: UInt32; + { The size of the W variable has been reduced to make the Coder method + consume less memory on dotNet. This change has been tested with the v1.1 + framework and entails a general increase of performance by >50%. } + W: array [0..19] of UInt32; + i: UInt32; +begin + { The first 16 W values are identical to the input block with endian + conversion. } + for i := 0 to 15 do + begin + W[i]:= (FCBuffer[i*4] shl 24) or + (FCBuffer[i*4+1] shl 16) or + (FCBuffer[i*4+2] shl 8) or + FCBuffer[i*4+3]; + end; + { In normal x86 code all of the remaining 64 W values would be calculated + here. Here only the four next values are calculated, to reduce the code + size of the first of the four loops below. } + for i := 16 to 19 do + begin + T := W[i-3] xor W[i-8] xor W[i-14] xor W[i-16]; + W[i] := (T shl 1) or (T shr 31); + end; + + A := FCheckSum[0]; + B := FCheckSum[1]; + C := FCheckSum[2]; + D := FCheckSum[3]; + E := FCheckSum[4]; + + { The following loop could be expanded, but has been kept together to reduce + the code size. A small code size entails better performance due to CPU + caching. + + Note that the code size could be reduced further by using the SHA-1 + reference code: + + for i := 0 to 19 do begin + T := E + (A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i]; + Inc(T,$5A827999); + E := D; + D := C; + C := (B shl 30) + (B shr 2); + B := A; + A := T; + end; + + The reference code is usually (at least partly) expanded, mostly because + the assignments that circle the state variables A, B, C, D and E are costly, + in particular on dotNET. (In x86 code further optimization can be achieved + by eliminating the loop variable, which occupies a CPU register that is + better used by one of the state variables, plus by expanding the W array + at the beginning.) } + + i := 0; + repeat + Inc(E,(A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i+0]); + Inc(E,$5A827999); + B := (B shl 30) + (B shr 2); + Inc(D,(E shl 5) + (E shr 27) + (C xor (A and (B xor C))) + W[i+1]); + Inc(D,$5A827999); + A := (A shl 30) + (A shr 2); + Inc(C,(D shl 5) + (D shr 27) + (B xor (E and (A xor B))) + W[i+2]); + Inc(C,$5A827999); + E := (E shl 30) + (E shr 2); + Inc(B,(C shl 5) + (C shr 27) + (A xor (D and (E xor A))) + W[i+3]); + Inc(B,$5A827999); + D := (D shl 30) + (D shr 2); + Inc(A,(B shl 5) + (B shr 27) + (E xor (C and (D xor E))) + W[i+4]); + Inc(A,$5A827999); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 20; + + { The following three loops will only use the first 16 elements of the W + array in a circular, recursive pattern. The following assignments are a + trade-off to avoid having to split up the first loop. } + W[0] := W[16]; + W[1] := W[17]; + W[2] := W[18]; + W[3] := W[19]; + + { In the following three loops the recursive W array expansion is performed + "just in time" following a circular pattern. Using circular indicies (e.g. + (i+2) and $F) is not free, but the cost of declaring a large W array would + be higher on dotNET. Before attempting to optimize this code, please note + that the following language features are also costly: + + * Assignments and moves/copies, in particular on dotNET + * Constant lookup tables, in particular on dotNET + * Sub functions, in particular on x86 + * if..then and case..of. } + + i := 20; + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T + $6ED9EBA1); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T + $6ED9EBA1); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T + $6ED9EBA1); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T + $6ED9EBA1); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T + $6ED9EBA1); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 40; + + { Note that the constant $70E44324 = $100000000 - $8F1BBCDC has been selected + to slightly reduce the probability that the CPU flag C (Carry) is set. This + trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1. + It entails a marginal but measurable performance gain on some CPUs. } + + i := 40; + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + ((B and C) or (D and (B or C))) + T); + Dec(E,$70E44324); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + ((A and B) or (C and (A or B))) + T); + Dec(D,$70E44324); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + ((E and A) or (B and (E or A))) + T); + Dec(C,$70E44324); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + ((D and E) or (A and (D or E))) + T); + Dec(B,$70E44324); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + ((C and D) or (E and (C or D))) + T); + Dec(A,$70E44324); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 60; + + { Note that the constant $359D3E2A = $100000000 - $CA62C1D6 has been selected + to slightly reduce the probability that the CPU flag C (Carry) is set. This + trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1. + It entails a marginal but measurable performance gain on some CPUs. } + + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T - $359D3E2A); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T - $359D3E2A); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T - $359D3E2A); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T - $359D3E2A); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T - $359D3E2A); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 80; + + FCheckSum[0]:= FCheckSum[0] + A; + FCheckSum[1]:= FCheckSum[1] + B; + FCheckSum[2]:= FCheckSum[2] + C; + FCheckSum[3]:= FCheckSum[3] + D; + FCheckSum[4]:= FCheckSum[4] + E; +end; + +function TIdHashSHA1.NativeGetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +var + LSize: Integer; + LLenHi: UInt32; + LLenLo: UInt32; + I: Integer; +begin + Result := nil; + + FCheckSum[0] := $67452301; + FCheckSum[1] := $EFCDAB89; + FCheckSum[2] := $98BADCFE; + FCheckSum[3] := $10325476; + FCheckSum[4] := $C3D2E1F0; + + LLenHi := 0; + LLenLo := 0; + + // Code the entire file in complete 64-byte chunks. + while ASize >= 64 do begin + LSize := ReadTIdBytesFromStream(AStream, FCBuffer, 64); + // TODO: handle stream read error + Inc(LLenLo, LSize * 8); + if LLenLo < UInt32(LSize * 8) then begin + Inc(LLenHi); + end; + Coder; + Dec(ASize, LSize); + end; + + // Read the last set of bytes. + LSize := ReadTIdBytesFromStream(AStream, FCBuffer, ASize); + // TODO: handle stream read error + Inc(LLenLo, LSize * 8); + if LLenLo < UInt32(LSize * 8) then begin + Inc(LLenHi); + end; + + FCBuffer[LSize] := $80; + if LSize >= 56 then begin + for I := (LSize + 1) to 63 do begin + FCBuffer[i] := 0; + end; + Coder; + LSize := -1; + end; + + for I := (LSize + 1) to 55 do begin + FCBuffer[i] := 0; + end; + FCBuffer[56] := (LLenHi shr 24); + FCBuffer[57] := (LLenHi shr 16) and $FF; + FCBuffer[58] := (LLenHi shr 8) and $FF; + FCBuffer[59] := (LLenHi and $FF); + FCBuffer[60] := (LLenLo shr 24); + FCBuffer[61] := (LLenLo shr 16) and $FF; + FCBuffer[62] := (LLenLo shr 8) and $FF; + FCBuffer[63] := (LLenLo and $FF); + Coder; + + FCheckSum[0] := SwapLongWord(FCheckSum[0]); + FCheckSum[1] := SwapLongWord(FCheckSum[1]); + FCheckSum[2] := SwapLongWord(FCheckSum[2]); + FCheckSum[3] := SwapLongWord(FCheckSum[3]); + FCheckSum[4] := SwapLongWord(FCheckSum[4]); + + SetLength(Result, SizeOf(UInt32)*5); + for I := 0 to 4 do begin + CopyTIdUInt32(FCheckSum[I], Result, SizeOf(UInt32)*I); + end; +end; + +function TIdHashSHA1.HashToHex(const AHash: TIdBytes): String; +begin + Result := LongWordHashToHex(AHash, 5); +end; +{$ENDIF} + +{$IFNDEF DOTNET} +{ TIdHashSHA224 } + +function TIdHashSHA224.InitHash: TIdHashIntCtx; +begin + Result := GetSHA224HashInst; +end; + +class function TIdHashSHA224.IsAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsSHA224HashIntfAvail; +end; +{$ENDIF} + +{ TIdHashSHA256 } + +function TIdHashSHA256.InitHash: TIdHashIntCtx; +begin + Result := GetSHA256HashInst; +end; + +class function TIdHashSHA256.IsAvailable : Boolean; +begin + Result := IsHashingIntfAvail and IsSHA256HashIntfAvail; +end; + +{ TIdHashSHA384 } + +function TIdHashSHA384.InitHash: TIdHashIntCtx; +begin + Result := GetSHA384HashInst; +end; + +class function TIdHashSHA384.IsAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsSHA384HashIntfAvail; +end; + +{ TIdHashSHA512 } + +function TIdHashSHA512.InitHash: TIdHashIntCtx; +begin + Result := GetSHA512HashInst; +end; + +class function TIdHashSHA512.IsAvailable: Boolean; +begin + Result := IsHashingIntfAvail and IsSHA512HashIntfAvail; +end; + +end. diff --git a/indy/Protocols/IdHashSHA1.pas b/indy/Protocols/IdHashSHA1.pas new file mode 100644 index 0000000..1cf9903 --- /dev/null +++ b/indy/Protocols/IdHashSHA1.pas @@ -0,0 +1,387 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.6 2003-10-12 15:25:50 HHellstrm + Comments added + + Rev 1.5 2003-10-12 03:08:24 HHellstrm + New implementation; copyright changed. The source code formatting has been + adjusted to fit the margins. The new implementation is faster on dotNet + compared to the old one, but is slightly slower on Win32. + + Rev 1.4 2003-10-11 18:44:54 HHellstrm + Range checking and overflow checking disabled in the Coder method only. The + purpose of this setting is to force the arithmetic operations performed on + LongWord variables to be modulo $100000000. This hack entails reasonable + performance on both Win32 and dotNet. + + Rev 1.3 10/10/2003 2:20:56 PM GGrieve + turn range checking off + + Rev 1.2 2003-09-21 17:31:02 HHellstrm Version: 1.2 + DotNET compatibility + + Rev 1.1 2/16/2003 03:19:18 PM JPMugaas + Should now compile on D7 better. + + Rev 1.0 11/13/2002 07:53:48 AM JPMugaas +} + +unit IdHashSHA1; + +interface +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, IdHash; + +type + T5x4LongWordRecord = array[0..4] of UInt32; + T512BitRecord = array [0..63] of Byte; + + TIdHashSHA1 = class(TIdHash) + protected + FCheckSum: T5x4LongWordRecord; + FCBuffer: TIdBytes; + procedure Coder; + function GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; override; + function HashToHex(const AHash: TIdBytes): String; override; + public + constructor Create; override; + end; + +implementation + {$IFNDEF DOTNET} +uses + IdStreamVCL; + {$ENDIF} +{ TIdHashSHA1 } + +function SwapLongWord(const AValue: UInt32): UInt32; +begin + Result := ((AValue and $FF) shl 24) or ((AValue and $FF00) shl 8) or ((AValue and $FF0000) shr 8) or ((AValue and $FF000000) shr 24); +end; + +constructor TIdHashSHA1.Create; +begin + inherited Create; + SetLength(FCBuffer, 64); +end; + +{$Q-,R-} // Operations performed modulo $100000000 +procedure TIdHashSHA1.Coder; +var + T, A, B, C, D, E: UInt32; + { The size of the W variable has been reduced to make the Coder method + consume less memory on dotNet. This change has been tested with the v1.1 + framework and entails a general increase of performance by >50%. } + W: array [0..19] of UInt32; + i: UInt32; +begin + { The first 16 W values are identical to the input block with endian + conversion. } + for i := 0 to 15 do + begin + W[i]:= (FCBuffer[i*4] shl 24) or + (FCBuffer[i*4+1] shl 16) or + (FCBuffer[i*4+2] shl 8) or + FCBuffer[i*4+3]; + end; + { In normal x86 code all of the remaining 64 W values would be calculated + here. Here only the four next values are calculated, to reduce the code + size of the first of the four loops below. } + for i := 16 to 19 do + begin + T := W[i-3] xor W[i-8] xor W[i-14] xor W[i-16]; + W[i] := (T shl 1) or (T shr 31); + end; + + A := FCheckSum[0]; + B := FCheckSum[1]; + C := FCheckSum[2]; + D := FCheckSum[3]; + E := FCheckSum[4]; + + { The following loop could be expanded, but has been kept together to reduce + the code size. A small code size entails better performance due to CPU + caching. + + Note that the code size could be reduced further by using the SHA-1 + reference code: + + for i := 0 to 19 do begin + T := E + (A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i]; + Inc(T,$5A827999); + E := D; + D := C; + C := (B shl 30) + (B shr 2); + B := A; + A := T; + end; + + The reference code is usually (at least partly) expanded, mostly because + the assignments that circle the state variables A, B, C, D and E are costly, + in particular on dotNET. (In x86 code further optimization can be achieved + by eliminating the loop variable, which occupies a CPU register that is + better used by one of the state variables, plus by expanding the W array + at the beginning.) } + + i := 0; + repeat + Inc(E,(A shl 5) + (A shr 27) + (D xor (B and (C xor D))) + W[i+0]); + Inc(E,$5A827999); + B := (B shl 30) + (B shr 2); + Inc(D,(E shl 5) + (E shr 27) + (C xor (A and (B xor C))) + W[i+1]); + Inc(D,$5A827999); + A := (A shl 30) + (A shr 2); + Inc(C,(D shl 5) + (D shr 27) + (B xor (E and (A xor B))) + W[i+2]); + Inc(C,$5A827999); + E := (E shl 30) + (E shr 2); + Inc(B,(C shl 5) + (C shr 27) + (A xor (D and (E xor A))) + W[i+3]); + Inc(B,$5A827999); + D := (D shl 30) + (D shr 2); + Inc(A,(B shl 5) + (B shr 27) + (E xor (C and (D xor E))) + W[i+4]); + Inc(A,$5A827999); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 20; + + { The following three loops will only use the first 16 elements of the W + array in a circular, recursive pattern. The following assignments are a + trade-off to avoid having to split up the first loop. } + W[0] := W[16]; + W[1] := W[17]; + W[2] := W[18]; + W[3] := W[19]; + + { In the following three loops the recursive W array expansion is performed + "just in time" following a circular pattern. Using circular indicies (e.g. + (i+2) and $F) is not free, but the cost of declaring a large W array would + be higher on dotNET. Before attempting to optimize this code, please note + that the following language features are also costly: + + * Assignments and moves/copies, in particular on dotNET + * Constant lookup tables, in particular on dotNET + * Sub functions, in particular on x86 + * if..then and case..of. } + + i := 20; + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T + $6ED9EBA1); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T + $6ED9EBA1); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T + $6ED9EBA1); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T + $6ED9EBA1); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T + $6ED9EBA1); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 40; + + { Note that the constant $70E44324 = $100000000 - $8F1BBCDC has been selected + to slightly reduce the probability that the CPU flag C (Carry) is set. This + trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1. + It entails a marginal but measurable performance gain on some CPUs. } + + i := 40; + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + ((B and C) or (D and (B or C))) + T); + Dec(E,$70E44324); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + ((A and B) or (C and (A or B))) + T); + Dec(D,$70E44324); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + ((E and A) or (B and (E or A))) + T); + Dec(C,$70E44324); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + ((D and E) or (A and (D or E))) + T); + Dec(B,$70E44324); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + ((C and D) or (E and (C or D))) + T); + Dec(A,$70E44324); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 60; + + { Note that the constant $359D3E2A = $100000000 - $CA62C1D6 has been selected + to slightly reduce the probability that the CPU flag C (Carry) is set. This + trick is taken from the StreamSec(R) StrSecII(TM) implementation of SHA-1. + It entails a marginal but measurable performance gain on some CPUs. } + + repeat + T := W[(i+13) and $F] xor W[(i+8) and $F]; + T := T xor W[(i+2) and $F] xor W[i and $F]; + T := (T shl 1) or (T shr 31); + W[i and $F] := T; + Inc(E,(A shl 5) + (A shr 27) + (B xor C xor D) + T - $359D3E2A); + B := (B shl 30) + (B shr 2); + T := W[(i+14) and $F] xor W[(i+9) and $F]; + T := T xor W[(i+3) and $F] xor W[(i+1) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+1) and $F] := T; + Inc(D,(E shl 5) + (E shr 27) + (A xor B xor C) + T - $359D3E2A); + A := (A shl 30) + (A shr 2); + T := W[(i+15) and $F] xor W[(i+10) and $F]; + T := T xor W[(i+4) and $F] xor W[(i+2) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+2) and $F] := T; + Inc(C,(D shl 5) + (D shr 27) + (E xor A xor B) + T - $359D3E2A); + E := (E shl 30) + (E shr 2); + T := W[i and $F] xor W[(i+11) and $F]; + T := T xor W[(i+5) and $F] xor W[(i+3) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+3) and $F] := T; + Inc(B,(C shl 5) + (C shr 27) + (D xor E xor A) + T - $359D3E2A); + D := (D shl 30) + (D shr 2); + T := W[(i+1) and $F] xor W[(i+12) and $F]; + T := T xor W[(i+6) and $F] xor W[(i+4) and $F]; + T := (T shl 1) or (T shr 31); + W[(i+4) and $F] := T; + Inc(A,(B shl 5) + (B shr 27) + (C xor D xor E) + T - $359D3E2A); + C := (C shl 30) + (C shr 2); + Inc(i,5); + until i = 80; + + FCheckSum[0]:= FCheckSum[0] + A; + FCheckSum[1]:= FCheckSum[1] + B; + FCheckSum[2]:= FCheckSum[2] + C; + FCheckSum[3]:= FCheckSum[3] + D; + FCheckSum[4]:= FCheckSum[4] + E; +end; + +function TIdHashSHA1.GetHashBytes(AStream: TStream; ASize: TIdStreamSize): TIdBytes; +var + LSize: Integer; + LLenHi: UInt32; + LLenLo: UInt32; + I: Integer; +begin + Result := nil; + + FCheckSum[0] := $67452301; + FCheckSum[1] := $EFCDAB89; + FCheckSum[2] := $98BADCFE; + FCheckSum[3] := $10325476; + FCheckSum[4] := $C3D2E1F0; + + LLenHi := 0; + LLenLo := 0; + + // Code the entire file in complete 64-byte chunks. + while ASize >= 64 do begin + LSize := ReadTIdBytesFromStream(AStream, FCBuffer, 64); + // TODO: handle stream read error + Inc(LLenLo, LSize * 8); + if LLenLo < UInt32(LSize * 8) then begin + Inc(LLenHi); + end; + Coder; + Dec(ASize, LSize); + end; + + // Read the last set of bytes. + LSize := ReadTIdBytesFromStream(AStream, FCBuffer, ASize); + // TODO: handle stream read error + Inc(LLenLo, LSize * 8); + if LLenLo < UInt32(LSize * 8) then begin + Inc(LLenHi); + end; + + FCBuffer[LSize] := $80; + if LSize >= 56 then begin + for I := (LSize + 1) to 63 do begin + FCBuffer[i] := 0; + end; + Coder; + LSize := -1; + end; + + for I := (LSize + 1) to 55 do begin + FCBuffer[i] := 0; + end; + FCBuffer[56] := (LLenHi shr 24); + FCBuffer[57] := (LLenHi shr 16) and $FF; + FCBuffer[58] := (LLenHi shr 8) and $FF; + FCBuffer[59] := (LLenHi and $FF); + FCBuffer[60] := (LLenLo shr 24); + FCBuffer[61] := (LLenLo shr 16) and $FF; + FCBuffer[62] := (LLenLo shr 8) and $FF; + FCBuffer[63] := (LLenLo and $FF); + Coder; + + FCheckSum[0] := SwapLongWord(FCheckSum[0]); + FCheckSum[1] := SwapLongWord(FCheckSum[1]); + FCheckSum[2] := SwapLongWord(FCheckSum[2]); + FCheckSum[3] := SwapLongWord(FCheckSum[3]); + FCheckSum[4] := SwapLongWord(FCheckSum[4]); + + SetLength(Result, SizeOf(UInt32)*5); + for I := 0 to 4 do begin + CopyTIdUInt32(FCheckSum[I], Result, SizeOf(UInt32)*I); + end; +end; + +function TIdHashSHA1.HashToHex(const AHash: TIdBytes): String; +begin + Result := LongWordHashToHex(AHash, 5); +end; + +end. diff --git a/indy/Protocols/IdHeaderCoder2022JP.pas b/indy/Protocols/IdHeaderCoder2022JP.pas new file mode 100644 index 0000000..ef6a1c6 --- /dev/null +++ b/indy/Protocols/IdHeaderCoder2022JP.pas @@ -0,0 +1,277 @@ +unit IdHeaderCoder2022JP; + +interface + +{$i IdCompilerDefines.inc} + +{RLebeau: TODO - move this logic into an IIdTextEncoding implementation} + +uses + IdGlobal, IdHeaderCoderBase; + +type + TIdHeaderCoder2022JP = class(TIdHeaderCoder) + public + class function Decode(const ACharSet: string; const AData: TIdBytes): String; override; + class function Encode(const ACharSet, AData: String): TIdBytes; override; + class function CanHandle(const ACharSet: String): Boolean; override; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterHeaderCoder can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdHeaderCoder2022JP"'} + {$ENDIF} + +implementation + +uses + SysUtils; + +const + // RLebeau 1/7/09: using integers 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... + + kana_tbl : array[161..223{#$A1..#$DF}] of Word = ( + $2123,$2156,$2157,$2122,$2126,$2572,$2521,$2523,$2525,$2527, + $2529,$2563,$2565,$2567,$2543,$213C,$2522,$2524,$2526,$2528, + $252A,$252B,$252D,$252F,$2531,$2533,$2535,$2537,$2539,$253B, + $253D,$253F,$2541,$2544,$2546,$2548,$254A,$254B,$254C,$254D, + $254E,$254F,$2552,$2555,$2558,$255B,$255E,$255F,$2560,$2561, + $2562,$2564,$2566,$2568,$2569,$256A,$256B,$256C,$256D,$256F, + $2573,$212B,$212C); + + vkana_tbl : array[161..223{#$A1..#$DF}] of Word = ( + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$2574,$0000, + $0000,$252C,$252E,$2530,$2532,$2534,$2536,$2538,$253A,$253C, + $253E,$2540,$2542,$2545,$2547,$2549,$0000,$0000,$0000,$0000, + $0000,$2550,$2553,$2556,$2559,$255C,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000); + + sj1_tbl : array[128..255{#128..#255}] of byte = ( + $00,$21,$23,$25,$27,$29,$2B,$2D,$2F,$31,$33,$35,$37,$39,$3B,$3D, + $3F,$41,$43,$45,$47,$49,$4B,$4D,$4F,$51,$53,$55,$57,$59,$5B,$5D, + $00,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01, + $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01, + $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01, + $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01, + $5F,$61,$63,$65,$67,$69,$6B,$6D,$6F,$71,$73,$75,$77,$79,$7B,$7D, + $02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$00,$00,$00); + + sj2_tbl : array[0..255{#0..#255}] of Word = ( + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000, + $0000,$0000,$0000,$0000,$0021,$0022,$0023,$0024,$0025,$0026, + $0027,$0028,$0029,$002A,$002B,$002C,$002D,$002E,$002F,$0030, + $0031,$0032,$0033,$0034,$0035,$0036,$0037,$0038,$0039,$003A, + $003B,$003C,$003D,$003E,$003F,$0040,$0041,$0042,$0043,$0044, + $0045,$0046,$0047,$0048,$0049,$004A,$004B,$004C,$004D,$004E, + $004F,$0050,$0051,$0052,$0053,$0054,$0055,$0056,$0057,$0058, + $0059,$005A,$005B,$005C,$005D,$005E,$005F,$0000,$0060,$0061, + $0062,$0063,$0064,$0065,$0066,$0067,$0068,$0069,$006A,$006B, + $006C,$006D,$006E,$006F,$0070,$0071,$0072,$0073,$0074,$0075, + $0076,$0077,$0078,$0079,$007A,$007B,$007C,$007D,$007E,$0121, + $0122,$0123,$0124,$0125,$0126,$0127,$0128,$0129,$012A,$012B, + $012C,$012D,$012E,$012F,$0130,$0131,$0132,$0133,$0134,$0135, + $0136,$0137,$0138,$0139,$013A,$013B,$013C,$013D,$013E,$013F, + $0140,$0141,$0142,$0143,$0144,$0145,$0146,$0147,$0148,$0149, + $014A,$014B,$014C,$014D,$014E,$014F,$0150,$0151,$0152,$0153, + $0154,$0155,$0156,$0157,$0158,$0159,$015A,$015B,$015C,$015D, + $015E,$015F,$0160,$0161,$0162,$0163,$0164,$0165,$0166,$0167, + $0168,$0169,$016A,$016B,$016C,$016D,$016E,$016F,$0170,$0171, + $0172,$0173,$0174,$0175,$0176,$0177,$0178,$0179,$017A,$017B, + $017C,$017D,$017E,$0000,$0000,$0000); + +class function TIdHeaderCoder2022JP.Decode(const ACharSet: String; const AData: TIdBytes): String; +var + T : string; + I, L : Integer; + isK : Boolean; + K1, K2 : Byte; + K3 : Byte; +begin + T := ''; {Do not Localize} + isK := False; + L := Length(AData); + I := 0; + while I < L do + begin + if AData[I] = 27 then + begin + Inc(I); + if (I+1) < L then + begin + if (AData[I] = Ord('$')) and (AData[I+1] = Ord('B')) then begin {do not localize} + isK := True; + end + else if (AData[I] = Ord('(')) and (AData[I+1] = Ord('B')) then begin {do not localize} + isK := False; + end; + Inc(I, 2); { TODO -oTArisawa : Check RFC 1468} + end; + end + else if isK then + begin + if (I+1) < L then + begin + K1 := AData[I]; + K2 := AData[I+1]; + + K3 := (K1 - 1) shr 1; + if K1 < 95 then begin + K3:= K3 + 113; + end else begin + K3 := K3 + 177; + end; + + if (K1 mod 2) = 1 then + begin + if K2 < 96 then begin + K2 := K2 + 31; + end else begin + K2 := K2 + 32; + end; + end + else begin + K2 := K2 + 126; + end; + + T := T + Char(K3) + Char(k2); + Inc(I, 2); + end + else begin + Inc(I); { invalid DBCS } + end; + end + else + begin + T := T + Char(AData[I]); + Inc(I); + end; + end; + Result := T; +end; + +class function TIdHeaderCoder2022JP.Encode(const ACharSet, AData: String): TIdBytes; +const + desig_asc: array[0..2] of Byte = (27, Ord('('), Ord('B')); {Do not Localize} + desig_jis: array[0..2] of Byte = (27, Ord('$'), Ord('B')); {Do not Localize} +var + T: TIdBytes; + I, L: Integer; + isK: Boolean; + K1: Byte; + K2, K3: Word; +begin + SetLength(T, 0); + isK := False; + L := Length(AData); + I := 1; + while I <= L do + begin + if Ord(AData[I]) < 128 then {Do not Localize} + begin + if isK then + begin + AppendByte(T, 27); + AppendByte(T, Ord('(')); {Do not Localize} + AppendByte(T, Ord('B')); {Do not Localize} + isK := False; + end; + AppendByte(T, Ord(AData[I])); + Inc(I); + end else + begin + K1 := sj1_tbl[Ord(AData[I])]; + case K1 of + 0: Inc(I); { invalid SBCS } + 2: Inc(I, 2); { invalid DBCS } + 1: + begin { halfwidth katakana } + if not isK then begin + AppendByte(T, 27); + AppendByte(T, Ord('$')); {Do not Localize} + AppendByte(T, Ord('B')); {Do not Localize} + isK := True; + end; + { simple SBCS -> DBCS conversion } + K2 := kana_tbl[Ord(AData[I])]; + if (I < L) and ((Ord(AData[I+1]) and $FE) = $DE) then + begin { convert kana + voiced mark to voiced kana } + K3 := vkana_tbl[Ord(AData[I])]; + // This is an if and not a case because of a D8 bug, return to + // case when d8 patch is released + + // RLebeau 1/7/09: using Char() for #128-#255 because in D2009, the compiler + // may change characters >= #128 from their Ansi codepage value to their true + // Unicode codepoint value, depending on the codepage used for the source code. + // For instance, #128 may become #$20AC... + + if AData[I+1] = Char($DE) then begin { voiced } + if K3 <> 0 then + begin + K2 := K3; + Inc(I); + end; + end + else if AData[I+1] = Char($DF) then begin { semivoiced } + if (K3 >= $2550) and (K3 <= $255C) then + begin + K2 := K3 + 1; + Inc(I); + end; + end; + end; + AppendByte(T, K2 shr 8); + AppendByte(T, K2 and $FF); + Inc(I); + end; + else { DBCS } + if (I < L) then begin + K2 := sj2_tbl[Ord(AData[I+1])]; + if K2 <> 0 then + begin + if not isK then begin + AppendByte(T, 27); + AppendByte(T, Ord('$')); {Do not Localize} + AppendByte(T, Ord('B')); {Do not Localize} + isK := True; + end; + AppendByte(T, K1 + K2 shr 8); + AppendByte(T, K2 and $FF); + end; + end; + Inc(I, 2); + end; + end; + end; + if isK then begin + AppendByte(T, 27); + AppendByte(T, Ord('(')); {Do not Localize} + AppendByte(T, Ord('B')); {Do not Localize} + end; + Result := T; +end; + +class function TIdHeaderCoder2022JP.CanHandle(const ACharSet: String): Boolean; +begin + Result := TextIsSame(ACharSet, 'ISO-2022-JP'); {do not localize} +end; + +initialization + RegisterHeaderCoder(TIdHeaderCoder2022JP); +finalization + UnregisterHeaderCoder(TIdHeaderCoder2022JP); + +end. diff --git a/indy/Protocols/IdHeaderCoderBase.pas b/indy/Protocols/IdHeaderCoderBase.pas new file mode 100644 index 0000000..12dbf3c --- /dev/null +++ b/indy/Protocols/IdHeaderCoderBase.pas @@ -0,0 +1,171 @@ +unit IdHeaderCoderBase; + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, IdGlobal, IdException; + +type + TIdHeaderDecodingNeededEvent = procedure(const ACharSet: String; const AData: TIdBytes; var VResult: String; var VHandled: Boolean) of object; + TIdHeaderEncodingNeededEvent = procedure(const ACharSet, AData: String; var VResult: TIdBytes; var VHandled: Boolean) of object; + + TIdHeaderCoder = class(TObject) + public + class function Decode(const ACharSet: String; const AData: TIdBytes): String; virtual; + class function Encode(const ACharSet, AData: String): TIdBytes; virtual; + class function CanHandle(const ACharSet: String): Boolean; virtual; + end; + + TIdHeaderCoderClass = class of TIdHeaderCoder; + + EIdHeaderEncodeError = class(EIdException); + +var + GHeaderEncodingNeeded: TIdHeaderEncodingNeededEvent = nil; + GHeaderDecodingNeeded: TIdHeaderDecodingNeededEvent = nil; + +function HeaderCoderByCharSet(const ACharSet: String): TIdHeaderCoderClass; +function DecodeHeaderData(const ACharSet: String; const AData: TIdBytes; var VResult: String): Boolean; +function EncodeHeaderData(const ACharSet, AData: String): TIdBytes; +procedure RegisterHeaderCoder(const ACoder: TIdHeaderCoderClass); +procedure UnregisterHeaderCoder(const ACoder: TIdHeaderCoderClass); + +implementation + +uses + {$IFDEF VCL_XE3_OR_ABOVE} + System.Types, + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + SysUtils, IdResourceStringsProtocols; + +type + TIdHeaderCoderList = class(TList{$IFDEF HAS_GENERICS_TList}{$ENDIF}) + public + function ByCharSet(const ACharSet: String): TIdHeaderCoderClass; + end; + +var + GHeaderCoderList: TIdHeaderCoderList = nil; + +{ TIdHeaderCoder } + +class function TIdHeaderCoder.Decode(const ACharSet: String; const AData: TIdBytes): String; +begin + Result := ''; +end; + +class function TIdHeaderCoder.Encode(const ACharSet, AData: String): TIdBytes; +begin + Result := nil; +end; + +class function TIdHeaderCoder.CanHandle(const ACharSet: String): Boolean; +begin + Result := False; +end; + +{ TIdHeaderCoderList } + +function TIdHeaderCoderList.ByCharSet(const ACharSet: string): TIdHeaderCoderClass; +var + I: Integer; + LCoder: TIdHeaderCoderClass; +begin + Result := nil; + // loop backwards so that user-defined coders can override native coders + for I := Count-1 downto 0 do begin + LCoder := TIdHeaderCoderClass(Items[I]); + if LCoder.CanHandle(ACharSet) then begin + Result := LCoder; + Exit; + end; + end; +end; + +function HeaderCoderByCharSet(const ACharSet: String): TIdHeaderCoderClass; +begin + if Assigned(GHeaderCoderList) then begin + Result := GHeaderCoderList.ByCharSet(ACharSet); + end else begin + Result := nil; + end; +end; + +function DecodeHeaderData(const ACharSet: String; const AData: TIdBytes; var VResult: String): Boolean; +var + LCoder: TIdHeaderCoderClass; +begin + LCoder := HeaderCoderByCharSet(ACharSet); + if LCoder <> nil then begin + VResult := LCoder.Decode(ACharSet, AData); + Result := True; + end else + begin + VResult := ''; + Result := False; + if Assigned(GHeaderDecodingNeeded) then begin + GHeaderDecodingNeeded(ACharSet, AData, VResult, Result); + end; + { RLebeau: TODO - enable this? + if not LDecoded then begin + raise EIdHeaderDecodeError.Create(RSHeaderDecodeError, [ACharSet]); + end; + } + end; +end; + +function EncodeHeaderData(const ACharSet, AData: String): TIdBytes; +var + LCoder: TIdHeaderCoderClass; + LEncoded: Boolean; +begin + LCoder := HeaderCoderByCharSet(ACharSet); + if LCoder <> nil then begin + Result := LCoder.Encode(ACharSet, AData); + end else + begin + Result := nil; + LEncoded := False; + if Assigned(GHeaderEncodingNeeded) then begin + GHeaderEncodingNeeded(ACharSet, AData, Result, LEncoded); + end; + if not LEncoded then begin + raise EIdHeaderEncodeError.CreateFmt(RSHeaderEncodeError, [ACharSet]); + end; + end; +end; + +procedure RegisterHeaderCoder(const ACoder: TIdHeaderCoderClass); +begin + if Assigned(ACoder) and + Assigned(GHeaderCoderList) and + (GHeaderCoderList.IndexOf( + {$IFDEF HAS_GENERICS_TList}ACoder{$ELSE}TObject(ACoder){$ENDIF} + ) = -1) then + begin + GHeaderCoderList.Add( + {$IFDEF HAS_GENERICS_TList}ACoder{$ELSE}TObject(ACoder){$ENDIF} + ); + end; +end; + +procedure UnregisterHeaderCoder(const ACoder: TIdHeaderCoderClass); +begin + if Assigned(GHeaderCoderList) then begin + GHeaderCoderList.Remove( + {$IFDEF HAS_GENERICS_TList}ACoder{$ELSE}TObject(ACoder){$ENDIF} + ); + end; +end; + +initialization + GHeaderCoderList := TIdHeaderCoderList.Create; +finalization + FreeAndNil(GHeaderCoderList); + +end. diff --git a/indy/Protocols/IdHeaderCoderBig5.pas b/indy/Protocols/IdHeaderCoderBig5.pas new file mode 100644 index 0000000..a17baf5 --- /dev/null +++ b/indy/Protocols/IdHeaderCoderBig5.pas @@ -0,0 +1,54 @@ +unit IdHeaderCoderBig5; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, IdHeaderCoderBase; + +type + TIdHeaderCoderBig5 = class(TIdHeaderCoder) + public + class function Decode(const ACharSet: string; const AData: TIdBytes): String; override; + class function Encode(const ACharSet, AData: String): TIdBytes; override; + class function CanHandle(const ACharSet: String): Boolean; override; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterHeaderCoder can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdHeaderCoderBig5"'} + {$ENDIF} + +implementation + +uses + SysUtils, IdException; + +class function TIdHeaderCoderBig5.Decode(const ACharSet: string; const AData: TIdBytes): String; +begin + Result := ''; + ToDo('Decode() method of TIdHeaderCoderBig5 class is not implemented yet'); {do not localize} +end; + +class function TIdHeaderCoderBig5.Encode(const ACharSet, AData: String): TIdBytes; +begin + Result := nil; + ToDo('Encode() method of TIdHeaderCoderBig5 class is not implemented yet'); {do not localize} +end; + +class function TIdHeaderCoderBig5.CanHandle(const ACharSet: String): Boolean; +begin + Result := TextIsSame(ACharSet, 'Big5'); +end; + +initialization + RegisterHeaderCoder(TIdHeaderCoderBig5); +finalization + UnregisterHeaderCoder(TIdHeaderCoderBig5); + +end. diff --git a/indy/Protocols/IdHeaderCoderIndy.pas b/indy/Protocols/IdHeaderCoderIndy.pas new file mode 100644 index 0000000..cb47092 --- /dev/null +++ b/indy/Protocols/IdHeaderCoderIndy.pas @@ -0,0 +1,64 @@ +unit IdHeaderCoderIndy; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, IdHeaderCoderBase; + +type + TIdHeaderCoderIndy = class(TIdHeaderCoder) + public + class function Decode(const ACharSet: string; const AData: TIdBytes): String; override; + class function Encode(const ACharSet, AData: String): TIdBytes; override; + class function CanHandle(const ACharSet: String): Boolean; override; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterHeaderCoder can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdHeaderCoderIndy"'} + {$ENDIF} + +implementation + +uses + IdGlobalProtocols; + +class function TIdHeaderCoderIndy.Decode(const ACharSet: string; const AData: TIdBytes): String; +begin + try + Result := CharsetToEncoding(ACharSet).GetString(AData); + except + Result := ''; + end; +end; + +class function TIdHeaderCoderIndy.Encode(const ACharSet, AData: String): TIdBytes; +begin + try + Result := CharsetToEncoding(ACharSet).GetBytes(AData); + except + Result := nil; + end; +end; + +class function TIdHeaderCoderIndy.CanHandle(const ACharSet: String): Boolean; +begin + try + Result := CharsetToEncoding(ACharSet) <> nil; + except + Result := False; + end; +end; + +initialization + RegisterHeaderCoder(TIdHeaderCoderIndy); +finalization + UnregisterHeaderCoder(TIdHeaderCoderIndy); + +end. diff --git a/indy/Protocols/IdHeaderCoderPlain.pas b/indy/Protocols/IdHeaderCoderPlain.pas new file mode 100644 index 0000000..05dec7f --- /dev/null +++ b/indy/Protocols/IdHeaderCoderPlain.pas @@ -0,0 +1,69 @@ +unit IdHeaderCoderPlain; + +interface + +{$i IdCompilerDefines.inc} + +uses + IdGlobal, IdHeaderCoderBase; + +type + TIdHeaderCoderPlain = class(TIdHeaderCoder) + public + class function Decode(const ACharSet: string; const AData: TIdBytes): String; override; + class function Encode(const ACharSet, AData: String): TIdBytes; override; + class function CanHandle(const ACharSet: String): Boolean; override; + end; + + // RLebeau 4/17/10: this forces C++Builder to link to this unit so + // RegisterHeaderCoder can be called correctly at program startup... + + {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} + {$HPPEMIT LINKUNIT} + {$ELSE} + {$HPPEMIT '#pragma link "IdHeaderCoderPlain"'} + {$ENDIF} + +implementation + +uses + SysUtils; + +class function TIdHeaderCoderPlain.Decode(const ACharSet: string; const AData: TIdBytes): String; +begin + Result := BytesToStringRaw(AData); +end; + +class function TIdHeaderCoderPlain.Encode(const ACharSet, AData: String): TIdBytes; +begin + Result := ToBytes(AData, IndyTextEncoding_8Bit{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); +end; + +class function TIdHeaderCoderPlain.CanHandle(const ACharSet: String): Boolean; +begin + Result := TextStartsWith(ACharSet, 'ISO'); {do not localize} + if Result then begin + // 'ISO-2022-JP' is handled by TIdHeaderCoder2022JP + Result := not TextIsSame(ACharSet, 'ISO-2022-JP'); {do not localize} + Exit; + end; + if not Result then begin + Result := TextStartsWith(ACharSet, 'WINDOWS'); {do not localize} + if not Result then begin + Result := TextStartsWith(ACharSet, 'KOI8'); {do not localize} + if not Result then begin + Result := TextStartsWith(ACharSet, 'GB2312'); {do not localize} + if not Result then begin + Result := TextIsSame(ACharSet, 'US-ASCII'); + end; + end; + end; + end; +end; + +initialization + RegisterHeaderCoder(TIdHeaderCoderPlain); +finalization + UnregisterHeaderCoder(TIdHeaderCoderPlain); + +end. diff --git a/indy/Protocols/IdHeaderList.pas b/indy/Protocols/IdHeaderList.pas new file mode 100644 index 0000000..7d43e84 --- /dev/null +++ b/indy/Protocols/IdHeaderList.pas @@ -0,0 +1,507 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.9 10/26/2004 10:10:58 PM JPMugaas + Updated refs. + + Rev 1.8 3/6/2004 2:53:30 PM JPMugaas + Cleaned up an if as per Bug #79. + + Rev 1.7 2004.02.03 5:43:42 PM czhower + Name changes + + Rev 1.6 2004.01.27 1:39:26 AM czhower + CharIsInSet bug fix + + Rev 1.5 1/22/2004 3:50:04 PM SPerry + fixed set problems (with CharIsInSet) + + Rev 1.4 1/22/2004 7:10:06 AM JPMugaas + Tried to fix AnsiSameText depreciation. + + Rev 1.3 10/5/2003 11:43:50 PM GGrieve + Use IsLeadChar + + Rev 1.2 10/4/2003 9:15:14 PM GGrieve + DotNet changes + + Rev 1.1 2/25/2003 12:56:20 PM JPMugaas + Updated with Hadi's fix for a bug . If complete boolean expression i on, you + may get an Index out of range error. + + Rev 1.0 11/13/2002 07:53:52 AM JPMugaas + + 2002-Jan-27 Don Siders + - Modified FoldLine to include Comma in break character set. + + 2000-May-31 J. Peter Mugaas + - started this class to facilitate some work on Indy so we don't have to + convert '=' to ":" and vice-versa just to use the Values property. + } + +unit IdHeaderList; + +{ + NOTE: This is a modification of Borland's TStrings definition in a + TStringList descendant. I had to conceal the original Values to do + this since most of low level property setting routines aren't virtual + and are private. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, IdGlobalProtocols; + +type + TIdHeaderList = class(TStringList) + protected + FNameValueSeparator : String; + FUnfoldLines : Boolean; + FFoldLines : Boolean; + FFoldLinesLength : Integer; + FQuoteType: TIdHeaderQuotingType; + // + procedure AssignTo(Dest: TPersistent); override; + {This deletes lines which were folded} + Procedure DeleteFoldedLines(Index : Integer); + {This folds one line into several lines} + function FoldLine(AString : string): TStrings; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use FoldLineToList()'{$ENDIF};{$ENDIF} + procedure FoldLineToList(AString : string; ALines: TStrings); + {Folds lines and inserts them into a position, Index} + procedure FoldAndInsert(AString : String; Index : Integer); + {Name property get method} + function GetName(Index: Integer): string; + {Value property get method} + function GetValue(const AName: string): string; + {Value property get method} + function GetParam(const AName, AParam: string): string; + function GetAllParams(const AName: string): string; + {Value property set method} + procedure SetValue(const AName, AValue: string); + {Value property set method} + procedure SetParam(const AName, AParam, AValue: string); + procedure SetAllParams(const AName, AValue: string); + {Gets a value from a string} + function GetValueFromLine(var VLine : Integer) : String; + procedure SkipValueAtLine(var VLine : Integer); + public + procedure AddStrings(Strings: TStrings); override; + { This method extracts "name=value" strings from the ASrc TStrings and adds + them to this list using our delimiter defined in NameValueSeparator. } + procedure AddStdValues(ASrc: TStrings); + { This method adds a single name/value pair to this list using our delimiter + defined in NameValueSeparator. } + procedure AddValue(const AName, AValue: string); // allows duplicates + { This method extracts all of the values from this list and puts them in the + ADest TStrings as "name=value" strings.} + procedure ConvertToStdValues(ADest: TStrings); + constructor Create(AQuoteType: TIdHeaderQuotingType); + { This method, given a name specified by AName, extracts all of the values + for that name and puts them in a new string list (just the values) one + per line in the ADest TIdStrings.} + procedure Extract(const AName: string; ADest: TStrings); + { This property works almost exactly as Borland's IndexOfName except it + uses our delimiter defined in NameValueSeparator } + function IndexOfName(const AName: string): Integer; reintroduce; + { This property works almost exactly as Borland's Names except it uses + our delimiter defined in NameValueSeparator } + property Names[Index: Integer]: string read GetName; + { This property works almost exactly as Borland's Values except it uses + our delimiter defined in NameValueSeparator } + property Values[const Name: string]: string read GetValue write SetValue; + property Params[const Name, Param: string]: string read GetParam write SetParam; + property AllParams[const Name: string]: string read GetAllParams write SetAllParams; + { This is the separator we need to separate the name from the value } + property NameValueSeparator : String read FNameValueSeparator + write FNameValueSeparator; + { Should we unfold lines so that continuation header data is returned as + well} + property UnfoldLines : Boolean read FUnfoldLines write FUnfoldLines; + { Should we fold lines we the Values(x) property is set with an + assignment } + property FoldLines : Boolean read FFoldLines write FFoldLines; + { The Wrap position for our folded lines } + property FoldLength : Integer read FFoldLinesLength write FFoldLinesLength; + end; + +implementation + +uses + IdException, + IdGlobal, + SysUtils; + +{ TIdHeaderList } + +procedure TIdHeaderList.AddStdValues(ASrc: TStrings); +var + i: integer; +begin + BeginUpdate; + try + for i := 0 to ASrc.Count - 1 do begin + AddValue(ASrc.Names[i], IndyValueFromIndex(ASrc, i)); + end; + finally + EndUpdate; + end; +end; + +procedure TIdHeaderList.AddValue(const AName, AValue: string); +var + I: Integer; +begin + if (AName <> '') and (AValue <> '') then begin {Do not Localize} + I := Add(''); {Do not Localize} + if FFoldLines then begin + FoldAndInsert(AName + FNameValueSeparator + AValue, I); + end else begin + Put(I, AName + FNameValueSeparator + AValue); + end; + end; +end; + +procedure TIdHeaderList.AddStrings(Strings: TStrings); +begin + if Strings is TIdHeaderList then begin + inherited AddStrings(Strings); + end else begin + AddStdValues(Strings); + end; +end; + +procedure TIdHeaderList.AssignTo(Dest: TPersistent); +begin + if (Dest is TStrings) and not (Dest is TIdHeaderList) then begin + ConvertToStdValues(TStrings(Dest)); + end else begin + inherited AssignTo(Dest); + end; +end; + +procedure TIdHeaderList.ConvertToStdValues(ADest: TStrings); +var + idx: Integer; + LName, LValue: string; +begin + ADest.BeginUpdate; + try + idx := 0; + while idx < Count do + begin + LName := GetName(idx); + LValue := GetValueFromLine(idx); + // TODO: use ADest.NameValueSeparator on platforms that support it + ADest.Add(LName + '=' + LValue); {do not localize} + end; + finally + ADest.EndUpdate; + end; +end; + +constructor TIdHeaderList.Create(AQuoteType: TIdHeaderQuotingType); +begin + inherited Create; + FNameValueSeparator := ': '; {Do not Localize} + FUnfoldLines := True; + FFoldLines := True; + { 78 was specified by a message draft available at + http://www.imc.org/draft-ietf-drums-msg-fmt } + // HTTP does not technically have a limitation on line lengths + FFoldLinesLength := iif(AQuoteType = QuoteHTTP, MaxInt, 78); + FQuoteType := AQuoteType; +end; + +procedure TIdHeaderList.DeleteFoldedLines(Index: Integer); +begin + Inc(Index); {skip the current line} + if Index < Count then begin + while (Index < Count) and CharIsInSet(Get(Index), 1, LWS) do begin {Do not Localize} + Delete(Index); + end; + end; +end; + +procedure TIdHeaderList.Extract(const AName: string; ADest: TStrings); +var + idx : Integer; +begin + if Assigned(ADest) then begin + ADest.BeginUpdate; + try + idx := 0; + while idx < Count do + begin + if TextIsSame(AName, GetName(idx)) then begin + ADest.Add(GetValueFromLine(idx)); + end else begin + SkipValueAtLine(idx); + end; + end; + finally + ADest.EndUpdate; + end; + end; +end; + +procedure TIdHeaderList.FoldAndInsert(AString : String; Index: Integer); +var + LStrs : TStrings; + idx : Integer; +begin + LStrs := TStringList.Create; + try + FoldLineToList(AString, LStrs); + idx := LStrs.Count - 1; + Put(Index, LStrs[idx]); + {We decrement by one because we put the last string into the HeaderList} + Dec(idx); + while idx > -1 do + begin + Insert(Index, LStrs[idx]); + Dec(idx); + end; + finally + FreeAndNil(LStrs); + end; //finally +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdHeaderList.FoldLine(AString : string): TStrings; +{$I IdDeprecatedImplBugOn.inc} +begin + Result := TStringList.Create; + try + FoldLineToList(AString, Result); + except + FreeAndNil(Result); + raise; + end; +end; + +procedure TIdHeaderList.FoldLineToList(AString : string; ALines: TStrings); +var + s : String; +begin + {we specify a space so that starts a folded line} + s := IndyWrapText(AString, EOL+' ', LWS+',', FFoldLinesLength); {Do not Localize} + if s <> '' then begin + ALines.BeginUpdate; + try + repeat + ALines.Add(TrimRight(Fetch(s, EOL))); + until s = ''; {Do not Localize}; + finally + ALines.EndUpdate; + end; + end; +end; + +function TIdHeaderList.GetName(Index: Integer): string; +var + I : Integer; +begin + Result := Get(Index); + + {We trim right to remove space to accomodate header errors such as + + Message-ID: 0 then begin + SetLength(Result, I - 1); + end else begin + SetLength(Result, 0); + end; +end; + +function TIdHeaderList.GetValue(const AName: string): string; +var + idx: Integer; +begin + idx := IndexOfName(AName); + Result := GetValueFromLine(idx); +end; + +function TIdHeaderList.GetValueFromLine(var VLine: Integer): String; +var + LLine, LSep: string; + P: Integer; +begin + if (VLine >= 0) and (VLine < Count) then begin + LLine := Get(VLine); + Inc(VLine); + + {We trim right to remove space to accomodate header errors such as + + Message-ID:= 0) and (VLine < Count) then begin + Inc(VLine); + if FUnfoldLines then begin + while VLine < Count do begin + // s[1] is safe since header lines cannot be empty as that causes then end of the header block + if not CharIsInSet(Get(VLine), 1, LWS) then begin + Break; + end; + Inc(VLine); + end; + end; + end; +end; + +function TIdHeaderList.GetParam(const AName, AParam: string): string; +var + s: string; + LQuoteType: TIdHeaderQuotingType; +begin + s := Values[AName]; + if s <> '' then begin + LQuoteType := FQuoteType; + case LQuoteType of + QuoteRFC822: begin + if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) <> -1 then begin {Do not Localize} + LQuoteType := QuoteMIME; + end; + end; + QuoteMIME: begin + if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) = -1 then begin {Do not Localize} + LQuoteType := QuoteRFC822; + end; + end; + end; + Result := ExtractHeaderSubItem(s, AParam, LQuoteType); + end else begin + Result := ''; + end; +end; + +function TIdHeaderList.GetAllParams(const AName: string): string; +var + s: string; +begin + s := Values[AName]; + if s <> '' then begin + Fetch(s, ';'); {do not localize} + Result := Trim(s); + end else begin + Result := ''; + end; +end; + +function TIdHeaderList.IndexOfName(const AName: string): Integer; +var + i: Integer; +begin + Result := -1; + for i := 0 to Count - 1 do begin + if TextIsSame(GetName(i), AName) then begin + Result := i; + Exit; + end; + end; +end; + +procedure TIdHeaderList.SetValue(const AName, AValue: string); +var + I: Integer; +begin + I := IndexOfName(AName); + if AValue <> '' then begin {Do not Localize} + if I < 0 then begin + I := Add(''); {Do not Localize} + end; + if FFoldLines then begin + DeleteFoldedLines(I); + FoldAndInsert(AName + FNameValueSeparator + AValue, I); + end else begin + Put(I, AName + FNameValueSeparator + AValue); + end; + end + else if I >= 0 then begin + if FFoldLines then begin + DeleteFoldedLines(I); + end; + Delete(I); + end; +end; + +procedure TIdHeaderList.SetParam(const AName, AParam, AValue: string); +var + LQuoteType: TIdHeaderQuotingType; +begin + LQuoteType := FQuoteType; + case LQuoteType of + QuoteRFC822: begin + if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) <> -1 then begin {Do not Localize} + LQuoteType := QuoteMIME; + end; + end; + QuoteMIME: begin + if PosInStrArray(AName, ['Content-Type', 'Content-Disposition'], False) = -1 then begin {Do not Localize} + LQuoteType := QuoteRFC822; + end; + end; + end; + Values[AName] := ReplaceHeaderSubItem(Values[AName], AParam, AValue, LQuoteType); +end; + +procedure TIdHeaderList.SetAllParams(const AName, AValue: string); +var + LValue: string; +begin + LValue := Values[AName]; + if LValue <> '' then + begin + LValue := ExtractHeaderItem(LValue); + if AValue <> '' then begin + LValue := LValue + '; ' + AValue; {do not localize} + end; + Values[AName] := LValue; + end; +end; + +end. diff --git a/indy/Protocols/IdHostnameServer.pas b/indy/Protocols/IdHostnameServer.pas new file mode 100644 index 0000000..826312d --- /dev/null +++ b/indy/Protocols/IdHostnameServer.pas @@ -0,0 +1,150 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.2 12/2/2004 4:23:54 PM JPMugaas + Adjusted for changes in Core. + + Rev 1.1 4/12/2003 10:24:08 PM GGrieve + Fix to Compile + + Rev 1.0 11/13/2002 07:54:06 AM JPMugaas + +2000-May-18: J. Peter Mugaas + -Ported to Indy + +2000-Jan-13: MTL + -13-JAN-2000 MTL: Moved to new Palette Scheme (Winshoes Servers) + +1999-May-13: Ozz Nixon + -Final version +} + +unit IdHostnameServer; + +interface + +{$i IdCompilerDefines.inc} + +{ + Original Author: Ozz Nixon +Based on RFC 953 +} + +uses + IdAssignedNumbers, + IdContext, + IdCustomTCPServer; + +Const + KnownCommands: array [0..8] of string = + ( + 'HNAME', {Do not Localize} + 'HADDR', {Do not Localize} + 'ALL', {Do not Localize} + 'HELP', {Do not Localize} + 'VERSION', {Do not Localize} + 'ALL-OLD', {Do not Localize} + 'DOMAINS', {Do not Localize} + 'ALL-DOM', {Do not Localize} + 'ALL-INGWAY' {Do not Localize} + ); + +Type + THostNameOneParmEvent = procedure(AThread: TIdContext; const AParam: String) of object; + + TIdHostNameServer = class(TIdCustomTCPServer) + protected + FOnCommandHNAME: THostNameOneParmEvent; + FOnCommandHADDR: THostNameOneParmEvent; + FOnCommandALL: TIdContextEvent; + FOnCommandHELP: TIdContextEvent; + FOnCommandVERSION: TIdContextEvent; + FOnCommandALLOLD: TIdContextEvent; + FOnCommandDOMAINS: TIdContextEvent; + FOnCommandALLDOM: TIdContextEvent; + FOnCommandALLINGWAY: TIdContextEvent; + // + function DoExecute(AContext: TIdContext): Boolean; override; + procedure InitComponent; override; + published + property DefaultPort default IdPORT_HOSTNAME; + property OnCommandHNAME: THostNameOneParmEvent read fOnCommandHNAME write fOnCommandHNAME; + property OnCommandHADDR: THostNameOneParmEvent read fOnCommandHADDR write fOnCommandHADDR; + property OnCommandALL: TIdContextEvent read fOnCommandALL write fOnCommandALL; + property OnCommandHELP: TIdContextEvent read fOnCommandHELP write fOnCommandHELP; + property OnCommandVERSION: TIdContextEvent read fOnCommandVERSION write fOnCommandVERSION; + property OnCommandALLOLD: TIdContextEvent read fOnCommandALLOLD write fOnCommandALLOLD; + property OnCommandDOMAINS: TIdContextEvent read fOnCommandDOMAINS write fOnCommandDOMAINS; + property OnCommandALLDOM: TIdContextEvent read fOnCommandALLDOM write fOnCommandALLDOM; + property OnCommandALLINGWAY: TIdContextEvent read fOnCommandALLINGWAY write fOnCommandALLINGWAY; + end; + +implementation + +uses + IdGlobalCore, + IdGlobal; + +procedure TIdHostNameServer.InitComponent; +begin + inherited InitComponent; + DefaultPort := IdPORT_HOSTNAME; +end; + +function TIdHostNameServer.DoExecute(AContext: TIdContext): Boolean; +var + S: String; +begin + Result := True; + while AContext.Connection.Connected do + begin + S := AContext.Connection.IOHandler.ReadLn; + case PosInStrArray(Fetch(S, CHAR32), KnownCommands, False) of + 0 : {hname} + if Assigned(OnCommandHNAME) then + OnCommandHNAME(AContext, S); + 1 : {haddr} + if Assigned(OnCommandHADDR) then + OnCommandHADDR(AContext, S); + 2 : {all} + if Assigned(OnCommandALL) then + OnCommandALL(AContext); + 3 : {help} + if Assigned(OnCommandHELP) then + OnCommandHELP(AContext); + 4 : {version} + if Assigned(OnCommandVERSION) then + OnCommandVERSION(AContext); + 5 : {all-old} + if Assigned(OnCommandALLOLD) then + OnCommandALLOLD(AContext); + 6 : {domains} + if Assigned(OnCommandDOMAINS) then + OnCommandDOMAINS(AContext); + 7 : {all-dom} + if Assigned(OnCommandALLDOM) then + OnCommandALLDOM(AContext); + 8 : {all-ingway} + if Assigned(OnCommandALLINGWAY) then + OnCommandALLINGWAY(AContext); + end; + end; + AContext.Connection.Disconnect; +end; + +end. diff --git a/indy/Protocols/IdIMAP4.pas b/indy/Protocols/IdIMAP4.pas new file mode 100644 index 0000000..c3ffd19 --- /dev/null +++ b/indy/Protocols/IdIMAP4.pas @@ -0,0 +1,6946 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.66 3/24/2005 3:03:28 AM DSiders + Modified TIdIMAP4.ParseStatusResult to correct an endless loop parsing an odd + number of status messages/values in the server response. + + Rev 1.65 3/23/2005 3:03:40 PM DSiders + Modified TIdIMAP4.Destroy to free resources for Capabilities and MUtf7 + properties. + + Rev 1.64 3/4/2005 3:08:42 PM JPMugaas + Removed compiler warning with stream. You sometimes need to use IdStreamVCL. + + Rev 1.63 3/3/2005 12:54:04 PM JPMugaas + Replaced TStringList with TIdStringList. + + Rev 1.62 3/3/2005 12:09:04 PM JPMugaas + TStrings were replaced with TIdStrings. + + Rev 1.60 20/02/2005 20:41:06 CCostelloe + Cleanup and reorganisations + + Rev 1.59 11/29/2004 2:46:10 AM JPMugaas + I hope that this fixes a compile error. + + Rev 1.58 11/27/04 3:11:56 AM RLebeau + Fixed bug in ownership of SASLMechanisms property. + + Updated to use TextIsSame() instead of Uppercase() comparisons. + + Rev 1.57 11/8/2004 8:39:00 AM DSiders + Removed comment in TIdIMAP4.SearchMailBox implementation that caused DOM + problem when locating the symbol id. + + Rev 1.56 10/26/2004 10:19:58 PM JPMugaas + Updated refs. + + Rev 1.55 2004.10.26 2:19:56 PM czhower + Resolved alias conflict. + + Rev 1.54 6/11/2004 9:36:34 AM DSiders + Added "Do not Localize" comments. + + Rev 1.53 6/4/04 12:48:12 PM RLebeau + ContentTransferEncoding bug fix + + Rev 1.52 01/06/2004 19:03:46 CCostelloe + .NET bug fix + + Rev 1.51 01/06/2004 01:16:18 CCostelloe + Various improvements + + Rev 1.50 20/05/2004 22:04:14 CCostelloe + IdStreamVCL changes + + Rev 1.49 20/05/2004 08:43:12 CCostelloe + IdStream change + + Rev 1.48 16/05/2004 20:40:46 CCostelloe + New TIdText/TIdAttachment processing + + Rev 1.47 24/04/2004 23:54:42 CCostelloe + IMAP-style UTF-7 encoding/decoding of mailbox names added + + Rev 1.46 13/04/2004 22:24:28 CCostelloe + Bug fix (FCapabilities not created if not DOTNET) + + Rev 1.45 3/18/2004 2:32:40 AM JPMugaas + Should compile under D8 properly. + + Rev 1.44 3/8/2004 10:10:32 AM JPMugaas + IMAP4 should now have SASLMechanisms again. Those work in DotNET now. + SSL abstraction is now supported even in DotNET so that should not be + IFDEF'ed out. + + Rev 1.43 07/03/2004 17:55:16 CCostelloe + Updates to cover changes in other units + + Rev 1.42 2/4/2004 2:36:58 AM JPMugaas + Moved more units down to the implementation clause in the units to make them + easier to compile. + + Rev 1.41 2/3/2004 4:12:50 PM JPMugaas + Fixed up units so they should compile. + + Rev 1.40 2004.02.03 5:43:48 PM czhower + Name changes + + Rev 1.39 2004.02.03 2:12:10 PM czhower + $I path change + + Rev 1.38 1/27/2004 4:01:12 PM SPerry + StringStream ->IdStringStream + + Rev 1.37 1/25/2004 3:11:12 PM JPMugaas + SASL Interface reworked to make it easier for developers to use. + SSL and SASL reenabled components. + + Rev 1.36 23/01/2004 01:48:28 CCostelloe + Added BinHex4.0 encoding support for parts + + Rev 1.35 1/21/2004 3:10:40 PM JPMugaas + InitComponent + + Rev 1.34 31/12/2003 09:40:32 CCostelloe + ChangeReplyClass removed, replaced AnsiSameText with TextIsSame, stream code + not tested. + + Rev 1.33 28/12/2003 23:48:18 CCostelloe + More TEMPORARY fixes to get it to compile under D7 and D8 .NET + + Rev 1.32 22/12/2003 01:20:20 CCostelloe + .NET fixes. This is a TEMPORARY combined Indy9/10/.NET master file. + + Rev 1.31 14/12/2003 21:03:16 CCostelloe + First version for .NET + + Rev 1.30 10/17/2003 12:11:06 AM DSiders + Added localization comments. + Added resource strings for exception messages. + + Rev 1.29 2003.10.12 3:53:10 PM czhower + compile todos + + Rev 1.28 10/12/2003 1:49:50 PM BGooijen + Changed comment of last checkin + + Rev 1.27 10/12/2003 1:43:34 PM BGooijen + Changed IdCompilerDefines.inc to Core\IdCompilerDefines.inc + + Rev 1.26 20/09/2003 15:38:38 CCostelloe + More patches added for different IMAP servers + + Rev 1.25 12/08/2003 01:17:38 CCostelloe + Retrieve and AppendMsg updated to suit changes made to attachment encoding + changes in other units + + Rev 1.24 21/07/2003 01:22:24 CCostelloe + Added CopyMsg and UIDCopyMsgs. (UID)Receive(Peek) rewritten. AppendMsg + still buggy with attachments. Public variable FGreetingBanner added. Added + "if Connected then " to Destroy. Attachment filenames now decoded if + necessary. Added support for multisection parts. Resolved issue of some + servers leaving out the trailing "NIL NIL NIL" at the end of some body + structures. UIDRetrieveAllHeaders removed + + Rev 1.23 18/06/2003 21:53:36 CCostelloe + Rewrote GetResponse from scratch. Restored Capabilities for login. Compiles + and runs properly (may be a couple of minor bugs not yet discovered). + + Rev 1.22 6/16/2003 11:48:18 PM JPMugaas + Capabilities has to be restored for SASL and SSL support. + + Rev 1.21 17/06/2003 01:33:46 CCostelloe + Updated to support new LoginSASL. Compiles OK, may not yet run OK. + + Rev 1.20 12/06/2003 10:17:54 CCostelloe + Partial update for Indy 10's new Reply structure. Compiles but does not run + correctly. Checked in to show problem with Get/SetNumericCode in IdReplyIMAP. + + Rev 1.19 04/06/2003 02:33:44 CCostelloe + Compiles under Indy 10 with the revised Indy 10 structure, but does not yet + work properly due to some of the changes. Will be fixed by me in a later + check-in. + + Rev 1.18 14/05/2003 01:55:50 CCostelloe + This version (with the extra IMAP functionality recently added) now compiles + on Indy 10 and works in a real application. + + Rev 1.17 5/12/2003 02:19:56 AM JPMugaas + Now should work properly again. I also removed all warnings and errors in + Indy 10. + + Rev 1.16 5/11/2003 07:35:44 PM JPMugaas + + Rev 1.15 5/11/2003 07:11:06 PM JPMugaas + Fixed to eliminate some warnings and compile errors in Indy 10. + + Rev 1.14 11/05/2003 23:53:52 CCostelloe + Bug fix due to Windows 98 / 2000 discrepancies + + Rev 1.13 11/05/2003 23:08:36 CCostelloe + Lots more bug fixes, plus IMAP code moved up from IdRFCReply + + Rev 1.12 5/10/2003 07:31:22 PM JPMugaas + Updated with some bug fixes and some cleanups. + + Rev 1.11 5/9/2003 10:51:26 AM JPMugaas + Bug fixes. Now works as it should. Verified. + + Rev 1.9 5/9/2003 03:49:44 AM JPMugaas + IMAP4 now supports SASL. Merged some code from Ciaran which handles the + + SASL continue reply in IMAP4 and makes a few improvements. Verified to work + on two servers. + + Rev 1.8 5/8/2003 05:41:48 PM JPMugaas + Added constant for SASL continuation. + + Rev 1.7 5/8/2003 03:17:50 PM JPMugaas + Flattened ou the SASL authentication API, made a custom descendant of SASL + enabled TIdMessageClient classes. + + Rev 1.6 5/8/2003 11:27:52 AM JPMugaas + Moved feature negoation properties down to the ExplicitTLSClient level as + feature negotiation goes hand in hand with explicit TLS support. + + Rev 1.5 5/8/2003 02:17:44 AM JPMugaas + Fixed an AV in IdPOP3 with SASL list on forms. Made exceptions for SASL + mechanisms missing more consistant, made IdPOP3 support feature feature + negotiation, and consolidated some duplicate code. + + Rev 1.4 5/7/2003 10:20:32 PM JPMugaas + + Rev 1.3 5/7/2003 04:35:30 AM JPMugaas + IMAP4 should now compile. Started on prelimary SSL support (not finished + yet). + + Rev 1.2 15/04/2003 00:57:08 CCostelloe + + Rev 1.1 2/24/2003 09:03:06 PM JPMugaas + + Rev 1.0 11/13/2002 07:54:50 AM JPMugaas + + 2001-FEB-27 IC: First version most of the IMAP features are implemented and + the core IdPOP3 features are implemented to allow a seamless + switch. + The unit is currently oriented to a session connection and not + to constant connection, because of that server events that are + raised from another user actions are not supported. + + 2001-APR-18 IC: Added support for the session's connection state with a + special exception for commands preformed in wrong connection + states. Exceptions were also added for response errors. + + 2001-MAY-05 IC: + + 2001-Mar-13 DS: Fixed Bug # 494813 in CheckMsgSeen where LastCmdResult.Text + was not using the Ln index variable to access server + responses. + + 2002-Apr-12 DS: fixed bug # 506026 in TIdIMAP4.ListSubscribedMailBoxes. Call + ParseLSubResut instead of ParseListResult. + + 2003-Mar-31 CC: Added GetUID and UIDSearchMailBox, sorted out some bugs (details + shown in comments in those functions which start with "CC:"). + + 2003-Apr-15 CC2:Sorted out some more bugs (details shown in comments in those + functions which start with "CC2:"). Set FMailBoxSeparator + in ParseListResult and ParseLSubResult. + Some IMAP servers generally return "OK completed" even if they + returned no data, such as passing a non-existent message + number to them: they possibly should return NO or BAD; the + functions here have been changed to return FALSE unless they + get good data back, even if the server answers OK. Similar + change made for other functions. + There are a few exceptions, e.g. ListMailBoxes may only return + "OK completed" if the user has no mailboxes, these are noted. + Also, RetrieveStructure(), UIDRetrieveStructure, RetrievePart, + UIDRetrievePart, RetrievePartPeek and UIDRetrievePartPeek + added to allow user to find the structure of a message and + just retrieve the part or parts he needs. + + 2003-Apr-30 CC3:Added functionality to retrieve the text of a message (only) + via RetrieveText / UIDRetrieveText / RetrieveTextPeek / + UIDRetrieveTextPeek. + Return codes now generally reflect if the function succeeded + instead of returning True even though function fails. + + 2003-May-15 CC4:Added functionality to retrieve individual parts of a message + to a file, including the decoding of those parts. + + 2003-May-29 CC5:Response of some servers to UID version of commands varies, + code changed to deal with those (UID position varies). + Some servers return NO such as when you request an envelope + for a message number that does not exist: functions return + False instead of throwing an exception, as was done for other + servers. The general logic is that if a valid result is + returned from the IMAP server, return True; if there is no + result (but the command is validly structured), return FALSE; + if the command is badly structured or if it gives a response + that this code does not expect, throw an exception (typically + when we get a BAD response instead of OK or NO). + Added IsNumberValid, IsUIDValid to prevent rubbishy parameters + being passed through to IMAP functions. + Sender field now filled in correctly in ParseEnvelope + functions. + All fields in ParseEnvelopeAddress are cleared out first, + avoids an unwitting error where some entries, such as CC list, + will append entries to existing entries. + Full test script now used that tests every TIdIMAP command, + more bugs eradicated. + First version to pass testing against both CommuniGate and + Cyrus IMAP servers. + Not tested against Microsoft Exchange, don't have an Exchange + account to test it against. + + 2003-Jun-10 CC6:Added (UID)RetrieveEnvelopeRaw, in case the user wants to do + their own envelope parsing. + Code in RetrievePart altered to make it more consistent. + Altered to incorporate Indy 10's use of IdReplyIMAP4 (not + complete at this stage). + ReceiveBody added to IdIMAP4, due to the response of some + servers, which gets (UID)Receive(Peek) functions to work on + more servers. + + 2003-Jun-20 CC7:ReceiveBody altered to work with Indy 10. Made changes due to + LoginSASL moving from TIdMessageSASLClient to TIdSASLList. + Public variable FGreetingBanner added to help user identify + the IMAP server he is connected to (may help him decide the + best strategy). Made AppendMsg work a bit better (now uses + platform-independent EOL and supports ExtraHeaders field). + Added 2nd version of AppendMsg. Added "if Connected then " + to Destroy. Attachment filenames now decoded if necessary. + Added support for multisection parts. + + 2003-Jul-16 CC8:Added RemoveAnyAdditionalResponses. Resolved issue of some + servers leaving out the trailing "NIL NIL NIL" at the end of + some body structures. (UID)Retrieve(Peek) functions + integrated via InternalRetrieve, new method of implementing + these functions (all variations of Retrieve) added for Indy + 10 based on getting message by the byte-count and then feeding + it into the standard message parser. + UIDRetrieveAllHeaders removed: it was never implemented anyway + but it makes no sense to retrieve a non-contiguous list which + would have gaps due to missing UIDs. + In the Indy 10 version, AppendMsg functions were altered to + support the sending of attachments (attachments had never + been supported in AppendMsg prior to this). + Added CopyMsg and UIDCopyMsgs to complete the command set. + 2003-Jul-30 CC9:Removed wDoublePoint so that the code is compliant with + the guidelines. Allowed for servers that don't implement + search commands in Indy 9 (OK in 10). InternalRetrieve + altered to (hopefully) deal with optional "FLAGS (\Seen)" + in response. + 2003-Aug-22 CCA:Yet another IMAP oddity - a server returns NIL for the + mailbox separator, ParseListResult modified. Added "Length + (LLine) > 0)" test to stop GPF on empty line in ReceiveBody. + 2003-Sep-26 CCB:Changed SendCmd altered to try to remove anything that may + be unprocessed from a previous (probably failed) command. + This uses the property FMilliSecsToWaitToClearBuffer, which + defaults to 10ms. + Added EIdDisconnectedProbablyIdledOut, trapped in + GetInternalResponse. + Unsolicited responses now filtered out (they are now transferred + from FLastCmdResult.Text to a new field, FLastCmdResult.Extra, + leaving just the responses we want to our command in + FLastCmdResult.Text). + 2003-Oct-21 CCC:Original GetLineResponse merged with GetResponse to reduce + complexity and to add filtering unsolicited responses when + we are looking for single-line responses (which GetLineResponse + did), removed/coded-out much of these functions to make the + code much simpler. + Removed RemoveAnyAdditionalResponses, no longer needed. + Parsing of body structure reworked to support ParentPart concept + allowing parsing of indefinitely-nested MIME parts. Note that + a`MIME "alternative" message with a plain-text and a html part + will have part[0] marked "alternative" with size 0 and ImapPartNumber + of 1, a part[1] of type text/plain with a ParentPart of 0 and an + ImapPartNumber of 1.1, and finally a part[2] of type text/html + again with a ParentPart of 0 and an ImapPartNumber of 1.2. + Imap part number changed from an integer to string, allowing + retrieval of IMAP sub-parts, e.g. part '3.2' is the 2nd subpart + of part 3. + 2003-Nov-20 CCD:Added UIDRetrievePartHeader & RetrievePartHeader. Started to + use an abstracted parsing method for the command response in + UIDRetrieveFlags. Added function FindHowServerCreatesFolders. + 2003-Dec-04 CCE:Copied DotNet connection changes from IdSMTP to tempoarily bypass + the SASL authentications until they are ported. + 2004-Jan-23 CCF:Finished .NET port, added BinHex4.0 encoding. + 2004-Apr-16 CCG:Added UTF-7 decoding/encoding code kindly written and submitted by + Roman Puls for encoding/decoding mailbox names. IMAP does not use + standard UTF-7 code (what's new?!) so these routines are localised + to this unit. +} + +unit IdIMAP4; + +{ + IMAP 4 (Internet Message Access Protocol - Version 4 Rev 1) + By Idan Cohen i_cohen@yahoo.com +} + +interface + +{ Todo -oIC : +Change the mailbox list commands so that they receive TMailBoxTree +structures and so they can store in them the mailbox name and it's attributes. } + +{ Todo -oIC : +Add support for \* special flag in messages, and check for \Recent +flag in STORE command because it cant be stored (will get no reply!!!) } + +{ Todo -oIC : +5.1.2. Mailbox Namespace Naming Convention +By convention, the first hierarchical element of any mailbox name +which begins with "#" identifies the "namespace" of the remainder of +the name. This makes it possible to disambiguate between different +types of mailbox stores, each of which have their own namespaces. +For example, implementations which offer access to USENET +newsgroups MAY use the "#news" namespace to partition the USENET +newsgroup namespace from that of other mailboxes. Thus, the +comp.mail.misc newsgroup would have an mailbox name of +"#news.comp.mail.misc", and the name "comp.mail.misc" could refer +to a different object (e.g. a user's private mailbox). } + +{ TO BE CONSIDERED -CC : +Double-quotes in mailbox names can cause major but subtle failures. Maybe +add the automatic stripping of double-quotes if passed in mailbox names, +to avoid ending up with ""INBOX"" +} + +{CC3: WARNING - if the following gives a "File not found" error on compilation, +you need to add the path "C:\Program Files\Borland\Delphi7\Source\Indy" in +Project -> Options -> Directories/Conditionals -> Search Path} + +{$I IdCompilerDefines.inc} + +uses + Classes, + {$IFNDEF VCL_6_OR_ABOVE}IdCTypes,{$ENDIF} + IdMessage, + IdAssignedNumbers, + IdMailBox, + IdException, + IdGlobal, + IdMessageParts, + IdMessageClient, + IdReply, + IdComponent, + IdMessageCoder, + IdHeaderList, + IdCoderHeader, + IdCoderMIME, + IdCoderQuotedPrintable, + IdCoderBinHex4, + IdSASLCollection, + IdMessageCollection, + IdBaseComponent; + +{ MUTF7 } + +type + EmUTF7Error = class(EIdSilentException); + EmUTF7Encode = class(EmUTF7Error); + EmUTF7Decode = class(EmUTF7Error); + +type + // TODO: make an IIdTextEncoding implementation for Modified UTF-7 + TIdMUTF7 = class(TObject) + public + function Encode(const aString : TIdUnicodeString): String; + function Decode(const aString : String): TIdUnicodeString; + function Valid(const aMUTF7String : String): Boolean; + function Append(const aMUTF7String: String; const aStr: TIdUnicodeString): String; + end; + +{ TIdIMAP4 } + +const + wsOk = 1; + wsNo = 2; + wsBad = 3; + wsPreAuth = 4; + wsBye = 5; + wsContinue = 6; + +type + TIdIMAP4FolderTreatment = ( //Result codes from FindHowServerCreatesFolders + ftAllowsTopLevelCreation, //Folders can be created at the same level as Inbox (the top level) + ftFoldersMustBeUnderInbox, //Folders must be created under INBOX, such as INBOX.Sent + ftDoesNotAllowFolderCreation, //Wont allow you create folders at top level or under Inbox (may be read-only connection) + ftCannotTestBecauseHasNoInbox, //Wont allow top-level creation but cannot test creation under Inbox because it does not exist + ftCannotRetrieveAnyFolders //No folders present for that user, cannot be determined + ); + +type + TIdIMAP4AuthenticationType = ( + iatUserPass, + iatSASL + ); + +const + DEF_IMAP4_AUTH = iatUserPass; + IDF_DEFAULT_MS_TO_WAIT_TO_CLEAR_BUFFER = 10; + +{CC3: TIdImapMessagePart and TIdImapMessageParts added for retrieving +individual parts of a message via IMAP, because IMAP uses some additional +terms. +Note that (rarely) an IMAP can have two sub-"parts" in the one part - +they are sent in the one part by the server, typically a plain-text and +html version with a boundary at the start, in between, and at the end. +TIdIMAP fills in the boundary in that case, and the FSubpart holds the +info on the second part. I call these multisection parts.} + +type + TIdImapMessagePart = class(TCollectionItem) + protected + FBodyType: string; + FBodySubType: string; + FFileName: string; + FDescription: string; + FEncoding: TIdMessageEncoding; + FCharSet: string; + FContentTransferEncoding: string; + FSize: integer; + FUnparsedEntry: string; {Text returned from server: useful for debugging or workarounds} + FBoundary: string; {Only used for multisection parts} + FParentPart: Integer; + FImapPartNumber: string; + public + constructor Create(Collection: TCollection); override; + property BodyType : String read FBodyType write FBodyType; + property BodySubType : String read FBodySubType write FBodySubType; + property FileName : String read FFileName write FFileName; + property Description : String read FDescription write FDescription; + property Encoding: TIdMessageEncoding read FEncoding write FEncoding; + property CharSet: string read FCharSet write FCharSet; + property ContentTransferEncoding : String read FContentTransferEncoding write FContentTransferEncoding; + property Size : integer read FSize write FSize; + property UnparsedEntry : string read FUnparsedEntry write FUnparsedEntry; + property Boundary : string read FBoundary write FBoundary; + property ParentPart: integer read FParentPart write FParentPart; + property ImapPartNumber: string read FImapPartNumber write FImapPartNumber; + end; + + {CC3: Added for validating message number} + EIdNumberInvalid = class(EIdException); + {CCB: Added for server disconnecting you if idle too long...} + EIdDisconnectedProbablyIdledOut = class(EIdException); + + TIdImapMessageParts = class(TOwnedCollection) + protected + function GetItem(Index: Integer): TIdImapMessagePart; + procedure SetItem(Index: Integer; const Value: TIdImapMessagePart); + public + constructor Create(AOwner: TPersistent); reintroduce; + function Add: TIdImapMessagePart; reintroduce; + property Items[Index: Integer]: TIdImapMessagePart read GetItem write SetItem; default; + end; + +{CCD: Added to parse out responses, because the order in which the responses appear +varies between servers. A typical line that gets parsed into this is: + * 9 FETCH (UID 1234 FLAGS (\Seen \Deleted)) +} + TIdIMAPLineStruct = class(TObject) + protected + HasStar: Boolean; //Line starts with a '*' + MessageNumber: string; //Line has a message number (after the *) + Command: string; //IMAP servers send back the command they are responding to, e.g. FETCH + UID: string; //Sometimes the UID is echoed back + Flags: TIdMessageFlagsSet; //Sometimes the FLAGS are echoed back + Complete: Boolean; //If false, line has no closing bracket (response continues on following line(s)) + ByteCount: integer; //The value in a trailing byte count like {123}, -1 means not present + IMAPFunction: string; //E.g. FLAGS + IMAPValue: string; //E.g. '(\Seen \Deleted)' + end; + + TIdIMAP4Commands = ( + cmdCAPABILITY, + cmdNOOP, + cmdLOGOUT, + cmdAUTHENTICATE, + cmdLOGIN, + cmdSELECT, + cmdEXAMINE, + cmdCREATE, + cmdDELETE, + cmdRENAME, + cmdSUBSCRIBE, + cmdUNSUBSCRIBE, + cmdLIST, + cmdLSUB, + cmdSTATUS, + cmdAPPEND, + cmdCHECK, + cmdCLOSE, + cmdEXPUNGE, + cmdSEARCH, + cmdFETCH, + cmdSTORE, + cmdCOPY, + cmdUID, + cmdXCmd + ); + + {CC3: Add csUnexpectedlyDisconnected for when we receive "Connection reset by peer"} + TIdIMAP4ConnectionState = ( + csAny, + csNonAuthenticated, + csAuthenticated, + csSelected, + csUnexpectedlyDisconnected + ); + + {**************************************************************************** + Universal commands CAPABILITY, NOOP, and LOGOUT + Authenticated state commands SELECT, EXAMINE, CREATE, DELETE, RENAME, + SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, STATUS, and APPEND + Selected state commands CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID + *****************************************************************************} + + TIdIMAP4SearchKey = ( + skAll, //All messages in the mailbox; the default initial key for ANDing. + skAnswered, //Messages with the \Answered flag set. + skBcc, //Messages that contain the specified string in the envelope structure's BCC field. + skBefore, //Messages whose internal date is earlier than the specified date. + skBody, //Messages that contain the specified string in the body of the message. + skCc, //Messages that contain the specified string in the envelope structure's CC field. + skDeleted, //Messages with the \Deleted flag set. + skDraft, //Messages with the \Draft flag set. + skFlagged, //Messages with the \Flagged flag set. + skFrom, //Messages that contain the specified string in the envelope structure's FROM field. + skHeader, //Messages that have a header with the specified field-name (as defined in [RFC-822]) + //and that contains the specified string in the [RFC-822] field-body. + skKeyword, //Messages with the specified keyword set. + skLarger, //Messages with an [RFC-822] size larger than the specified number of octets. + skNew, //Messages that have the \Recent flag set but not the \Seen flag. + //This is functionally equivalent to "(RECENT UNSEEN)". + skNot, //Messages that do not match the specified search key. + skOld, //Messages that do not have the \Recent flag set. This is functionally + //equivalent to "NOT RECENT" (as opposed to "NOT NEW"). + skOn, //Messages whose internal date is within the specified date. + skOr, //Messages that match either search key. + skRecent, //Messages that have the \Recent flag set. + skSeen, //Messages that have the \Seen flag set. + skSentBefore,//Messages whose [RFC-822] Date: header is earlier than the specified date. + skSentOn, //Messages whose [RFC-822] Date: header is within the specified date. + skSentSince, //Messages whose [RFC-822] Date: header is within or later than the specified date. + skSince, //Messages whose internal date is within or later than the specified date. + skSmaller, //Messages with an [RFC-822] size smaller than the specified number of octets. + skSubject, //Messages that contain the specified string in the envelope structure's SUBJECT field. + skText, //Messages that contain the specified string in the header or body of the message. + skTo, //Messages that contain the specified string in the envelope structure's TO field. + skUID, //Messages with unique identifiers corresponding to the specified unique identifier set. + skUnanswered,//Messages that do not have the \Answered flag set. + skUndeleted, //Messages that do not have the \Deleted flag set. + skUndraft, //Messages that do not have the \Draft flag set. + skUnflagged, //Messages that do not have the \Flagged flag set. + skUnKeyWord, //Messages that do not have the specified keyword set. + skUnseen, + skGmailRaw, //Gmail-specific extension toaccess full Gmail search syntax + skGmailMsgID, //Gmail-specific unique message identifier + skGmailThreadID, //Gmail-specific thread identifier + skGmailLabels //Gmail-specific labels + ); + + TIdIMAP4SearchKeyArray = array of TIdIMAP4SearchKey; + + TIdIMAP4SearchRec = record + Date: TDateTime; + Size: Integer; + Text: String; + SearchKey : TIdIMAP4SearchKey; + FieldName: String; + end; + + TIdIMAP4SearchRecArray = array of TIdIMAP4SearchRec; + + TIdIMAP4StatusDataItem = ( + mdMessages, + mdRecent, + mdUIDNext, + mdUIDValidity, + mdUnseen + ); + + TIdIMAP4StoreDataItem = ( + sdReplace, + sdReplaceSilent, + sdAdd, + sdAddSilent, + sdRemove, + sdRemoveSilent + ); + + TIdRetrieveOnSelect = ( + rsDisabled, + rsHeaders, + rsMessages + ); + + TIdAlertEvent = procedure(ASender: TObject; const AAlertMsg: String) of object; + + TIdIMAP4 = class(TIdMessageClient) + protected + FCmdCounter : Integer; + FConnectionState : TIdIMAP4ConnectionState; + FMailBox : TIdMailBox; + FMailBoxSeparator: Char; + FOnAlert: TIdAlertEvent; + FRetrieveOnSelect: TIdRetrieveOnSelect; + FMilliSecsToWaitToClearBuffer: integer; + FMUTF7: TIdMUTF7; + FOnWorkForPart: TWorkEvent; + FOnWorkBeginForPart: TWorkBeginEvent; + FOnWorkEndForPart: TWorkEndEvent; + FGreetingBanner : String; {CC7: Added because it may help identify the server} + FHasCapa : Boolean; + FSASLMechanisms : TIdSASLEntries; + FAuthType : TIdIMAP4AuthenticationType; + FCapabilities: TStrings; + FLineStruct: TIdIMAPLineStruct; + function GetReplyClass:TIdReplyClass; override; + function GetSupportsTLS: Boolean; override; + function CheckConnectionState(AAllowedState: TIdIMAP4ConnectionState): TIdIMAP4ConnectionState; overload; + function CheckConnectionState(const AAllowedStates: array of TIdIMAP4ConnectionState): TIdIMAP4ConnectionState; overload; + function CheckReplyForCapabilities: Boolean; + procedure BeginWorkForPart(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64); + procedure DoWorkForPart(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); + procedure EndWorkForPart(ASender: TObject; AWorkMode: TWorkMode); + //The following call FMUTF7 but do exception-handling on invalid strings... + function DoMUTFEncode(const aString : String): String; + function DoMUTFDecode(const aString : String): String; + function GetCmdCounter: String; + function GetConnectionStateName: String; + function GetNewCmdCounter: String; + property LastCmdCounter: String read GetCmdCounter; + property NewCmdCounter: String read GetNewCmdCounter; + { General Functions } + function ArrayToNumberStr (const AMsgNumList: array of Integer): String; + function MessageFlagSetToStr (const AFlags: TIdMessageFlagsSet): String; + procedure StripCRLFs(var AText: string); overload; virtual; //Allow users to optimise + procedure StripCRLFs(ASourceStream, ADestStream: TStream); overload; + { Parser Functions } + procedure ParseImapPart(ABodyStructure: string; + AImapParts: TIdImapMessageParts; AThisImapPart: TIdImapMessagePart; + AParentImapPart: TIdImapMessagePart; APartNumber: integer); + procedure ParseMessagePart(ABodyStructure: string; AMessageParts: TIdMessageParts; + AThisMessagePart: TIdMessagePart; AParentMessagePart: TIdMessagePart; + APartNumber: integer); + procedure ParseBodyStructureResult(ABodyStructure: string; ATheParts: TIdMessageParts; AImapParts: TIdImapMessageParts); + procedure ParseBodyStructurePart(APartString: string; AThePart: TIdMessagePart; AImapPart: TIdImapMessagePart); + procedure ParseTheLine(ALine: string; APartsList: TStrings); + procedure ParseIntoParts(APartString: string; AParams: TStrings); + procedure ParseIntoBrackettedQuotedAndUnquotedParts(APartString: string; AParams: TStrings; AKeepBrackets: Boolean); + procedure BreakApartParamsInQuotes(const AParam: string; AParsedList: TStrings); + function GetNextWord(AParam: string): string; + function GetNextQuotedParam(AParam: string; ARemoveQuotes: Boolean): string; + procedure ParseExpungeResult (AMB: TIdMailBox; ACmdResultDetails: TStrings); + procedure ParseListResult (AMBList: TStrings; ACmdResultDetails: TStrings); + procedure ParseLSubResult(AMBList: TStrings; ACmdResultDetails: TStrings); + procedure InternalParseListResult(ACmd: string; AMBList: TStrings; ACmdResultDetails: TStrings); + procedure ParseMailBoxAttributeString(AAttributesList: String; var AAttributes: TIdMailBoxAttributesSet); + procedure ParseMessageFlagString (AFlagsList: String; var AFlags: TIdMessageFlagsSet); + procedure ParseSelectResult (AMB: TIdMailBox; ACmdResultDetails: TStrings); + procedure ParseStatusResult (AMB: TIdMailBox; ACmdResultDetails: TStrings); + procedure ParseSearchResult (AMB: TIdMailBox; ACmdResultDetails: TStrings); + procedure ParseEnvelopeResult (AMsg: TIdMessage; ACmdResultStr: String); + function ParseLastCmdResult(ALine: string; AExpectedCommand: string; AExpectedIMAPFunction: array of string): Boolean; + procedure ParseLastCmdResultButAppendInfo(ALine: string); + function InternalRetrieve(const AMsgNum: Integer; AUseUID: Boolean; AUsePeek: Boolean; AMsg: TIdMessage): Boolean; + function InternalRetrievePart(const AMsgNum: Integer; const APartNum: string; + AUseUID: Boolean; AUsePeek: Boolean; + ADestStream: TStream; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; {NOTE: var args cannot have default params} + ADestFileNameAndPath: string = ''; {Do not Localize} + AContentTransferEncoding: string = 'text'): Boolean; {Do not Localize} + //Retrieves the specified number of headers of the selected mailbox to the specified TIdMessageCollection. + function InternalRetrieveHeaders(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; + //Retrieves the specified number of messages of the selected mailbox to the specified TIdMessageCollection. + function InternalRetrieveMsgs(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; + function InternalSearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; AUseUID: Boolean; const ACharSet: string): Boolean; + function ParseBodyStructureSectionAsEquates(AParam: string): string; + function ParseBodyStructureSectionAsEquates2(AParam: string): string; + function InternalRetrieveText(const AMsgNum: Integer; var AText: string; + AUseUID: Boolean; AUsePeek: Boolean; AUseFirstPartInsteadOfText: Boolean): Boolean; + function IsCapabilityListed(ACapability: string): Boolean; + function InternalRetrieveEnvelope(const AMsgNum: Integer; AMsg: TIdMessage; ADestList: TStrings): Boolean; + function UIDInternalRetrieveEnvelope(const AMsgUID: String; AMsg: TIdMessage; ADestList: TStrings): Boolean; + function InternalRetrievePartHeader(const AMsgNum: Integer; const APartNum: string; const AUseUID: Boolean; + AHeaders: TIdHeaderList): Boolean; + function ReceiveHeader(AMsg: TIdMessage; const AAltTerm: string = ''): string; override; + {CC3: Need to validate message numbers (relative and UIDs) and part numbers, because otherwise + the routines wait for a response that never arrives and so functions never return. + Also used for validating part numbers.} + function IsNumberValid(const ANumber: Integer): Boolean; + function IsUIDValid(const AUID: string): Boolean; + function IsImapPartNumberValid(const AUID: string): Boolean; + function IsItDigitsAndOptionallyPeriod(const AStr: string; AAllowPeriod: Boolean): Boolean; + {CC6: Override IdMessageClient's ReceiveBody due to the responses from some servers...} + procedure ReceiveBody(AMsg: TIdMessage; const ADelim: string = '.'); override; {Do not Localize} + procedure InitComponent; override; + procedure SetMailBox(const Value: TIdMailBox); + procedure SetSASLMechanisms(AValue: TIdSASLEntries); + public + { TIdIMAP4 Commands } + destructor Destroy; override; + //Requests a listing of capabilities that the server supports... + function Capability: Boolean; overload; + function Capability(ASlCapability: TStrings): Boolean; overload; + function FindHowServerCreatesFolders: TIdIMAP4FolderTreatment; + procedure DoAlert(const AMsg: String); + property ConnectionState: TIdIMAP4ConnectionState read FConnectionState; + property MailBox: TIdMailBox read FMailBox write SetMailBox; + {CC7: Two versions of AppendMsg are provided. The first is the normal one you + would use. The second allows you to specify an alternative header list which + will be used in place of AMsg.Headers. + An email client may need the second type if it sends an email via IdSMTP and wants + to copy it to a "Sent" IMAP folder. In Indy 10, + IdSMTP puts the generated headers in the LastGeneratedHeaders field, so you + can use the second version of AppendMsg, passing it AMsg.LastGeneratedHeaders as + the AAlternativeHeaders field. Note that IdSMTP puts both the Headers and + the ExtraHeaders fields in LastGeneratedHeaders.} + function AppendMsg(const AMBName: String; AMsg: TIdMessage; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; overload; + function AppendMsg(const AMBName: String; AMsg: TIdMessage; AAlternativeHeaders: TIdHeaderList; + const AFlags: TIdMessageFlagsSet = []; const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; overload; + //The following are used for raw (unparsed) messages in a file or stream... + function AppendMsgNoEncodeFromFile(const AMBName: String; ASourceFile: string; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; + function AppendMsgNoEncodeFromStream(const AMBName: String; AStream: TStream; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; + //Requests a checkpoint of the currently selected mailbox. Does NOTHING on most servers. + function CheckMailBox: Boolean; + //Checks if the message was read or not. + function CheckMsgSeen(const AMsgNum: Integer): Boolean; + //Method for logging in manually if you didn't login at connect + procedure Login; virtual; + //Connects and logins to the IMAP4 account. + function Connect(const AAutoLogin: boolean = true): Boolean; reintroduce; virtual; + //Closes the current selected mailbox in the account. + function CloseMailBox: Boolean; + //Creates a new mailbox with the specified name in the account. + function CreateMailBox(const AMBName: String): Boolean; + //Deletes the specified mailbox from the account. + function DeleteMailBox(const AMBName: String): Boolean; + //Marks messages for deletion, it will be deleted when the mailbox is purged. + function DeleteMsgs(const AMsgNumList: array of Integer): Boolean; + //Logouts and disconnects from the IMAP account. + procedure Disconnect(ANotifyPeer: Boolean); override; + procedure DisconnectNotifyPeer; override; + //Examines the specified mailbox and inserts the results to the TIdMailBox provided. + function ExamineMailBox(const AMBName: String; AMB: TIdMailBox): Boolean; + //Expunges (deletes the marked files) the current selected mailbox in the account. + function ExpungeMailBox: Boolean; + //Sends a NOOP (No Operation) to keep the account connection with the server alive. + procedure KeepAlive; + //Returns a list of all the child mailboxes (one level down) to the mailbox supplied. + //This should be used when you fear that there are too many mailboxes and the listing of + //all of them could be time consuming, so this should be used to retrieve specific mailboxes. + function ListInferiorMailBoxes(AMailBoxList, AInferiorMailBoxList: TStrings): Boolean; + //Returns a list of all the mailboxes in the user account. + function ListMailBoxes(AMailBoxList: TStrings): Boolean; + //Returns a list of all the subscribed mailboxes in the user account. + function ListSubscribedMailBoxes (AMailBoxList: TStrings): Boolean; + //Renames the specified mailbox in the account. + function RenameMailBox(const AOldMBName, ANewMBName: String): Boolean; + //Searches the current selected mailbox for messages matching the SearchRec and + //returns the results to the mailbox SearchResults array. + function SearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; const ACharSet: string = ''): Boolean; + //Selects the current a mailbox in the account. + function SelectMailBox(const AMBName: String): Boolean; + //Retrieves the status of the indicated mailbox. + {CC2: It is pointless calling StatusMailBox with AStatusDataItems set to [] + because you are asking the IMAP server to update none of the status flags. + Instead, if called with no AStatusDataItems specified, we use the standard flags + returned by SelectMailBox, which allows the user to easily check if the mailbox + has changed.} + function StatusMailBox(const AMBName: String; AMB: TIdMailBox): Boolean; overload; + function StatusMailBox(const AMBName: String; AMB: TIdMailBox; + const AStatusDataItems: array of TIdIMAP4StatusDataItem): Boolean; overload; + //Changes (adds or removes) message flags. + function StoreFlags(const AMsgNumList: array of Integer; const AStoreMethod: TIdIMAP4StoreDataItem; + const AFlags: TIdMessageFlagsSet): Boolean; + //Adds the specified mailbox name to the server's set of "active" or "subscribed" + //mailboxes as returned by the LSUB command. + function SubscribeMailBox(const AMBName: String): Boolean; + {CC8: Added CopyMsg, should have always been there...} + function CopyMsg(const AMsgNum: Integer; const AMBName: String): Boolean; + //Copies a message from the current selected mailbox to the specified mailbox. {Do not Localize} + function CopyMsgs(const AMsgNumList: array of Integer; const AMBName: String): Boolean; + //Retrieves a whole message while marking it read. + function Retrieve(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; + //Retrieves a whole message "raw" and saves it to file, while marking it read. + function RetrieveNoDecodeToFile(const AMsgNum: Integer; ADestFile: string): Boolean; + function RetrieveNoDecodeToFilePeek(const AMsgNum: Integer; ADestFile: string): Boolean; + function RetrieveNoDecodeToStream(const AMsgNum: Integer; AStream: TStream): Boolean; + function RetrieveNoDecodeToStreamPeek(const AMsgNum: Integer; AStream: TStream): Boolean; + //Retrieves all envelope of the selected mailbox to the specified TIdMessageCollection. + function RetrieveAllEnvelopes(AMsgList: TIdMessageCollection): Boolean; + //Retrieves all headers of the selected mailbox to the specified TIdMessageCollection. + function RetrieveAllHeaders(AMsgList: TIdMessageCollection): Boolean; + //Retrieves the first NN headers of the selected mailbox to the specified TIdMessageCollection. + function RetrieveFirstHeaders(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; + //Retrieves all messages of the selected mailbox to the specified TIdMessageCollection. + function RetrieveAllMsgs(AMsgList: TIdMessageCollection): Boolean; + //Retrieves the first NN messages of the selected mailbox to the specified TIdMessageCollection. + function RetrieveFirstMsgs(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; + //Retrieves the message envelope, parses it, and discards the envelope. + function RetrieveEnvelope(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; + //Retrieves the message envelope into a TStringList but does NOT parse it. + function RetrieveEnvelopeRaw(const AMsgNum: Integer; ADestList: TStrings): Boolean; + //Returnes the message flag values. + function RetrieveFlags(const AMsgNum: Integer; var AFlags: TIdMessageFlagsSet): Boolean; + {CC2: Following added for retrieving individual parts of a message...} + function InternalRetrieveStructure(const AMsgNum: Integer; AMsg: TIdMessage; AParts: TIdImapMessageParts): Boolean; + //Retrieve only the message structure (this tells you what parts are in the message). + function RetrieveStructure(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; overload; + function RetrieveStructure(const AMsgNum: Integer; AParts: TIdImapMessageParts): Boolean; overload; + {CC2: Following added for retrieving individual parts of a message...} + {Retrieve a specific individual part of a message to a stream (part/sub-part like '2' or '2.3')...} + function RetrievePart(const AMsgNum: Integer; const APartNum: string; + ADestStream: TStream; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function RetrievePart(const AMsgNum: Integer; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function RetrievePart(const AMsgNum: Integer; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message to a stream (part/sub-part like '2' or '2.3') + without marking the message as "read"...} + function RetrievePartPeek(const AMsgNum: Integer; const APartNum: string; + ADestStream: TStream; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3' + without marking the message as "read"...} + function RetrievePartPeek(const AMsgNum: Integer; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility) + without marking the message as "read"...} + function RetrievePartPeek(const AMsgNum: Integer; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {CC2: Following added for retrieving individual parts of a message...} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function RetrievePartToFile(const AMsgNum: Integer; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function RetrievePartToFile(const AMsgNum: Integer; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {CC2: Following added for retrieving individual parts of a message...} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility) + without marking the message as "read"...} + function RetrievePartToFilePeek(const AMsgNum: Integer; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3' + without marking the message as "read"...} + function RetrievePartToFilePeek(const AMsgNum: Integer; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {CC3: Following added for retrieving the text-only part of a message...} + function RetrieveText(const AMsgNum: Integer; var AText: string): Boolean; + {CC4: An alternative for retrieving the text-only part of a message which + may give a better response from some IMAP implementations...} + function RetrieveText2(const AMsgNum: Integer; var AText: string): Boolean; + {CC3: Following added for retrieving the text-only part of a message + without marking the message as "read"...} + function RetrieveTextPeek(const AMsgNum: Integer; var AText: string): Boolean; + function RetrieveTextPeek2(const AMsgNum: Integer; var AText: string): Boolean; + //Retrieves only the message header. + function RetrieveHeader (const AMsgNum: Integer; AMsg: TIdMessage): Boolean; + //CCD: Retrieve the header for a particular part... + function RetrievePartHeader(const AMsgNum: Integer; const APartNum: string; AHeaders: TIdHeaderList): Boolean; + //Retrives the current selected mailbox size. + function RetrieveMailBoxSize: Integer; + //Returnes the message size. + function RetrieveMsgSize(const AMsgNum: Integer): Integer; + //Retrieves a whole message while keeping its Seen flag unchanged + //(preserving the previous value). + function RetrievePeek(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; + //Get the UID corresponding to a relative message number. + function GetUID(const AMsgNum: Integer; var AUID: string): Boolean; + //Copies a message from the current selected mailbox to the specified mailbox. + function UIDCopyMsg(const AMsgUID: String; const AMBName: String): Boolean; + {CC8: Added UID version of CopyMsgs...} + function UIDCopyMsgs(const AMsgUIDList: TStrings; const AMBName: String): Boolean; + //Checks if the message was read or not. + function UIDCheckMsgSeen(const AMsgUID: String): Boolean; + //Marks a message for deletion, it will be deleted when the mailbox will be purged. + function UIDDeleteMsg(const AMsgUID: String): Boolean; + function UIDDeleteMsgs(const AMsgUIDList: array of String): Boolean; + //Retrieves all envelope and UID of the selected mailbox to the specified TIdMessageCollection. + function UIDRetrieveAllEnvelopes(AMsgList: TIdMessageCollection): Boolean; + //Retrieves a whole message while marking it read. + function UIDRetrieve(const AMsgUID: String; AMsg: TIdMessage): Boolean; + //Retrieves a whole message "raw" and saves it to file, while marking it read. + function UIDRetrieveNoDecodeToFile(const AMsgUID: String; ADestFile: string): Boolean; + function UIDRetrieveNoDecodeToFilePeek(const AMsgUID: String; ADestFile: string): Boolean; + function UIDRetrieveNoDecodeToStream(const AMsgUID: String; AStream: TStream): Boolean; + function UIDRetrieveNoDecodeToStreamPeek(const AMsgUID: String; AStream: TStream): Boolean; + //Retrieves the message envelope, parses it, and discards the envelope. + function UIDRetrieveEnvelope(const AMsgUID: String; AMsg: TIdMessage): Boolean; + //Retrieves the message envelope into a TStringList but does NOT parse it. + function UIDRetrieveEnvelopeRaw(const AMsgUID: String; ADestList: TStrings): Boolean; + //Returnes the message flag values. + function UIDRetrieveFlags(const AMsgUID: String; var AFlags: TIdMessageFlagsSet): Boolean; + {CC2: Following added for retrieving individual parts of a message...} + function UIDInternalRetrieveStructure(const AMsgUID: String; AMsg: TIdMessage; AParts: TIdImapMessageParts): Boolean; + //Retrieve only the message structure (this tells you what parts are in the message). + function UIDRetrieveStructure(const AMsgUID: String; AMsg: TIdMessage): Boolean; overload; + function UIDRetrieveStructure(const AMsgUID: String; AParts: TIdImapMessageParts): Boolean; overload; + {Retrieve a specific individual part of a message to a stream (part/sub-part like '2' or '2.3')...} + function UIDRetrievePart(const AMsgUID: String; const APartNum: string; + var ADestStream: TStream; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function UIDRetrievePart(const AMsgUID: String; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function UIDRetrievePart(const AMsgUID: String; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message to a stream (part/sub-part like '2' or '2.3') + without marking the message as "read"...} + function UIDRetrievePartPeek(const AMsgUID: String; const APartNum: string; + var ADestStream: TStream; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function UIDRetrievePartPeek(const AMsgUID: String; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function UIDRetrievePartPeek(const AMsgUID: String; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string = 'text'): Boolean; overload; {Do not Localize} + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function UIDRetrievePartToFile(const AMsgUID: String; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function UIDRetrievePartToFile(const AMsgUID: String; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Retrieve a specific individual part of a message where part is an integer (for backward compatibility)...} + function UIDRetrievePartToFilePeek(const AMsgUID: String; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Retrieve a specific individual part of a message where part is an integer or sub-part like '2.3'...} + function UIDRetrievePartToFilePeek(const AMsgUID: String; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; overload; + {Following added for retrieving the text-only part of a message...} + function UIDRetrieveText(const AMsgUID: String; var AText: string): Boolean; + function UIDRetrieveText2(const AMsgUID: String; var AText: string): Boolean; + {Following added for retrieving the text-only part of a message without marking the message as read...} + function UIDRetrieveTextPeek(const AMsgUID: String; var AText: string): Boolean; + function UIDRetrieveTextPeek2(const AMsgUID: String; var AText: string): Boolean; + //Retrieves only the message header. + function UIDRetrieveHeader(const AMsgUID: String; AMsg: TIdMessage): Boolean; + //Retrieve the header for a particular part... + function UIDRetrievePartHeader(const AMsgUID: String; const APartNum: string; AHeaders: TIdHeaderList): Boolean; + //Retrives the current selected mailbox size. + function UIDRetrieveMailBoxSize: Integer; + //Returnes the message size. + function UIDRetrieveMsgSize(const AMsgUID: String): Integer; + //Retrieves a whole message while keeping its Seen flag untucked + //(preserving the previous value). + function UIDRetrievePeek(const AMsgUID: String; AMsg: TIdMessage): Boolean; + //Searches the current selected mailbox for messages matching the SearchRec and + //returnes the results as UIDs to the mailbox SearchResults array. + function UIDSearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; const ACharSet: String = ''): Boolean; + //Changes (adds or removes) message flags. + function UIDStoreFlags(const AMsgUID: String; const AStoreMethod: TIdIMAP4StoreDataItem; + const AFlags: TIdMessageFlagsSet): Boolean; overload; + function UIDStoreFlags(const AMsgUIDList: array of String; const AStoreMethod: TIdIMAP4StoreDataItem; + const AFlags: TIdMessageFlagsSet): Boolean; overload; + //Removes the specified mailbox name from the server's set of "active" or "subscribed" + //mailboxes as returned by the LSUB command. + function UnsubscribeMailBox(const AMBName: String): Boolean; + { IdTCPConnection Commands } + function GetInternalResponse(const ATag: String; AExpectedResponses: array of String; ASingleLineMode: Boolean; + ASingleLineMayBeSplit: Boolean = True): string; reintroduce; overload; + function GetResponse: string; reintroduce; overload; + function SendCmd(const AOut: string; AExpectedResponses: array of String; + ASingleLineMode: Boolean = False; ASingleLineMayBeSplit: Boolean = True): string; reintroduce; overload; + function SendCmd(const ATag, AOut: string; AExpectedResponses: array of String; + ASingleLineMode: Boolean = False; ASingleLineMayBeSplit: Boolean = True): string; overload; + function ReadLnWait: string; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use IOHandler.ReadLnWait()'{$ENDIF};{$ENDIF} + procedure WriteLn(const AOut: string = ''); {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use IOHandler.WriteLn()'{$ENDIF};{$ENDIF} + { IdTCPConnection Commands } + published + property OnAlert: TIdAlertEvent read FOnAlert write FOnAlert; + property Password; + property RetrieveOnSelect: TIdRetrieveOnSelect read FRetrieveOnSelect write FRetrieveOnSelect default rsDisabled; + property Port default IdPORT_IMAP4; + property Username; + property MailBoxSeparator: Char read FMailBoxSeparator write FMailBoxSeparator default '/'; {Do not Localize} + {GreetingBanner added because it may help identify the server...} + property GreetingBanner : string read FGreetingBanner; + property Host; + property UseTLS; + property SASLMechanisms : TIdSASLEntries read FSASLMechanisms write SetSASLMechanisms; + property AuthType : TIdIMAP4AuthenticationType read FAuthType write FAuthType default DEF_IMAP4_AUTH; + property MilliSecsToWaitToClearBuffer: integer read FMilliSecsToWaitToClearBuffer write FMilliSecsToWaitToClearBuffer; + {The following is the OnWork property for use when retrieving PARTS of a message. + It is also used for AppendMsg and Retrieve. This is in addition to the normal + OnWork property, which is exposed by TIdIMAP4, but which is only activated during + IMAP sending & receiving of commands (subject to the general OnWork caveats, i.e. + it is only called during certain methods, note OnWork[Begin][End] are all only + called in the methods AllData(), PerformCapture() and Read/WriteStream() ). + When a PART of a message is processed, use this for progress notification of + retrieval of IMAP parts, such as retrieving attachments. OnWorkBegin and + OnWorkEnd are not exposed, because they won't be activated during the processing + of a part.} + property OnWorkForPart: TWorkEvent read FOnWorkForPart write FOnWorkForPart; + property OnWorkBeginForPart: TWorkBeginEvent read FOnWorkBeginForPart write FOnWorkBeginForPart; + property OnWorkEndForPart: TWorkEndEvent read FOnWorkEndForPart write FOnWorkEndForPart; + end; + +implementation + +uses + //facilitate inlining on + {$IFDEF KYLIXCOMPAT} + Libc, + {$IFDEF MACOSX} + Posix.Unistd, + {$ENDIF} + {$ENDIF} + //facilitate inlining only. + {$IFDEF WINDOWS} + {$IFDEF USE_INLINE} + Windows, + {$ELSE} + //facilitate inlining only. + {$IFDEF VCL_2009_OR_ABOVE} + Windows, + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF DOTNET} + IdStreamNET, + {$ELSE} + IdStreamVCL, + {$ENDIF} + {$IFDEF HAS_UNIT_Generics_Collections} + System.Generics.Collections, + {$ENDIF} + IdCoder, + IdEMailAddress, + IdResourceStrings, + IdExplicitTLSClientServerBase, + IdGlobalProtocols, + IdExceptionCore, + IdStack, + IdStackConsts, + IdStream, + IdTCPStream, + IdText, + IdAttachment, + IdResourceStringsProtocols, + IdBuffer, + IdAttachmentMemory, + IdReplyIMAP4, + IdTCPConnection, + IdSSL, + IdSASL, + IdMessageHelper, + SysUtils; + +// TODO: move this to IdCompilerDefines.inc +{$IFDEF DCC} + {$IFDEF VCL_2005_OR_ABOVE} + {$DEFINE HAS_CLASS_HELPER} + {$ENDIF} +{$ENDIF} +{$IFDEF FPC} + {$DEFINE HAS_CLASS_HELPER} // TODO: when were class helpers introduced? +{$ENDIF} + +type + TIdIMAP4FetchDataItem = ( + fdAll, //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE) + fdBody, //Non-extensible form of BODYSTRUCTURE. + fdBodyExtensible, + fdBodyPeek, + fdBodyStructure, //The [MIME-IMB] body structure of the message. This + //is computed by the server by parsing the [MIME-IMB] + //header fields in the [RFC-822] header and [MIME-IMB] headers. + fdEnvelope, //The envelope structure of the message. This is + //computed by the server by parsing the [RFC-822] + //header into the component parts, defaulting various + //fields as necessary. + fdFast, //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE) + fdFlags, //The flags that are set for this message. + fdFull, //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY) + fdInternalDate, //The internal date of the message. + fdRFC822, //Functionally equivalent to BODY[], differing in the + //syntax of the resulting untagged FETCH data (RFC822 + //is returned). + fdRFC822Header, //Functionally equivalent to BODY.PEEK[HEADER], + //differing in the syntax of the resulting untagged + //FETCH data (RFC822.HEADER is returned). + fdRFC822Size, //The [RFC-822] size of the message. + fdRFC822Text, //Functionally equivalent to BODY[TEXT], differing in + //the syntax of the resulting untagged FETCH data + //(RFC822.TEXT is returned). + fdHeader, //CC: Added to get the header of a part + fdUID, //The unique identifier for the message. + fdGmailMsgID, //Gmail-specific unique identifier for the message. + fdGmailThreadID, //Gmail-specific thread identifier for the message. + fdGmailLabels //Gmail-specific labels for the message. + ); + +const + IMAP4Commands : array [TIdIMAP4Commands] of String = ( + { Client Commands - Any State} + 'CAPABILITY', {Do not Localize} + 'NOOP', {Do not Localize} + 'LOGOUT', {Do not Localize} + { Client Commands - Non Authenticated State} + 'AUTHENTICATE', {Do not Localize} + 'LOGIN', {Do not Localize} + { Client Commands - Authenticated State} + 'SELECT', {Do not Localize} + 'EXAMINE', {Do not Localize} + 'CREATE', {Do not Localize} + 'DELETE', {Do not Localize} + 'RENAME', {Do not Localize} + 'SUBSCRIBE', {Do not Localize} + 'UNSUBSCRIBE', {Do not Localize} + 'LIST', {Do not Localize} + 'LSUB', {Do not Localize} + 'STATUS', {Do not Localize} + 'APPEND', {Do not Localize} + { Client Commands - Selected State} + 'CHECK', {Do not Localize} + 'CLOSE', {Do not Localize} + 'EXPUNGE', {Do not Localize} + 'SEARCH', {Do not Localize} + 'FETCH', {Do not Localize} + 'STORE', {Do not Localize} + 'COPY', {Do not Localize} + 'UID', {Do not Localize} + { Client Commands - Experimental/ Expansion} + 'X' {Do not Localize} + ); + + IMAP4FetchDataItem : array [TIdIMAP4FetchDataItem] of String = ( + 'ALL', {Do not Localize} //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE) + 'BODY', {Do not Localize} //Non-extensible form of BODYSTRUCTURE. + 'BODY[%s]<%s>', {Do not Localize} + 'BODY.PEEK[]', {Do not Localize} + 'BODYSTRUCTURE', {Do not Localize} //The [MIME-IMB] body structure of the message. This + //is computed by the server by parsing the [MIME-IMB] + //header fields in the [RFC-822] header and [MIME-IMB] headers. + 'ENVELOPE', {Do not Localize} //The envelope structure of the message. This is + //computed by the server by parsing the [RFC-822] + //header into the component parts, defaulting various + //fields as necessary. + 'FAST', {Do not Localize} //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE) + 'FLAGS', {Do not Localize} //The flags that are set for this message. + 'FULL', {Do not Localize} //Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY) + 'INTERNALDATE', {Do not Localize} //The internal date of the message. + 'RFC822', {Do not Localize} //Functionally equivalent to BODY[], differing in the + //syntax of the resulting untagged FETCH data (RFC822 + //is returned). + 'RFC822.HEADER', {Do not Localize} //Functionally equivalent to BODY.PEEK[HEADER], + //differing in the syntax of the resulting untagged + //FETCH data (RFC822.HEADER is returned). + 'RFC822.SIZE', {Do not Localize} //The [RFC-822] size of the message. + 'RFC822.TEXT', {Do not Localize} //Functionally equivalent to BODY[TEXT], differing in + //the syntax of the resulting untagged FETCH data + //(RFC822.TEXT is returned). + 'HEADER', {Do not Localize} //CC: Added to get the header of a part + 'UID', {Do not Localize} //The unique identifier for the message. + 'X-GM-MSGID', {Do not Localize} //Gmail-specific unique identifier for the message. + 'X-GM-THRID', {Do not Localize} //Gmail-specific thread identifier for the message. + 'X-GM-LABELS' {Do not Localize} //Gmail-specific labels for the message. + ); + + IMAP4SearchKeys : array [TIdIMAP4SearchKey] of String = ( + 'ALL', {Do not Localize} //All messages in the mailbox; the default initial key for ANDing. + 'ANSWERED', {Do not Localize} //Messages with the \Answered flag set. + 'BCC', {Do not Localize} //Messages that contain the specified string in the envelope structure's BCC field. + 'BEFORE', {Do not Localize} //Messages whose internal date is earlier than the specified date. + 'BODY', {Do not Localize} //Messages that contain the specified string in the body of the message. + 'CC', {Do not Localize} //Messages that contain the specified string in the envelope structure's CC field. + 'DELETED', {Do not Localize} //Messages with the \Deleted flag set. + 'DRAFT', {Do not Localize} //Messages with the \Draft flag set. + 'FLAGGED', {Do not Localize} //Messages with the \Flagged flag set. + 'FROM', {Do not Localize} //Messages that contain the specified string in the envelope structure's FROM field. + 'HEADER', {Do not Localize} //Messages that have a header with the specified field-name (as defined in [RFC-822]) + //and that contains the specified string in the [RFC-822] field-body. + 'KEYWORD', {Do not Localize} //Messages with the specified keyword set. + 'LARGER', {Do not Localize} //Messages with an [RFC-822] size larger than the specified number of octets. + 'NEW', {Do not Localize} //Messages that have the \Recent flag set but not the \Seen flag. + //This is functionally equivalent to "(RECENT UNSEEN)". + 'NOT', {Do not Localize} //Messages that do not match the specified search key. + 'OLD', {Do not Localize} //Messages that do not have the \Recent flag set. This is functionally + //equivalent to "NOT RECENT" (as opposed to "NOT NEW"). + 'ON', {Do not Localize} //Messages whose internal date is within the specified date. + 'OR', {Do not Localize} //Messages that match either search key. + 'RECENT', {Do not Localize} //Messages that have the \Recent flag set. + 'SEEN', {Do not Localize} //Messages that have the \Seen flag set. + 'SENTBEFORE',{Do not Localize} //Messages whose [RFC-822] Date: header is earlier than the specified date. + 'SENTON', {Do not Localize} //Messages whose [RFC-822] Date: header is within the specified date. + 'SENTSINCE', {Do not Localize} //Messages whose [RFC-822] Date: header is within or later than the specified date. + 'SINCE', {Do not Localize} //Messages whose internal date is within or later than the specified date. + 'SMALLER', {Do not Localize} //Messages with an [RFC-822] size smaller than the specified number of octets. + 'SUBJECT', {Do not Localize} //Messages that contain the specified string in the envelope structure's SUBJECT field. + 'TEXT', {Do not Localize} //Messages that contain the specified string in the header or body of the message. + 'TO', {Do not Localize} //Messages that contain the specified string in the envelope structure's TO field. + 'UID', {Do not Localize} //Messages with unique identifiers corresponding to the specified unique identifier set. + 'UNANSWERED',{Do not Localize} //Messages that do not have the \Answered flag set. + 'UNDELETED', {Do not Localize} //Messages that do not have the \Deleted flag set. + 'UNDRAFT', {Do not Localize} //Messages that do not have the \Draft flag set. + 'UNFLAGGED', {Do not Localize} //Messages that do not have the \Flagged flag set. + 'UNKEYWORD', {Do not Localize} //Messages that do not have the specified keyword set. + 'UNSEEN', {Do not Localize} + 'X-GM-RAW', {Do not Localize} //Gmail extension to SEARCH command to allow full access to Gmail search syntax + 'X-GM-MSGID',{Do not Localize} //Gmail extension to SEARCH command to allow access to Gmail message identifier + 'X-GM-THRID',{Do not Localize} //Gmail extension to SEARCH command to allow access to Gmail thread identifier + 'X-GM-LABELS'{Do not Localize} //Gmail extension to SEARCH command to allow access to Gmail labels + ); + + IMAP4StoreDataItem : array [TIdIMAP4StoreDataItem] of String = ( + 'FLAGS', {Do not Localize} + 'FLAGS.SILENT', {Do not Localize} + '+FLAGS', {Do not Localize} + '+FLAGS.SILENT', {Do not Localize} + '-FLAGS', {Do not Localize} + '-FLAGS.SILENT' {Do not Localize} + ); + + IMAP4StatusDataItem : array [TIdIMAP4StatusDataItem] of String = ( + 'MESSAGES', {Do not Localize} + 'RECENT', {Do not Localize} + 'UIDNEXT', {Do not Localize} + 'UIDVALIDITY', {Do not Localize} + 'UNSEEN' {Do not Localize} + ); + +function IMAPQuotedStr(const S: String): String; +begin + Result := '"' + StringsReplace(S, ['\', '"'], ['\\', '\"']) + '"'; {Do not Localize} +end; + +{ TIdSASLEntriesIMAP4 } + +// RLebeau 2/8/2013 - TIdSASLEntries.LoginSASL() uses TIdTCPConnection.SendCmd() +// but TIdIMAP4 does not override the necessary virtuals to make that SendCmd() +// work correctly with IMAP. TIdIMAP reintroduces its own SendCmd() implementation, +// which TIdSASLEntries does not call. Until that can be changed, we will have +// to send the IMAP 'AUTHENTICATE' command manually! Doing it this way so as +// not to introduce an interface change that breaks backwards compatibility... + +function CheckStrFail(const AStr : String; const AOk, ACont: array of string) : Boolean; +begin + Result := (PosInStrArray(AStr, AOk) = -1) and (PosInStrArray(AStr, ACont) = -1); +end; + +function PerformSASLLogin_IMAP(ASASL: TIdSASL; AEncoder: TIdEncoder; + ADecoder: TIdDecoder; AClient : TIdIMAP4): Boolean; +const + AOkReplies: array[0..0] of string = (IMAP_OK); + AContinueReplies: array[0..0] of string = (IMAP_CONT); +var + S: String; + AuthStarted: Boolean; +begin + Result := False; + AuthStarted := False; + if AClient.IsCapabilityListed('SASL-IR') then begin {Do not localize} + if ASASL.TryStartAuthenticate(AClient.Host, IdGSKSSN_imap, S) then begin + AClient.SendCmd(AClient.NewCmdCounter, 'AUTHENTICATE ' + String(ASASL.ServiceName) + ' ' + AEncoder.Encode(S), [], True); {Do not Localize} + if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then begin + ASASL.FinishAuthenticate; + Exit; // this mechanism is not supported + end; + AuthStarted := True; + end; + end; + if not AuthStarted then begin + AClient.SendCmd(AClient.NewCmdCounter, 'AUTHENTICATE ' + String(ASASL.ServiceName), [], True); {Do not Localize} + if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then begin + Exit; // this mechanism is not supported + end; + end; + if (PosInStrArray(AClient.LastCmdResult.Code, AOkReplies) > -1) then begin + if AuthStarted then begin + ASASL.FinishAuthenticate; + end; + Result := True; + Exit; // we've authenticated successfully :) + end; + // must be a continue reply... + if not AuthStarted then begin + S := ADecoder.DecodeString(TrimRight(TIdReplyIMAP4(AClient.LastCmdResult).Extra.Text)); + S := ASASL.StartAuthenticate(S, AClient.Host, IdGSKSSN_imap); + AClient.IOHandler.WriteLn(AEncoder.Encode(S)); + AClient.GetInternalResponse(AClient.LastCmdCounter, [], True); + if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then + begin + ASASL.FinishAuthenticate; + Exit; + end; + end; + while PosInStrArray(AClient.LastCmdResult.Code, AContinueReplies) > -1 do begin + S := ADecoder.DecodeString(TrimRight(TIdReplyIMAP4(AClient.LastCmdResult).Extra.Text)); + S := ASASL.ContinueAuthenticate(S, AClient.Host, IdGSKSSN_imap); + AClient.IOHandler.WriteLn(AEncoder.Encode(S)); + AClient.GetInternalResponse(AClient.LastCmdCounter, [], True); + if CheckStrFail(AClient.LastCmdResult.Code, AOkReplies, AContinueReplies) then + begin + ASASL.FinishAuthenticate; + Exit; + end; + end; + Result := (PosInStrArray(AClient.LastCmdResult.Code, AOkReplies) > -1); + ASASL.FinishAuthenticate; +end; + +type + {$IFDEF HAS_GENERICS_TList} + TIdSASLList = TList; + {$ELSE} + // TODO: flesh out to match TList for non-Generics compilers + TIdSASLList = TList; + {$ENDIF} + + TIdSASLEntriesIMAP4 = class(TIdSASLEntries) + public + procedure LoginSASL_IMAP(AClient: TIdIMAP4); + end; + +procedure TIdSASLEntriesIMAP4.LoginSASL_IMAP(AClient: TIdIMAP4); +var + i : Integer; + LE : TIdEncoderMIME; + LD : TIdDecoderMIME; + LSupportedSASL : TStrings; + LSASLList: TIdSASLList; + LSASL : TIdSASL; + LError : TIdReply; + + function SetupErrorReply: TIdReply; + begin + Result := TIdReplyClass(AClient.LastCmdResult.ClassType).Create(nil); + Result.Assign(AClient.LastCmdResult); + end; + +begin + // make sure the collection is not empty + CheckIfEmpty; + + //create a list of mechanisms that both parties support + LSASLList := TIdSASLList.Create; + try + LSupportedSASL := TStringList.Create; + try + ParseCapaReplyToList(AClient.FCapabilities, LSupportedSASL, 'AUTH'); {Do not Localize} + for i := Count-1 downto 0 do begin + LSASL := Items[i].SASL; + if LSASL <> nil then begin + if not LSASL.IsAuthProtocolAvailable(LSupportedSASL) then begin + Continue; + end; + if LSASLList.IndexOf(LSASL) = -1 then begin + LSASLList.Add(LSASL); + end; + end; + end; + finally + FreeAndNil(LSupportedSASL); + end; + + if LSASLList.Count = 0 then begin + raise EIdSASLNotSupported.Create(RSSASLNotSupported); + end; + + //now do it + LE := nil; + try + LD := nil; + try + LError := nil; + try + for i := 0 to LSASLList.Count-1 do begin + LSASL := {$IFDEF HAS_GENERICS_TList}LSASLList.Items[i]{$ELSE}TIdSASL(LSASLList.Items[i]){$ENDIF}; + if not LSASL.IsReadyToStart then begin + Continue; + end; + if not Assigned(LE) then begin + LE := TIdEncoderMIME.Create(nil); + end; + if not Assigned(LD) then begin + LD := TIdDecoderMIME.Create(nil); + end; + if PerformSASLLogin_IMAP(LSASL, LE, LD, AClient) then begin + Exit; + end; + if not Assigned(LError) then begin + LError := SetupErrorReply; + end; + end; + if Assigned(LError) then begin + LError.RaiseReplyError; + end else begin + raise EIdSASLNotReady.Create(RSSASLNotReady); + end; + finally + FreeAndNil(LError); + end; + finally + FreeAndNil(LD); + end; + finally + FreeAndNil(LE); + end; + finally + FreeAndNil(LSASLList); + end; +end; + +{ TIdIMAP4WorkHelper } + +type + TIdIMAP4WorkHelper = class(TIdComponent) + protected + fIMAP4: TIdIMAP4; + fOldTarget: TIdComponent; + public + constructor Create(AIMAP4: TIdIMAP4); reintroduce; + destructor Destroy; override; + end; + +constructor TIdIMAP4WorkHelper.Create(AIMAP4: TIdIMAP4); +begin + inherited Create(nil); + fIMAP4 := AIMAP4; + fOldTarget := fIMAP4.WorkTarget; + fIMAP4.WorkTarget := Self; + Self.OnWorkBegin := fIMAP4.BeginWorkForPart; + Self.OnWork := fIMAP4.DoWorkForPart; + Self.OnWorkEnd := fIMAP4.EndWorkForPart; +end; + +destructor TIdIMAP4WorkHelper.Destroy; +begin + fIMAP4.WorkTarget := fOldTarget; + inherited Destroy; +end; + +{ TIdEMUTF7 } + +const + b64Chars : String = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,'; {Do not Localize} + + b64Index : array [0..127] of Integer = ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,63,-1,-1,-1, // 48 + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, // 64 + -1,00,01,02,03,04,05,06,07,08,09,10,11,12,13,14, // 80 + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, // 96 + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, // 112 + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1 // 128 + ); + + b64Table : array[0..127] of Integer = ( + $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, // 16 + $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, $FF,$FF,$FF,$FF, // 32 + $20,$21,$22,$23, $24,$25,$FF,$27, $28,$29,$2A,$2B, $2C,$2D,$2E,$2F, // 48 + $30,$31,$32,$33, $34,$35,$36,$37, $38,$39,$3A,$3B, $3C,$3D,$3E,$3F, // 64 + $40,$41,$42,$43, $44,$45,$46,$47, $48,$49,$4A,$4B, $4C,$4D,$4E,$4F, // 80 + $50,$51,$52,$53, $54,$55,$56,$57, $58,$59,$5A,$5B, $5C,$5D,$5E,$5F, // 96 + $60,$61,$62,$63, $64,$65,$66,$67, $68,$69,$6A,$6B, $6C,$6D,$6E,$6F, // 112 + $70,$71,$72,$73, $74,$75,$76,$77, $78,$79,$7A,$7B, $7C,$7D,$7E,$FF);// 128 + +// TODO: re-write this to derive from IdCoder3To4.pas or IdCoderMIME.pas classes... + +function TIdMUTF7.Encode(const aString: TIdUnicodeString): String; +{ -- MUTF7Encode ------------------------------------------------------------- +PRE: nothing +POST: returns a string encoded as described in IETF RFC 3501, section 5.1.3 + based upon RFC 2152 + + 2004-03-02 roman puls: speed improvements of around 2000 percent due to + replacement of pchar/while loops to delphi-style string/for + loops. Minor changes for '&' handling. Delphi 8 compatible. + 2004-02-29 roman puls: initial version ---} +var + c : Word; + bitBuf : UInt32; + bitShift : Integer; + x : Integer; + escaped : Boolean; + CharToAppend: Char; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + Result := ''; + escaped := False; + bitShift := 0; + bitBuf := 0; + + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create; + {$ENDIF} + + for x := 1 to Length(aString) do begin + c := Word(aString[x]); + // c must be < 128 _and_ in table b64table + if (c <= $7f) and (b64Table[c] <> $FF) or (aString[x] = '&') then begin // we can directly encode that char + if escaped then begin + if (bitShift > 0) then begin // flush bitbuffer if needed + CharToAppend := b64Chars[(bitBuf shl (6 - bitShift) and $3F) + 1]; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); + {$ELSE} + Result := Result + CharToAppend; + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char('-')); // leave escape sequence + {$ELSE} + Result := Result + '-'; // leave escape sequence + {$ENDIF} + escaped := False; + end; + if (aString[x] = '&') then begin // escape special char "&" + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append('&-'); + {$ELSE} + Result := Result + '&-'; + {$ENDIF} + end else begin + CharToAppend := Char(c); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); // store direct translated char + {$ELSE} + Result := Result + CharToAppend; // store direct translated char + {$ENDIF} + end; + end else begin + if not escaped then begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char('&')); + {$ELSE} + Result := Result + '&'; + {$ENDIF} + bitShift := 0; + bitBuf := 0; + escaped := True; + end; + bitbuf := (bitBuf shl 16) or c; // shift and store new bye + Inc(bitShift, 16); + while (bitShift >= 6) do begin // flush buffer as far as we can + Dec(bitShift, 6); + CharToAppend := b64Chars[((bitBuf shr bitShift) and $3F) + 1]; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); + {$ELSE} + Result := Result + CharToAppend; + {$ENDIF} + end; + end; + end; + + // we love duplicate work but must test for flush buffers for the price + // of speed (loop) + if escaped then begin + if (bitShift > 0) then begin + CharToAppend := b64Chars[(bitBuf shl (6 - bitShift) and $3F) + 1]; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); + {$ELSE} + Result := Result + CharToAppend; + {$ENDIF} + end; + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char('-')); + {$ELSE} + Result := Result + '-'; + {$ENDIF} + end; + + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +function TIdMUTF7.Decode(const aString: String): TIdUnicodeString; +{ -- mUTF7Decode ------------------------------------------------------------- +PRE: aString encoding must conform to IETF RFC 3501, section 5.1.3 +POST: SUCCESS: an 8bit string + FAILURE: an exception of type EMUTF7Decode + + 2004-03-02 roman puls: speed improvements of around 400 percent due to + replacement of pchar/while loops to delphi-style string/for + loops. Delphi 8 compatible. + 2004-02-29 roman puls: initial version ---} +const + bitMasks: array[0..4] of UInt32 = ($00000000, $00000001, $00000003, $00000007, $0000000F); +var + ch : Byte; + last : Char; + bitBuf : UInt32; + escaped : Boolean; + x, bitShift: Integer; + CharToAppend: WideChar; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + Result := ''; + escaped := False; + bitShift := 0; + last := #0; + bitBuf := 0; + + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create; + {$ENDIF} + + for x := 1 to Length(aString) do begin + ch := Byte(aString[x]); + if not escaped then begin + if (aString[x] = '&') then begin // escape sequence found + escaped := True; + bitBuf := 0; + bitShift := 0; + last := '&'; + end + else if (ch < $80) and (b64Table[ch] <> $FF) then begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(WideChar(ch)); + {$ELSE} + Result := Result + WideChar(ch); + {$ENDIF} + end else begin + raise EMUTF7Decode.CreateFmt('Illegal char #%d in UTF7 sequence.', [ch]); {do not localize} + end; + end else begin // we're escaped + { break out of escape mode } + if (aString[x] = '-') then begin + // extra check for pending bits + if (last = '&') then begin // special sequence '&-' ? + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(Char('&')); + {$ELSE} + Result := Result + '&'; + {$ENDIF} + end else begin + if (bitShift >= 16) then begin + Dec(bitShift, 16); + CharToAppend := WideChar((bitBuf shr bitShift) and $FFFF); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); + {$ELSE} + Result := Result + CharToAppend; + {$ENDIF} + end; + if (bitShift > 4) or ((bitBuf and bitMasks[bitShift]) <> 0) then begin // check for bitboundaries + raise EMUTF7Decode.Create('Illegal bit sequence in MUTF7 string'); {do not localize} + end; + end; + escaped := False; + end else begin // still escaped + // check range for ch: must be < 128 and in b64table + if (ch >= $80) or (b64Index[ch] = -1) then begin + raise EMUTF7Decode.CreateFmt('Illegal char #%d in UTF7 sequence.', [ch]); {do not localize} + end; + ch := b64Index[ch]; + bitBuf := (bitBuf shl 6) or (ch and $3F); + Inc(bitShift, 6); + if (bitShift >= 16) then begin + Dec(bitShift, 16); + CharToAppend := WideChar((bitBuf shr bitShift) and $FFFF); + {$IFDEF STRING_IS_IMMUTABLE} + LSB.Append(CharToAppend); + {$ELSE} + Result := Result + CharToAppend; + {$ENDIF} + end; + end; + last := #0; + end; + end; + if escaped then begin + raise EmUTF7Decode.Create('Missing unescape in UTF7 sequence.'); {do not localize} + end; + + {$IFDEF STRING_IS_IMMUTABLE} + Result := LSB.ToString; + {$ENDIF} +end; + +function TIdMUTF7.Valid(const aMUTF7String : String): Boolean; +{ -- mUTF7valid ------------------------------------------------------------- +PRE: NIL +POST: returns true if string is correctly encoded (as described in mUTF7Encode) + returns false otherwise +} +begin + try + Result := (aMUTF7String = {mUTF7}Encode({mUTF7}Decode(aMUTF7String))); + except + on e: EmUTF7Error do begin + Result := False; + end; + // do not handle others + end; +end; + +function TIdMUTF7.Append(const aMUTF7String: String; const aStr : TIdUnicodeString): String; +{ -- mUTF7Append ------------------------------------------------------------- +PRE: aMUTF7String is complying to mUTF7Encode's description +POST: SUCCESS: a concatenation of both input strings in mUTF + FAILURE: an exception of EMUTF7Decode or EMUTF7Encode will be raised +} +begin + Result := {mUTF7}Encode({mUTF7}Decode(aMUTF7String) + aStr); +end; + +{ TIdImapMessageParts } + +constructor TIdImapMessagePart.Create(Collection: TCollection); +begin + {Make sure these are initialised properly...} + inherited Create(Collection); + FParentPart := -1; + FBoundary := ''; {Do not Localize} +end; + +constructor TIdImapMessageParts.Create(AOwner: TPersistent); +begin + inherited Create(AOwner, TIdImapMessagePart); +end; + +function TIdImapMessageParts.GetItem(Index: Integer): TIdImapMessagePart; +begin + Result := TIdImapMessagePart(inherited GetItem(Index)); +end; + +function TIdImapMessageParts.Add: TIdImapMessagePart; +begin + Result := TIdImapMessagePart(inherited Add); +end; + +procedure TIdImapMessageParts.SetItem(Index: Integer; const Value: TIdImapMessagePart); +begin + inherited SetItem(Index, Value); +end; + +{ TIdIMAP4 } + +procedure TIdIMAP4.BeginWorkForPart(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64); +begin + if Assigned(FOnWorkBeginForPart) then begin + FOnWorkBeginForPart(Self, AWorkMode, AWorkCountMax); + end; +end; + +procedure TIdIMAP4.DoWorkForPart(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); +begin + if Assigned(FOnWorkForPart) then begin + FOnWorkForPart(Self, AWorkMode, AWorkCount); + end; +end; + +procedure TIdIMAP4.EndWorkForPart(ASender: TObject; AWorkMode: TWorkMode); +begin + if Assigned(FOnWorkEndForPart) then begin + FOnWorkEndForPart(Self, AWorkMode); + end; +end; + +//The following call FMUTF7 but do exception-handling on invalid strings... +function TIdIMAP4.DoMUTFEncode(const aString : String): String; +begin + // TODO: if the server advertises the "UTF8=ACCEPT" capability, use + // a UTF-8 quoted string instead of IMAP's Modified UTF-7... + try + Result := FMUTF7.Encode( + {$IFDEF STRING_IS_UNICODE} + aString + {$ELSE} + TIdUnicodeString(aString) // explicit convert to Unicode + {$ENDIF} + ); + except + Result := aString; + end; +end; + +function TIdIMAP4.DoMUTFDecode(const aString : String): String; +begin + try + {$IFDEF STRING_IS_UNICODE} + Result := FMUTF7.Decode(aString); + {$ELSE} + Result := String(FMUTF7.Decode(aString)); // explicit convert to Ansi + {$ENDIF} + except + Result := aString; + end; +end; + +function TIdIMAP4.GetReplyClass:TIdReplyClass; +begin + Result := TIdReplyIMAP4; +end; + +function TIdIMAP4.GetSupportsTLS: Boolean; +begin + Result := IsCapabilityListed('STARTTLS'); //do not localize +end; + +function TIdIMAP4.CheckConnectionState(AAllowedState: TIdIMAP4ConnectionState): TIdIMAP4ConnectionState; +begin + Result := CheckConnectionState([AAllowedState]); +end; + +function TIdIMAP4.CheckConnectionState(const AAllowedStates: array of TIdIMAP4ConnectionState): TIdIMAP4ConnectionState; +var + i: integer; +begin + if High(AAllowedStates) > -1 then begin + for i := Low(AAllowedStates) to High(AAllowedStates) do begin + if FConnectionState = AAllowedStates[i] then begin + Result := FConnectionState; + Exit; + end; + end; + end; + raise EIdConnectionStateError.CreateFmt(RSIMAP4ConnectionStateError, [GetConnectionStateName]); +end; + +function TIdIMAP4.CheckReplyForCapabilities: Boolean; +var + I: Integer; + LExtra: TStrings; +begin + FCapabilities.Clear; + FHasCapa := False; + LExtra := TIdReplyIMAP4(FLastCmdResult).Extra; + for I := 0 to LExtra.Count-1 do begin + if TextStartsWith(LExtra.Strings[I], 'CAPABILITY ') then begin {Do not Localize} + BreakApart(LExtra.Strings[I], ' ', FCapabilities); {Do not Localize} + // RLebeau: do not delete the first item anymore! It specifies the IMAP + // version/revision, which is needed to support certain extensions, like + // 'IMAP4rev1'... + {FCapabilities.Delete(0);} + FHasCapa := True; + Break; + end; + end; + Result := FHasCapa; +end; + +function TIdIMAP4.FindHowServerCreatesFolders: TIdIMAP4FolderTreatment; +var + LUsersFolders: TStringList; + LN: integer; + LInbox: string; + LTestFolder: string; +begin + LUsersFolders := TStringList.Create; + try + {$IFDEF HAS_TStringList_CaseSensitive} + LUsersFolders.CaseSensitive := False; + {$ENDIF} + //Get folder names... + if (not ListMailBoxes(LUsersFolders)) or (LUsersFolders.Count = 0) then begin + Result := ftCannotRetrieveAnyFolders; + Exit; + end; + + //Do we have an Inbox? + LN := IndyIndexOf(LUsersFolders, 'INBOX'); {Do not Localize} + if LN = -1 then begin + Result := ftCannotTestBecauseHasNoInbox; + Exit; + end; + LInbox := LUsersFolders.Strings[LN]; + + //Make sure our test folder does not already exist at the top level... + LTestFolder := 'CiaransTestFolder'; {Do not Localize} + while IndyIndexOf(LUsersFolders, LTestFolder) <> -1 do begin + LTestFolder := LTestFolder + '9'; {Do not Localize} + end; + + //Try to create LTestFolder at the top level... + if CreateMailbox(LTestFolder) then begin + //We were able to create it at the top level - delete it and exit.. + DeleteMailbox(LTestFolder); + Result := ftAllowsTopLevelCreation; + Exit; + end; + + //See if our test folder does not exist under INBOX... + LTestFolder := LInbox + FMailBoxSeparator + 'CiaransTestFolder'; {Do not Localize} + while IndyIndexOf(LUsersFolders, LTestFolder) <> -1 do begin + LTestFolder := LTestFolder + '9'; {Do not Localize} + end; + + //Try to create LTestFolder under Inbox... + if CreateMailbox(LTestFolder) then begin + //We were able to create it under the top level - delete it and exit.. + DeleteMailbox(LTestFolder); + Result := ftFoldersMustBeUnderInbox; + Exit; + end; + + //It does not allow us create folders under any level (read-only?)... + Result := ftDoesNotAllowFolderCreation; + finally + FreeAndNil(LUsersFolders); + end; +end; + +function TIdIMAP4.IsNumberValid(const ANumber: Integer): Boolean; + {CC3: Need to validate message numbers (relative and UIDs), because otherwise + the routines wait for a response that never arrives and so functions never return.} +begin + if ANumber < 1 then begin + raise EIdNumberInvalid.Create(RSIMAP4NumberInvalid); + end; + Result := True; +end; + +function TIdIMAP4.IsUIDValid(const AUID: string): Boolean; + {CC3: Need to validate message numbers (relative and UIDs), because otherwise + the routines wait for a response that never arrives and so functions never return.} +begin + //Must be digits only (no - or .) + IsItDigitsAndOptionallyPeriod(AUID, False); + Result := IsNumberValid(IndyStrToInt(AUID)); +end; + +function TIdIMAP4.IsImapPartNumberValid(const AUID: string): Boolean; + {CC3: IMAP part numbers are 3 or 4.5 etc, i.e. digits or period allowed} +begin + Result := IsItDigitsAndOptionallyPeriod(AUID, True); +end; + +function TIdIMAP4.IsItDigitsAndOptionallyPeriod(const AStr: string; AAllowPeriod: Boolean): Boolean; +var + LN: integer; +begin + if Length(AStr) = 0 then begin + raise EIdNumberInvalid.Create(RSIMAP4NumberInvalid); + end; + for LN := 1 to Length(AStr) do begin + if not IsNumeric(AStr[LN]) then begin + if (not AAllowPeriod) or (AStr[LN] <> '.') then begin {Do not Localize} + raise EIdNumberInvalid.Create(RSIMAP4NumberInvalid); + end; + end; + end; + Result := True; +end; + +function TIdIMAP4.GetUID(const AMsgNum: Integer; var AUID: string): Boolean; +{This gets the message UID from the message relative number.} +begin + Result := False; + AUID := ''; {Do not Localize} + IsNumberValid(AMsgNum); + + CheckConnectionState(csSelected); + {Some servers return NO if the requested message number is not present + (e.g. Cyrus), others return OK but no data (CommuniGate).} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdUID] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + //Might as well leave 3rd param as [] because ParseLastCmdResult always grabs the UID... + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], []) then begin + AUID := FLineStruct.UID; + Result := True; + end; + end; + end; +end; + +{$I IdDeprecatedImplBugOff.inc} +procedure TIdIMAP4.WriteLn(const AOut: string = ''); +{$I IdDeprecatedImplBugOn.inc} +begin + IOHandler.WriteLn(AOut); +end; + +{$I IdDeprecatedImplBugOff.inc} +function TIdIMAP4.ReadLnWait: string; +{$I IdDeprecatedImplBugOn.inc} +begin + Result := IOHandler.ReadLnWait; {This can have hit an exception of Connection Reset By Peer (timeout)} +end; + +{ IdTCPConnection Commands... } + +function TIdIMAP4.GetInternalResponse(const ATag: String; AExpectedResponses: array of String; + ASingleLineMode: Boolean; ASingleLineMayBeSplit: Boolean {= True}): string; +{ASingleLineMode is True if the caller just wants the FIRST line of the response, +e.g., he may be looking only for "* FETCH (blah blah)", because he needs to parse +that line to figure out how the rest will follow. This arises with a number of the +FETCH commands where the caller needs to get the byte-count from the first line +before he can retrieve the rest of the response. +Note "FETCH" would have to be in AExpectedResponses. +When False, the caller wants everything up to and including the reply terminator +(e.g. "C45 OK Completed"). +In ASingleLineMode, we ignore any lines that dont have one of AExpectedResponses +at the start, otherwise we add all lines to .Text and later strip out any lines that +dont have one of AExpectedResponses at the start. +ASingleLineMayBeSplit (which should only be used with ASingleLineMode = True) deals +with the case where the server cannot or does not fit a single-line +response onto one line. This arises when FETCHing the BODYSTRUCTURE, which can +be very long. The server (Courier, anyway) signals it by adding a byte-count to +the end of the first line, that would not normally be present.} +//For example, for normal short responses, the server would send: +// * FETCH (BODYSTRUCTURE (Part1 Part2)) +//but if it splits it, it sends: +// * FETCH (BODYSTRUCTURE (Part1 {7} +// Part2)) +//The number in the curly brackets {7} is the byte count following the line break. +{WARNING: If you use ASingleLineMayBeSplit on a line that is EXPECTED to end +with a byte-count, the code will break, so don't use it unless absolutely +necessary.} +var + LLine: String; + LResponse: TStringList; + LWord: string; + LPos: integer; + LStrippedLineLength: Integer; + LGotALineWithAnExpectedResponse: Boolean; + LStrippedLine: string; + LSplitLine: string; +begin + LGotALineWithAnExpectedResponse := False; + LResponse := TStringList.Create; + try + repeat + LLine := IOHandler.ReadLnWait; + {CCB: Trap case of server telling you that you have been disconnected, usually because + you were inactive for too long (get "* BYE idle time too long"). } + if TextStartsWith(LLine, '* BYE') then begin {Do not Localize} + {If BYE is in AExpectedResponses, this means we are expecting to + disconnect, i.e. it is a LOGOUT.} + if PosInStrArray('BYE', AExpectedResponses) = -1 then begin {Do not Localize} + {We were not expecting a BYE response. + For the moment, throw an exception. Could modify this by adding a + ReconnectOnDisconnect property to automatically reconnect?} + FConnectionState := csUnexpectedlyDisconnected; + raise EIdDisconnectedProbablyIdledOut.Create(RSIMAP4DisconnectedProbablyIdledOut); + end; + end; + if ASingleLineMode then begin + //See if it may continue on the next line... + if ASingleLineMayBeSplit then begin + //If the line is split, it will have a byte-count field at the end... + if TextEndsWith(LLine, '}') then begin + //It is split. + LStrippedLine := LLine; + LLine := ''; + repeat + //First, remove the byte count... + LPos := Length(LStrippedLine)-1; + while LPos >= 1 do begin + if LStrippedLine[LPos] = '{' then begin + Break; + end; + Dec(LPos); + end; + LWord := Copy(LStrippedLine, LPos+1, (Length(LStrippedLine)-LPos)-1); + if TextIsSame(LWord, 'NIL') then begin + LStrippedLineLength := 0; + end else begin + LStrippedLineLength := StrToInt(LWord); + end; + LStrippedLine := Copy(LStrippedLine, 1, LPos-1); + //The rest of the reply is on the following line... + LSplitLine := IOHandler.ReadString(LStrippedLineLength); + // At this point LSplitLine should be parsed and the following characters should be escaped... " CR LF. + LLine := LLine + LStrippedLine + LSplitLine; + LStrippedLine := IOHandler.ReadLn; //Cannot thrash LLine, need it later + until not TextEndsWith(LStrippedLine, '}'); + LLine := LLine + LStrippedLine; + end; + end; + LStrippedLine := LLine; + if TextStartsWith(LLine, '* ') then begin {Do not Localize} + LStrippedLine := Copy(LLine, 3, MaxInt); + end; + LGotALineWithAnExpectedResponse := TIdReplyIMAP4(FLastCmdResult).DoesLineHaveExpectedResponse(LStrippedLine, AExpectedResponses); + if LGotALineWithAnExpectedResponse then begin + FLastCmdResult.Text.Clear; + TIdReplyIMAP4(FLastCmdResult).Extra.Clear; + FLastCmdResult.Text.Add(LStrippedLine); + end; + end else + begin + //If the line is split, it will have a byte-count field at the end... + if TextEndsWith(LLine, '}') then begin + LStrippedLine := LLine; + LLine := ''; + repeat + //It is split. + //First, remove the byte count... + LPos := Length(LStrippedLine)-1; + while LPos >= 1 do begin + if LStrippedLine[LPos] = '{' then begin + Break; + end; + Dec(LPos); + end; + LWord := Copy(LStrippedLine, LPos+1, (Length(LStrippedLine)-LPos)-1); + if TextIsSame(LWord, 'NIL') then begin + LStrippedLineLength := 0; + end else begin + LStrippedLineLength := StrToInt(LWord); + end; + LStrippedLine := Copy(LStrippedLine, 1, LPos-1); + //The rest of the reply is on the following line... + LSplitLine := IOHandler.ReadString(LStrippedLineLength); + // At this point LSplitLine should be parsed and the following characters should be escaped... " CR LF. + LLine := LLine + LStrippedLine + LSplitLine; + LStrippedLine := IOHandler.ReadLn; //Cannot thrash LLine, need it later + until not TextEndsWith(LStrippedLine, '}'); + LLine := LLine + LStrippedLine; + end; + end; + LResponse.Add(LLine); + //Need to get the 1st word on the line in case it is +, PREAUTH, etc... + LPos := Pos(' ', LLine); {Do not Localize} + if LPos <> 0 then begin + {There are at least two words on this line...} + LWord := Trim(Copy(LLine, 1, LPos-1)); + end else begin + {No space, so this line is a single word. A bit weird, but it + could be just an OK...} + LWord := LLine; {A bit pedantic, but emphasises we have a word, not a line} + end; + until + TextStartsWith(LLine, ATag) + or (PosInStrArray(LWord, VALID_TAGGEDREPLIES) <> -1) + or LGotALineWithAnExpectedResponse; + if LGotALineWithAnExpectedResponse then begin + //This only arises if ASingleLineMode is True... + FLastCmdResult.Code := IMAP_OK; + end else begin + FLastCmdResult.FormattedReply := LResponse; + TIdReplyIMAP4(FLastCmdResult).RemoveUnsolicitedResponses(AExpectedResponses); + end; + Result := FLastCmdResult.Code; + finally + FreeAndNil(LResponse); + end; +end; + +function TIdIMAP4.SendCmd(const AOut: string; AExpectedResponses: array of String; + ASingleLineMode: Boolean = False; ASingleLineMayBeSplit: Boolean = True): string; +begin + Result := SendCmd(NewCmdCounter, AOut, AExpectedResponses, ASingleLineMode, ASingleLineMayBeSplit); +end; + +function TIdIMAP4.SendCmd(const ATag, AOut: string; AExpectedResponses: array of String; + ASingleLineMode: Boolean = False; ASingleLineMayBeSplit: Boolean = True): string; +var + LCmd: String; +begin + {CC3: Catch "Connection reset by peer"...} + try + if (AOut <> #0) then begin + //Remove anything that may be unprocessed from a previous (probably failed) command... + repeat + IOHandler.InputBuffer.Clear; + until not IOHandler.CheckForDataOnSource(MilliSecsToWaitToClearBuffer); + LCmd := ATag + ' ' + AOut; + CheckConnected; + PrepareCmd(LCmd); + IOHandler.WriteLn(LCmd); + end; + Result := GetInternalResponse(ATag, AExpectedResponses, ASingleLineMode, ASingleLineMayBeSplit); + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; +end; + +{ ...IdTCPConnection Commands } + +procedure TIdIMAP4.DoAlert(const AMsg: String); +begin + if Assigned(OnAlert) then begin + OnAlert(Self, AMsg); + end; +end; + +procedure TIdIMAP4.SetMailBox(const Value: TIdMailBox); +begin + FMailBox.Assign(Value); +end; + +procedure TIdIMAP4.SetSASLMechanisms(AValue: TIdSASLEntries); +begin + FSASLMechanisms.Assign(AValue); +end; + +procedure TIdIMAP4.Login; +var + LIO: TIdSSLIOHandlerSocketBase; +begin + try + if (IOHandler is TIdSSLIOHandlerSocketBase) and (UseTLS in ExplicitTLSVals) then begin + LIO := TIdSSLIOHandlerSocketBase(IOHandler); + //we check passthrough because we can either be using TLS currently with + //implicit TLS support or because STARTLS was issued previously. + if LIO.PassThrough then begin + if SupportsTLS then begin + if SendCmd(NewCmdCounter, 'STARTTLS', []) = IMAP_OK then begin {Do not Localize} + TLSHandshake; + //obtain capabilities again - RFC2595 + Capability; + end else begin + ProcessTLSNegCmdFailed; + end; + end else begin + ProcessTLSNotAvail; + end; + end; + end; + FConnectionState := csNonAuthenticated; + FCmdCounter := 0; + if FAuthType = iatUserPass then begin + if Length(Password) <> 0 then begin {Do not Localize} + SendCmd(NewCmdCounter, IMAP4Commands[cmdLogin] + ' ' + Username + ' ' + IMAPQuotedStr(Password), [IMAP_OK]); {Do not Localize} + end else begin + SendCmd(NewCmdCounter, IMAP4Commands[cmdLogin] + ' ' + Username, [IMAP_OK]); {Do not Localize} + end; + if LastCmdResult.Code <> IMAP_OK then begin + RaiseExceptionForLastCmdResult; + end; + end else + begin + if not FHasCapa then begin + Capability; + end; + // FSASLMechanisms.LoginSASL('AUTHENTICATE', FHost, IdGSKSSN_imap, [IMAP_OK], [IMAP_CONT], Self, FCapabilities, 'AUTH', IsCapabilityListed('SASL-IR')); {Do not Localize} + TIdSASLEntriesIMAP4(FSASLMechanisms).LoginSASL_IMAP(Self); + end; + FConnectionState := csAuthenticated; + // RLebeau: check if the response includes new Capabilities, if not then query for them... + if not CheckReplyForCapabilities then begin + Capability; + end; + except + Disconnect; + raise; + end; +end; + +function TIdIMAP4.Connect(const AAutoLogin: Boolean = True): Boolean; +begin + {CC2: Need to set FConnectionState to csNonAuthenticated here. If not, then + an unsuccessful connect after a previous successful connect (such as when a + client program changes users) can leave it as csAuthenticated.} + FConnectionState := csNonAuthenticated; + try + {CC2: Don't call Connect if already connected, this could be just a change of user} + if not Connected then begin + inherited Connect; + GetResponse; + // if PosInStrArray(LastCmdResult.Code, [IMAP_OK, IMAP_PREAUTH]) = -1 then begin + {Should have got OK or PREAUTH in the greeting. Happened with some server, + may need further investigation and coding...} + // end; + {CC7: Save FGreetingBanner so the user can use it to determine what type of + server he is connected to...} + if LastCmdResult.Text.Count > 0 then begin + FGreetingBanner := LastCmdResult.Text[0]; + end else begin + FGreetingBanner := ''; + end; + if LastCmdResult.Code = IMAP_PREAUTH then begin + FConnectionState := csAuthenticated; + FCmdCounter := 0; + // RLebeau: check if the greeting includes initial Capabilities, if not then query for them... + if not CheckReplyForCapabilities then begin + Capability; + end; + end else begin + // RLebeau: check if the greeting includes initial Capabilities... + CheckReplyForCapabilities; + end; + end; + if AAutoLogin then begin + Login; + end; + except + Disconnect(False); + raise; + end; + Result := True; +end; + +procedure TIdIMAP4.InitComponent; +begin + inherited InitComponent; + FMailBox := TIdMailBox.Create(Self); + //FSASLMechanisms := TIdSASLEntries.Create(Self); + FSASLMechanisms := TIdSASLEntriesIMAP4.Create(Self); + Port := IdPORT_IMAP4; + FLineStruct := TIdIMAPLineStruct.Create; + FCapabilities := TStringList.Create; + {$IFDEF HAS_TStringList_CaseSensitive} + TStringList(FCapabilities).CaseSensitive := False; + {$ENDIF} + FMUTF7 := TIdMUTF7.Create; + + //Todo: Not sure which number is appropriate. Should be tested further. + FImplicitTLSProtPort := IdPORT_IMAP4S; + FRegularProtPort := IdPORT_IMAP4; + + FMilliSecsToWaitToClearBuffer := IDF_DEFAULT_MS_TO_WAIT_TO_CLEAR_BUFFER; + FCmdCounter := 0; + FConnectionState := csNonAuthenticated; + FRetrieveOnSelect := rsDisabled; + {CC2: FMailBoxSeparator is now detected when a mailbox is selected, following + line is probably redundant, but leave it here as a default just in case.} + FMailBoxSeparator := '/'; {Do not Localize} +end; + +procedure TIdIMAP4.Disconnect(ANotifyPeer: Boolean); +begin + try + inherited Disconnect(ANotifyPeer); + finally + FConnectionState := csNonAuthenticated; + FCapabilities.Clear; + end; +end; + +procedure TIdIMAP4.DisconnectNotifyPeer; +begin + inherited DisconnectNotifyPeer; + //IMPORTANT: Logout must pass 'BYE' as the first + //element of the AExpectedResponses array (the 3rd param in SendCmd + //below), because this flags to GetInternalResponse that this is the + //logout, and it must EXPECT the BYE response + SendCmd(NewCmdCounter, IMAP4Commands[cmdLogout], ['BYE']); {Do not Localize} +end; + +procedure TIdIMAP4.KeepAlive; +begin + //Avialable in any state. + SendCmd(NewCmdCounter, IMAP4Commands[cmdNoop], []); +end; + +function TIdIMAP4.IsCapabilityListed(ACapability: string):Boolean; +begin + if not FHasCapa then begin + Capability; + end; + Result := IndyIndexOf(TStringList(FCapabilities), ACapability) <> -1; +end; + +function TIdIMAP4.Capability: Boolean; +begin + FHasCapa := Capability(FCapabilities); + Result := FHasCapa; +end; + +function TIdIMAP4.Capability(ASlCapability: TStrings): Boolean; +begin + //Available in any state. + Result := False; + ASlCapability.Clear; + SendCmd(NewCmdCounter, IMAP4Commands[CmdCapability], [IMAP4Commands[CmdCapability]]); + if LastCmdResult.Code = IMAP_OK then begin + if LastCmdResult.Text.Count > 0 then begin + BreakApart(LastCmdResult.Text[0], ' ', ASlCapability); {Do not Localize} + end; + // RLebeau: do not delete the first item anymore! It specifies the IMAP + // version/revision, which is needed to support certain extensions, like + // 'IMAP4rev1'... + { + if ASlCapability.Count > 0 then begin + ASlCapability.Delete(0); + end; + } + Result := True; + end; +end; + +function TIdIMAP4.GetCmdCounter: String; +begin + Result := 'C' + IntToStr(FCmdCounter); {Do not Localize} +end; + +function TIdIMAP4.GetNewCmdCounter: String; +begin + Inc(FCmdCounter); + Result := 'C' + IntToStr(FCmdCounter); {Do not Localize} +end; + +destructor TIdIMAP4.Destroy; +begin + {Disconnect before we die} + { Note we have to pass false to an overloaded method or an exception is + raised in the destructor. That can cause weirdness in the IDE. } + if Connected then begin + Disconnect(False); + end; + FreeAndNil(FMailBox); + FreeAndNil(FSASLMechanisms); + FreeAndNil(FLineStruct); + FreeAndNil(FCapabilities); + FreeAndNil(FMUTF7); + inherited Destroy; +end; + +function TIdIMAP4.SelectMailBox(const AMBName: String): Boolean; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdSelect] + ' "' + DoMUTFEncode(AMBName) + '"', {Do not Localize} + ['FLAGS', 'OK', 'EXISTS', 'RECENT', '[READ-WRITE]', '[ALERT]']); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + //Put the parse in the IMAP Class and send the MB; + ParseSelectResult(FMailBox, LastCmdResult.Text); + FMailBox.Name := AMBName; + FConnectionState := csSelected; + case RetrieveOnSelect of + rsHeaders: RetrieveAllHeaders(FMailBox.MessageList); + rsMessages: RetrieveAllMsgs(FMailBox.MessageList); + end; + Result := True; + end; +end; + +function TIdIMAP4.ExamineMailBox(const AMBName: String; AMB: TIdMailBox): Boolean; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + //TO DO: Check that Examine's expected responses really are STATUS, FLAGS and OK... + SendCmd(NewCmdCounter, + IMAP4Commands[cmdExamine] + ' "' + DoMUTFEncode(AMBName) + '"', {Do not Localize} + ['STATUS', 'FLAGS', 'OK', 'EXISTS', 'RECENT', '[READ-WRITE]', '[ALERT]']); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + ParseSelectResult(AMB, LastCmdResult.Text); + AMB.Name := AMBName; + FConnectionState := csSelected; + Result := True; + end; +end; + +function TIdIMAP4.CloseMailBox: Boolean; +begin + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdClose], []); + if LastCmdResult.Code = IMAP_OK then begin + MailBox.Clear; + FConnectionState := csAuthenticated; + Result := True; + end; +end; + +function TIdIMAP4.CreateMailBox(const AMBName: String): Boolean; +begin + {CC5: Recode to return False if NO returned rather than throwing an exception...} + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + {CC5: The NO response is typically due to Permission Denied} + SendCmd(NewCmdCounter, IMAP4Commands[cmdCreate] + ' "' + DoMUTFEncode(AMBName) + '"', []); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.DeleteMailBox(const AMBName: String): Boolean; +begin + {CC5: Recode to return False if NO returned rather than throwing an exception...} + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + {CC5: The NO response is typically due to Permission Denied} + SendCmd(NewCmdCounter, IMAP4Commands[cmdDelete] + ' "' + DoMUTFEncode(AMBName) + '"', []); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.RenameMailBox(const AOldMBName, ANewMBName: String): Boolean; +begin + {CC5: Recode to return False if NO returned rather than throwing an exception...} + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + {CC5: The NO response is typically due to Permission Denied} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdRename] + ' "' + DoMUTFEncode(AOldMBName) + '" "' + DoMUTFEncode(ANewMBName) + '"', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.StatusMailBox(const AMBName: String; AMB: TIdMailBox): Boolean; + {CC2: It is pointless calling StatusMailBox with AStatusDataItems set to [] + because you are asking the IMAP server to update none of the status flags. + Instead, if called with no AStatusDataItems specified, use the standard flags + returned by SelectMailBox, which allows the user to easily check if the mailbox + has changed. Overload the functions, since AStatusDataItems cannot be set + to nil.} +var + AStatusDataItems: array[1..5] of TIdIMAP4StatusDataItem; +begin + AStatusDataItems[1] := mdMessages; + AStatusDataItems[2] := mdRecent; + AStatusDataItems[3] := mdUIDNext; + AStatusDataItems[4] := mdUIDValidity; + AStatusDataItems[5] := mdUnseen; + Result := StatusMailBox(AMBName, AMB, AStatusDataItems); +end; + +function TIdIMAP4.StatusMailBox(const AMBName: String; AMB: TIdMailBox; const AStatusDataItems: array of TIdIMAP4StatusDataItem): Boolean; +var + LDataItems : string; + Ln : Integer; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + for Ln := Low(AStatusDataItems) to High(AStatusDataItems) do begin + case AStatusDataItems[Ln] of + mdMessages: LDataItems := LDataItems + IMAP4StatusDataItem[mdMessages] + ' '; {Do not Localize} + mdRecent: LDataItems := LDataItems + IMAP4StatusDataItem[mdRecent] + ' '; {Do not Localize} + mdUIDNext: LDataItems := LDataItems + IMAP4StatusDataItem[mdUIDNext] + ' '; {Do not Localize} + mdUIDValidity: LDataItems := LDataItems + IMAP4StatusDataItem[mdUIDValidity] + ' '; {Do not Localize} + mdUnseen: LDataItems := LDataItems + IMAP4StatusDataItem[mdUnseen] + ' '; {Do not Localize} + end; + end; + SendCmd(NewCmdCounter, + IMAP4Commands[cmdStatus] + ' "' + DoMUTFEncode(AMBName) + '" (' + Trim(LDataItems) + ')', {Do not Localize} + [IMAP4Commands[cmdStatus]]); + if LastCmdResult.Code = IMAP_OK then begin + ParseStatusResult(AMB, LastCmdResult.Text); + Result := True; + end; +end; + +function TIdIMAP4.CheckMailBox: Boolean; +begin + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdCheck], []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.ExpungeMailBox: Boolean; +begin + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdExpunge], []); + if LastCmdResult.Code = IMAP_OK then begin + ParseExpungeResult(FMailBox, LastCmdResult.Text); + Result := True; + end; +end; + +//This function is needed because when using the regular DateToStr with dd/MMM/yyyy +//(which is the IMAP needed convension) may give the month as the local language +//three letter month instead of the English month needed. +function DateToIMAPDateStr (const ADate: TDateTime): String; +var + LDay, LMonth, LYear : Word; +begin + {Do not use the global settings from the system unit here because: + 1) It might not be thread safe + 2) Changing the settings could create problems for a user who's local date conventions + are diffrent than dd-mm-yyyy. Some people prefer mm-dd-yyy. Don't mess with a user's display settings. + 3) Using the display settings for dates may not always work as expected if a user + changes their settings at a time between whn you do it but before the date is formatted. + } + DecodeDate(ADate, LYear, LMonth, LDay); + Result := IndyFormat('%.2d-%s-%.4d', [LDay, UpperCase(monthnames[LMonth]), LYear]); {Do not Localize} +end; + +function TIdIMAP4.InternalSearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; + AUseUID: Boolean; const ACharSet: string): Boolean; +var + LCmd: String; + Ln : Integer; + LTextBuf: TIdBytes; + LCharSet: string; + LEncoding: IIdTextEncoding; + LLiteral: string; + LUseNonSyncLiteral: Boolean; + LUseUTF8QuotedString: Boolean; + + function RequiresEncoding(const S: String): Boolean; + var + I: Integer; + begin + Result := False; + for I := 1 to Length(S) do begin + if Ord(S[I]) > $7F then begin + Result := True; + Exit; + end; + end; + end; + + function IsCharsetNeeded: Boolean; + var + I : Integer; + begin + Result := False; + for I := Low(ASearchInfo) to High(ASearchInfo) do begin + case ASearchInfo[I].SearchKey of + skBcc, + skBody, + skCc, + skFrom, + skHeader, + skSubject, + skText, + skTo, + skGmailRaw, + skGmailMsgID, + skGmailThreadID, + skGmailLabels: + if RequiresEncoding(ASearchInfo[I].Text) then begin + Result := True; + Exit; + end; + end; + end; + end; + +begin + Result := False; + LTextBuf := nil; // keep the compiler happy + CheckConnectionState(csSelected); + + LCmd := NewCmdCounter + ' '; {Do not Localize} + if AUseUID then begin + LCmd := LCmd + IMAP4Commands[cmdUID] + ' '; {Do not Localize} + end; + LCmd := LCmd + IMAP4Commands[cmdSearch]; + if IsCharsetNeeded then begin + LUseNonSyncLiteral := IsCapabilityListed('LITERAL+'); {Do not localize} + LUseUTF8QuotedString := IsCapabilityListed('UTF8=ACCEPT') or {Do not localize} + IsCapabilityListed('UTF8=ONLY') or {Do not localize} + IsCapabilityListed('UTF8=ALL'); {Do not localize} + if LUseUTF8QuotedString then begin + LCharSet := 'UTF-8'; {Do not Localize} + end else begin + LCharSet := Trim(ACharSet); + if LCharSet = '' then begin + LCharSet := 'UTF-8'; {Do not Localize} + end; + end; + LCmd := LCmd + ' CHARSET ' + LCharSet; {Do not localize} + LEncoding := CharsetToEncoding(LCharSet); + end else begin + LUseNonSyncLiteral := False; + LUseUTF8QuotedString := False; + end; + + {CC3: Catch "Connection reset by peer"...} + try + //Remove anything that may be unprocessed from a previous (probably failed) command... + repeat + IOHandler.InputBuffer.Clear; + until not IOHandler.CheckForDataOnSource(MilliSecsToWaitToClearBuffer); + CheckConnected; + //IMAP.PrepareCmd(LCmd); + + // now encode the search values. Most values are ASCII and do not need + // special encoding. For text values that do need to be encoded, IMAP + // string literals have to be used in order to support 8-bit octets in + // charset encoded payloads... + for Ln := Low(ASearchInfo) to High(ASearchInfo) do begin + case ASearchInfo[Ln].SearchKey of + skAll, + skAnswered, + skDeleted, + skDraft, + skFlagged, + skNew, + skNot, + skOld, + skOr, + skRecent, + skSeen, + skUnanswered, + skUndeleted, + skUndraft, + skUnflagged, + skUnKeyWord, + skUnseen: + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey]; {Do not Localize} + + skHeader: + begin + // TODO: support RFC 5738 to allow for UTF-8 encoded quoted strings + if not RequiresEncoding(ASearchInfo[Ln].Text) then begin + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + ASearchInfo[Ln].FieldName + ' ' + IMAPQuotedStr(ASearchInfo[Ln].Text); {Do not Localize} + end else + begin + if LUseUTF8QuotedString then begin + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + ASearchInfo[Ln].FieldName + ' *'; {Do not Localize} + IOHandler.Write(LCmd); + IOHandler.Write(IMAPQuotedStr(ASearchInfo[Ln].Text), LEncoding{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_OSDefault{$ENDIF}); + end else + begin + LTextBuf := ToBytes(ASearchInfo[Ln].Text, LEncoding{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_OSDefault{$ENDIF}); + if LUseNonSyncLiteral then begin + LLiteral := '{' + IntToStr(Length(LTextBuf)) + '+}'; {Do not Localize} + end else begin + LLiteral := '{' + IntToStr(Length(LTextBuf)) + '}'; {Do not Localize} + end; + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + ASearchInfo[Ln].FieldName + ' ' + LLiteral; {Do not Localize} + IOHandler.WriteLn(LCmd); + if not LUseNonSyncLiteral then begin + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdSearch], IMAP4Commands[cmdUID]], False) <> IMAP_CONT then begin + RaiseExceptionForLastCmdResult; + end; + end; + IOHandler.Write(LTextBuf); + end; + LTextBuf := nil; + LCmd := ''; + end; + end; + + skKeyword, + skUID: + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + ASearchInfo[Ln].Text; {Do not Localize} + + skBcc, + skBody, + skCc, + skFrom, + skSubject, + skText, + skTo, + skGmailRaw, + skGmailMsgID, + skGmailThreadID, + skGmailLabels: + begin + // TODO: support RFC 5738 to allow for UTF-8 encoded quoted strings + if not RequiresEncoding(ASearchInfo[Ln].Text) then begin + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + IMAPQuotedStr(ASearchInfo[Ln].Text); {Do not Localize} + end else + begin + if LUseUTF8QuotedString then begin + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' *'; {Do not Localize} + IOHandler.Write(LCmd); + IOHandler.Write(IMAPQuotedStr(ASearchInfo[Ln].Text), LEncoding{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_OSDefault{$ENDIF}); + end else + begin + LTextBuf := ToBytes(ASearchInfo[Ln].Text, LEncoding{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_OSDefault{$ENDIF}); + if LUseNonSyncLiteral then begin + LLiteral := '{' + IntToStr(Length(LTextBuf)) + '+}'; {Do not Localize} + end else begin + LLiteral := '{' + IntToStr(Length(LTextBuf)) + '}'; {Do not Localize} + end; + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + LLiteral; {Do not Localize} + IOHandler.WriteLn(LCmd); + if not LUseNonSyncLiteral then begin + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdSearch], IMAP4Commands[cmdUID]], False) <> IMAP_CONT then begin + RaiseExceptionForLastCmdResult; + end; + end; + IOHandler.Write(LTextBuf); + end; + LTextBuf := nil; + LCmd := ''; + end; + end; + + skBefore, + skOn, + skSentBefore, + skSentOn, + skSentSince, + skSince: + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + DateToIMAPDateStr(ASearchInfo[Ln].Date); {Do not Localize} + + skLarger, + skSmaller: + LCmd := LCmd + ' ' + IMAP4SearchKeys[ASearchInfo[Ln].SearchKey] + ' ' + IntToStr(ASearchInfo[Ln].Size); {Do not Localize} + end; + end; + + if LCmd <> '' then begin + IOHandler.Write(LCmd); + end; + + // After we send the last of the data, we need to send an EXTRA CRLF to terminates the SEARCH command... + IOHandler.WriteLn; + + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdSearch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + ParseSearchResult(FMailBox, LastCmdResult.Text); + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; +end; + +function TIdIMAP4.SearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; + const ACharSet: string = ''): Boolean; +begin + Result := InternalSearchMailBox(ASearchInfo, False, ACharSet); +end; + +function TIdIMAP4.UIDSearchMailBox(const ASearchInfo: array of TIdIMAP4SearchRec; + const ACharSet: string = '') : Boolean; +begin + Result := InternalSearchMailBox(ASearchInfo, True, ACharSet); +end; + +function TIdIMAP4.SubscribeMailBox(const AMBName: String): Boolean; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdSubscribe] + ' "' + DoMUTFEncode(AMBName) + '"', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.UnsubscribeMailBox(const AMBName: String): Boolean; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUnsubscribe] + ' "' + DoMUTFEncode(AMBName) + '"', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.ListMailBoxes(AMailBoxList: TStrings): Boolean; +begin + Result := False; + {CC2: This is one of the few cases where the server can return only "OK completed" + meaning that the user has no mailboxes.} + CheckConnectionState([csAuthenticated, csSelected]); + SendCmd(NewCmdCounter, IMAP4Commands[cmdList] + ' "" *', [IMAP4Commands[cmdList]]); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + ParseListResult(AMailBoxList, LastCmdResult.Text); + Result := True; + end; +end; + +function TIdIMAP4.ListInferiorMailBoxes(AMailBoxList, AInferiorMailBoxList: TStrings): Boolean; +var + Ln : Integer; + LAuxMailBoxList : TStringList; +begin + Result := False; + {CC2: This is one of the few cases where the server can return only "OK completed" + meaning that the user has no inferior mailboxes.} + CheckConnectionState([csAuthenticated, csSelected]); + if AMailBoxList = nil then begin + SendCmd(NewCmdCounter, IMAP4Commands[cmdList] + ' "" %', [IMAP4Commands[cmdList]]); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + ParseListResult(AInferiorMailBoxList, LastCmdResult.Text); + //The INBOX mailbox is added because I think it always has to exist + //in an IMAP4 account (default) but it does not list it in this command. + Result := True; + end; + end else begin + LAuxMailBoxList := TStringList.Create; + try + AInferiorMailBoxList.Clear; + for Ln := 0 to AMailBoxList.Count - 1 do begin + SendCmd(NewCmdCounter, + IMAP4Commands[cmdList] + ' "" "' + DoMUTFEncode(AMailBoxList[Ln]) + FMailBoxSeparator + '%"', {Do not Localize} + [IMAP4Commands[cmdList]]); + if LastCmdResult.Code = IMAP_OK then begin + ParseListResult(LAuxMailBoxList, LastCmdResult.Text); + AInferiorMailBoxList.AddStrings(LAuxMailBoxList); + Result := True; + end else begin + Break; + end; + end; + finally + FreeAndNil(LAuxMailBoxList); + end; + end; +end; + +function TIdIMAP4.ListSubscribedMailBoxes(AMailBoxList: TStrings): Boolean; +begin + {CC2: This is one of the few cases where the server can return only "OK completed" + meaning that the user has no subscribed mailboxes.} + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + SendCmd(NewCmdCounter, IMAP4Commands[cmdLSub] + ' "" *', [IMAP4Commands[cmdList], IMAP4Commands[cmdLSub]]); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + // ds - fixed bug # 506026 + ParseLSubResult(AMailBoxList, LastCmdResult.Text); + Result := True; + end; +end; + +function TIdIMAP4.StoreFlags(const AMsgNumList: array of Integer; + const AStoreMethod: TIdIMAP4StoreDataItem; const AFlags: TIdMessageFlagsSet): Boolean; +var + LDataItem, + LMsgSet, + LFlags: String; +begin + Result := False; + if Length(AMsgNumList) > 0 then begin + LMsgSet := ArrayToNumberStr(AMsgNumList); + case AStoreMethod of + sdReplace: LDataItem := IMAP4StoreDataItem[sdReplaceSilent]; + sdAdd: LDataItem := IMAP4StoreDataItem[sdAddSilent]; + sdRemove: LDataItem := IMAP4StoreDataItem[sdRemoveSilent]; + else + LDataItem := IMAP4StoreDataItem[AStoreMethod]; + end; + LFlags := MessageFlagSetToStr(AFlags); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdStore] + ' ' + LMsgSet + ' ' + LDataItem + ' (' + LFlags + ')', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; + end; +end; + +function TIdIMAP4.UIDStoreFlags(const AMsgUID: String; + const AStoreMethod: TIdIMAP4StoreDataItem; const AFlags: TIdMessageFlagsSet): Boolean; +var + LDataItem, + LFlags : String; +begin + Result := False; + IsUIDValid(AMsgUID); + case AStoreMethod of + sdReplace: LDataItem := IMAP4StoreDataItem[sdReplaceSilent]; + sdAdd: LDataItem := IMAP4StoreDataItem[sdAddSilent]; + sdRemove: LDataItem := IMAP4StoreDataItem[sdRemoveSilent]; + else + LDataItem := IMAP4StoreDataItem[AStoreMethod]; + end; + LFlags := MessageFlagSetToStr(AFlags); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdStore] + ' ' + AMsgUID + ' ' + LDataItem + ' (' + LFlags + ')', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.UIDStoreFlags(const AMsgUIDList: array of String; + const AStoreMethod: TIdIMAP4StoreDataItem; const AFlags: TIdMessageFlagsSet): Boolean; +var + LDataItem, + LMsgSet, + LFlags : String; + LN: integer; +begin + Result := False; + LMsgSet := ''; + for LN := 0 to Length(AMsgUIDList) -1 do begin + IsUIDValid(AMsgUIDList[LN]); + if LN > 0 then begin + LMsgSet := LMsgSet + ','; {Do not Localize} + end; + LMsgSet := LMsgSet+AMsgUIDList[LN]; + end; + case AStoreMethod of + sdReplace: LDataItem := IMAP4StoreDataItem[sdReplaceSilent]; + sdAdd: LDataItem := IMAP4StoreDataItem[sdAddSilent]; + sdRemove: LDataItem := IMAP4StoreDataItem[sdRemoveSilent]; + else + LDataItem := IMAP4StoreDataItem[AStoreMethod]; + end; + LFlags := MessageFlagSetToStr(AFlags); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdStore] + ' ' + LMsgSet + ' ' + LDataItem + ' (' + LFlags + ')', {Do not Localize} + []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.CopyMsgs(const AMsgNumList: array of Integer; const AMBName: String): Boolean; +var + LMsgSet : String; +begin + Result := False; + if Length(AMsgNumList) > 0 then begin + LMsgSet := ArrayToNumberStr ( AMsgNumList ); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdCopy] + ' ' + LMsgSet + ' "' + DoMUTFEncode(AMBName) + '"', []); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; + end; +end; + +function TIdIMAP4.UIDCopyMsgs(const AMsgUIDList: TStrings; const AMBName: String): Boolean; +var + LCmd : String; + LN: integer; +begin + Result := False; + if AMsgUIDList.Count > 0 then begin + LCmd := IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdCopy] + ' '; {Do not Localize} + for LN := 0 to AMsgUIDList.Count-1 do begin + IsUIDValid(AMsgUIDList.Strings[LN]); + if LN = 0 then begin + LCmd := LCmd + AMsgUIDList.Strings[LN]; + end else begin + LCmd := LCmd + ',' + AMsgUIDList.Strings[LN]; {Do not Localize} + end; + end; + LCmd := LCmd + ' "' + DoMUTFEncode(AMBName) + '"'; {Do not Localize} + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, LCmd, []); + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; + end; +end; + +function TIdIMAP4.CopyMsg(const AMsgNum: Integer; const AMBName: String): Boolean; +//Copies a message from the current selected mailbox to the specified mailbox. +begin + Result := False; + IsNumberValid(AMsgNum); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdCopy] + ' ' + IntToStr(AMsgNum) + ' "' + DoMUTFEncode(AMBName) + '"', []); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + +function TIdIMAP4.UIDCopyMsg(const AMsgUID: String; const AMBName: String): Boolean; +//Copies a message from the current selected mailbox to the specified mailbox. +begin + Result := False; + IsUIDValid(AMsgUID); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdCopy] + ' ' + AMsgUID + ' "' + DoMUTFEncode(AMBName) + '"', []); {Do not Localize} + if LastCmdResult.Code = IMAP_OK then begin + Result := True; + end; +end; + + +function TIdIMAP4.AppendMsg(const AMBName: String; AMsg: TIdMessage; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; +begin + Result := AppendMsg(AMBName, AMsg, nil, AFlags, AInternalDateTimeGMT); +end; + +function TIdIMAP4.AppendMsg(const AMBName: String; AMsg: TIdMessage; AAlternativeHeaders: TIdHeaderList; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; +var + LFlags, + LMsgLiteral, LDateTime: String; + LUseNonSyncLiteral: Boolean; + Ln: Integer; + LCmd: string; + LLength: TIdStreamSize; + LHeadersToSend, LCopiedHeaders: TIdHeaderList; + LHeadersAsString: string; + LHeadersAsBytes: TIdBytes; + LMimeBoundary: string; + LStream: TStream; + LHelper: TIdIMAP4WorkHelper; +begin + Result := False; + LHeadersasBytes := nil; // keep the compiler happy + + CheckConnectionState([csAuthenticated, csSelected]); + if Length(AMBName) <> 0 then begin + LFlags := MessageFlagSetToStr(AFlags); + if LFlags <> '' then begin {Do not Localize} + LFlags := '(' + LFlags + ')'; {Do not Localize} + end; + if AInternalDateTimeGMT <> 0.0 then begin + // even though flags are optional, some servers, such as GMail, will + // fail to parse the command correctly if no flags are specified in + // front of the internal date... + if LFlags = '' then begin + LFlags := '()'; // TODO: should 'NIL' be used instead? {Do not Localize} + end; + LDateTime := '"' + DateTimeGMTToImapStr(AInternalDateTimeGMT) + '"'; {do not localize} + end; + + {CC8: In Indy 10, we want to support attachments (previous versions did + not). The problem is that we have to know the size of the message + in advance of sending it for the IMAP APPEND command. + The problem is that there is no way of calculating the size of a + message without generating the encoded message. Therefore, write the + message out to a temporary stream, and then get the size of the data, + which with a bit of adjustment, will give us the size of the message + we will send. + The "adjustment" is necessary because SaveToStream generates it's own + headers, which will be different to both the ones in AMsg and + AAlternativeHeaders, in the Date header, if nothing else.} + + LStream := TMemoryStream.Create; + try + {RLebeau 04/02/2014: if the user passed in AMsg.LastGeneratedHeaders + or AMsg.Headers as AAlternativeHeaders, then assume the user wants to + use the headers that existed prior to AMsg being saved below, which + may create new header values...} + + LCopiedHeaders := nil; + try + if (AAlternativeHeaders <> nil) and + ((AAlternativeHeaders = AMsg.LastGeneratedHeaders) or (AAlternativeHeaders = AMsg.Headers)) then + begin + LCopiedHeaders := TIdHeaderList.Create(QuoteRFC822); + LCopiedHeaders.Assign(AAlternativeHeaders); + end; + + {RLebeau 12/09/2012: this is a workaround to a design limitation in + TIdMessage.SaveToStream(). It always outputs the stream data in an + escaped format using SMTP dot transparency, but that is not used in + IMAP! Until this design is corrected, we have to use a workaround + for now. This logic is copied from TIdMessage.SaveToSteam() and + slightly tweaked...} + + //AMsg.SaveToStream(LStream); + {$IFDEF HAS_CLASS_HELPER} + AMsg.SaveToStream(LStream, False, False); + {$ELSE} + TIdMessageHelper_SaveToStream(AMsg, LStream, False, False); + {$ENDIF} + + LStream.Position := 0; + {We are better off making up the headers as a string first rather than predicting + its length. Slightly wasteful of memory, but it will not take up much.} + LHeadersAsString := ''; + + {Make sure the headers we end up using have the correct MIME boundary actually + used in the message being saved...} + if AMsg.NoEncode then begin + LMimeBoundary := AMsg.Headers.Params['Content-Type', 'boundary']; {do not localize} + end else begin + LMimeBoundary := AMsg.LastGeneratedHeaders.Params['Content-Type', 'boundary']; {do not localize} + end; + if (LCopiedHeaders = nil) and (AAlternativeHeaders <> nil) then begin + if AAlternativeHeaders.Params['Content-Type', 'boundary'] <> LMimeBoundary then {do not localize} + begin + LCopiedHeaders := TIdHeaderList.Create(QuoteRFC822); + LCopiedHeaders.Assign(AAlternativeHeaders); + end; + end; + + if LCopiedHeaders <> nil then begin + {Use the copied headers that the user has passed to us, adjusting the MIME boundary...} + LCopiedHeaders.Params['Content-Type', 'boundary'] := LMimeBoundary; {do not localize} + LHeadersToSend := LCopiedHeaders; + end + else if AAlternativeHeaders <> nil then begin + {Use the headers that the user has passed to us...} + LHeadersToSend := AAlternativeHeaders; + end + else if AMsg.NoEncode then begin + {Use the headers that are in the message AMsg...} + LHeadersToSend := AMsg.Headers; + end else begin + {Use the headers that SaveToStream() generated...} + LHeadersToSend := AMsg.LastGeneratedHeaders; + end; + // not using LHeadersToSend.Text because it uses platform-specific line breaks + for Ln := 0 to Pred(LHeadersToSend.Count) do begin + LHeadersAsString := LHeadersAsString + LHeadersToSend[Ln] + EOL; + end; + finally + LCopiedHeaders.Free; + end; + + LHeadersAsBytes := ToBytes(LHeadersAsString + EOL); + LHeadersAsString := ''; + + {Get the size of the headers we are sending...} + repeat until Length(ReadLnFromStream(LStream)) = 0; + {We have to subtract the size of the headers in the file and + add back the size of the headers we are to use + to get the size of the message we are going to send...} + LLength := Length(LHeadersAsBytes) + (LStream.Size - LStream.Position); + + LUseNonSyncLiteral := IsCapabilityListed('LITERAL+'); {Do not Localize} + if LUseNonSyncLiteral then begin + LMsgLiteral := '{' + IntToStr ( LLength ) + '+}'; {Do not Localize} + end else begin + LMsgLiteral := '{' + IntToStr ( LLength ) + '}'; {Do not Localize} + end; + {CC: The original code sent the APPEND command first, then followed it with the + message. Maybe this worked with some server, but most send a + response like "+ Send the additional command..." between the two, + which was not expected by the client and caused an exception.} + + //CC: Added double quotes around mailbox name, else mailbox names with spaces will cause server parsing error + LCmd := IMAP4Commands[cmdAppend] + ' "' + DoMUTFEncode(AMBName) + '" '; {Do not Localize} + if Length(LFlags) <> 0 then begin + LCmd := LCmd + LFlags + ' '; {Do not Localize} + end; + if Length(LDateTime) <> 0 then begin + LCmd := LCmd + LDateTime + ' '; {Do not Localize} + end; + LCmd := LCmd + LMsgLiteral; {Do not Localize} + + {CC3: Catch "Connection reset by peer"...} + try + if LUseNonSyncLiteral then begin + {Send the APPEND command and the message immediately, no + response needed...} + IOHandler.WriteLn(NewCmdCounter + ' ' + LCmd); + end else begin + {Try sending the APPEND command, get the + response, then send the message...} + SendCmd(NewCmdCounter, LCmd, []); + if LastCmdResult.Code <> IMAP_CONT then begin + Exit; + end; + end; + + LHelper := TIdIMAP4WorkHelper.Create(Self); + try + IOHandler.Write(LHeadersAsBytes); + {RLebeau: passing -1 to TIdIOHandler.Write(TStream) will send the + rest of the stream starting at its current Position...} + IOHandler.Write(LStream, -1, False); + finally + FreeAndNil(LHelper); + end; + {WARNING: After we send the message (which should be exactly + LLength bytes long), we need to send an EXTRA CRLF which is in + addition to the count in LLength, because this CRLF terminates the + APPEND command...} + IOHandler.WriteLn; + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdAppend]], False) = IMAP_OK then begin + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + finally + LStream.Free; + end; + end; +end; + +function TIdIMAP4.AppendMsgNoEncodeFromFile(const AMBName: String; ASourceFile: string; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; +var + LSourceStream: TIdReadFileExclusiveStream; +begin + LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile); + try + Result := AppendMsgNoEncodeFromStream(AMBName, LSourceStream, AFlags, AInternalDateTimeGMT); + finally + FreeAndNil(LSourceStream); + end; +end; + +function TIdIMAP4.AppendMsgNoEncodeFromStream(const AMBName: String; AStream: TStream; const AFlags: TIdMessageFlagsSet = []; + const AInternalDateTimeGMT: TDateTime = 0.0): Boolean; +const + cTerminator: array[0..4] of Byte = (13, 10, Ord('.'), 13, 10); +var + LFlags, LDateTime, LMsgLiteral: String; + LUseNonSyncLiteral: Boolean; + I: Integer; + LFound: Boolean; + LCmd: string; + LLength: TIdStreamSize; + LTempStream: TMemoryStream; + LHelper: TIdIMAP4WorkHelper; + LBuf: TIdBytes; +begin + Result := False; + CheckConnectionState([csAuthenticated, csSelected]); + if Length(AMBName) <> 0 then begin + LFlags := MessageFlagSetToStr(AFlags); + if LFlags <> '' then begin {Do not Localize} + LFlags := '(' + LFlags + ')'; {Do not Localize} + end; + if AInternalDateTimeGMT <> 0.0 then begin + // even though flags are optional, some servers, such as GMail, will + // fail to parse the command correctly if no flags are specified in + // front of the internal date... + if LFlags = '' then begin + LFlags := '()'; // TODO: should 'NIL' be used instead? {Do not Localize} + end; + LDateTime := '"' + DateTimeGMTToImapStr(AInternalDateTimeGMT) + '"'; {Do not Localize} + end; + LLength := AStream.Size - AStream.Position; + LTempStream := TMemoryStream.Create; + try + //Hunt for CRLF.CRLF, if present then we need to remove it... + + // RLebeau: why? The lines of the message data are not required to be + // dot-prefixed like in SMTP, so why should TIdIMAP care about any + // termination sequences in the file? We are telling the server exactly + // how large the message actually is. What if the message data actually + // contains a valid line with just a dot on it? This code would end up + // truncating the message that is stored on the server... + + SetLength(LBuf, 5); + if LLength > 0 then begin + LTempStream.CopyFrom(AStream, LLength); + LTempStream.Position := 0; + end; + repeat + if TIdStreamHelper.ReadBytes(LTempStream, LBuf, 5) < 5 then begin + Break; + end; + LFound := True; + for I := 0 to 4 do begin + if LBuf[I] <> cTerminator[I] then begin + LFound := False; + Break; + end; + end; + if LFound then begin + LLength := LTempStream.Position-5; + Break; + end; + TIdStreamHelper.Seek(LTempStream, -4, soCurrent); + until False; + + LUseNonSyncLiteral := IsCapabilityListed('LITERAL+'); {Do not Localize} + if LUseNonSyncLiteral then begin + LMsgLiteral := '{' + IntToStr(LLength) + '+}'; {Do not Localize} + end else begin + LMsgLiteral := '{' + IntToStr(LLength) + '}'; {Do not Localize} + end; + + {CC: The original code sent the APPEND command first, then followed it with the + message. Maybe this worked with some server, but most send a + response like "+ Send the additional command..." between the two, + which was not expected by the client and caused an exception.} + + //CC: Added double quotes around mailbox name, else mailbox names with spaces will cause server parsing error + LCmd := IMAP4Commands[cmdAppend] + ' "' + DoMUTFEncode(AMBName) + '" '; {Do not Localize} + if Length(LFlags) <> 0 then begin + LCmd := LCmd + LFlags + ' '; {Do not Localize} + end; + if Length(LDateTime) <> 0 then begin + LCmd := LCmd + LDateTime + ' '; {Do not Localize} + end; + LCmd := LCmd + LMsgLiteral; {Do not Localize} + + {CC3: Catch "Connection reset by peer"...} + try + if LUseNonSyncLiteral then begin + {Send the APPEND command and the message immediately, no + response needed...} + IOHandler.WriteLn(NewCmdCounter + ' ' + LCmd); + end else begin + {Try sending the APPEND command, get the + response, then send the message...} + SendCmd(NewCmdCounter, LCmd, []); + if LastCmdResult.Code <> IMAP_CONT then begin + Exit; + end; + end; + + LTempStream.Position := 0; + LHelper := TIdIMAP4WorkHelper.Create(Self); + try + IOHandler.Write(LTempStream, LLength); + finally + FreeAndNil(LHelper); + end; + {WARNING: After we send the message (which should be exactly + LLength bytes long), we need to send an EXTRA CRLF which is in + addition to the count in LLength, because this CRLF terminates the + APPEND command...} + IOHandler.WriteLn; + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdAppend]], False) = IMAP_OK then begin + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + finally + FreeAndNil(LTempStream); + end; + end; +end; + +function TIdIMAP4.RetrieveEnvelope(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; +begin + Result := InternalRetrieveEnvelope(AMsgNum, AMsg, nil); +end; + +function TIdIMAP4.RetrieveEnvelopeRaw(const AMsgNum: Integer; ADestList: TStrings): Boolean; +begin + Result := InternalRetrieveEnvelope(AMsgNum, nil, ADestList); +end; + +function TIdIMAP4.InternalRetrieveEnvelope(const AMsgNum: Integer; AMsg: TIdMessage; ADestList: TStrings): Boolean; +begin + {CC2: Return False if message number is invalid...} + IsNumberValid(AMsgNum); + Result := False; + CheckConnectionState(csSelected); + {Some servers return NO if the requested message number is not present + (e.g. Cyrus), others return OK but no data (CommuniGate).} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdEnvelope] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdEnvelope]]) then begin + if ADestList <> nil then begin + ADestList.Clear; + ADestList.Add(FLineStruct.IMAPValue); + end; + if AMsg <> nil then begin + ParseEnvelopeResult(AMsg, FLineStruct.IMAPValue); + end; + Result := True; + end; + end; + end; +end; + +function TIdIMAP4.UIDRetrieveEnvelope(const AMsgUID: String; AMsg: TIdMessage): Boolean; +begin + Result := UIDInternalRetrieveEnvelope(AMsgUID, AMsg, nil); +end; + +function TIdIMAP4.UIDRetrieveEnvelopeRaw(const AMsgUID: String; ADestList: TStrings): Boolean; +begin + Result := UIDInternalRetrieveEnvelope(AMsgUID, nil, ADestList); +end; + +function TIdIMAP4.UIDInternalRetrieveEnvelope(const AMsgUID: String; AMsg: TIdMessage; ADestList: TStrings): Boolean; +begin + IsUIDValid(AMsgUID); + {CC2: Return False if message number is invalid...} + Result := False; + CheckConnectionState(csSelected); + {Some servers return NO if the requested message number is not present + (e.g. Cyrus), others return OK but no data (CommuniGate).} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdEnvelope] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]]); + if LastCmdResult.Code = IMAP_OK then begin + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdEnvelope]]) then begin + if ADestList <> nil then begin + ADestList.Clear; + ADestList.Add(FLineStruct.IMAPValue); + end; + if AMsg <> nil then begin + ParseEnvelopeResult(AMsg, FLineStruct.IMAPValue); + end; + Result := True; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveAllEnvelopes(AMsgList: TIdMessageCollection): Boolean; +{NOTE: If AMsgList is empty or does not have enough records, records will be added. +If you pass a non-empty AMsgList, it is assumed the records are in relative record +number sequence: if not, pass in an empty AMsgList and copy the results to your +own AMsgList.} +var + Ln: Integer; + LMsg: TIdMessage; +begin + Result := False; + {CC2: This is one of the few cases where the server can return only "OK completed" + meaning that the user has no envelopes.} + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' 1:* (' + IMAP4FetchDataItem[fdEnvelope] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + for Ln := 0 to LastCmdResult.Text.Count-1 do begin + if ParseLastCmdResult(LastCmdResult.Text[Ln], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdEnvelope]]) then begin + if LN >= AMsgList.Count then begin + LMsg := AMsgList.Add.Msg; + end else begin + LMsg := AMsgList.Messages[LN]; + end; + ParseEnvelopeResult(LMsg, FLineStruct.IMAPValue); + end; + end; + Result := True; + end; +end; + +function TIdIMAP4.UIDRetrieveAllEnvelopes(AMsgList: TIdMessageCollection): Boolean; +{NOTE: If AMsgList is empty or does not have enough records, records will be added. +If you pass a non-empty AMsgList, it is assumed the records are in relative record +number sequence: if not, pass in an empty AMsgList and copy the results to your +own AMsgList.} +var + Ln: Integer; + LMsg: TIdMessage; +begin + Result := False; + {CC2: This is one of the few cases where the server can return only "OK completed" + meaning that the user has no envelopes.} + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' 1:* (' + IMAP4FetchDataItem[fdEnvelope] + ' ' + IMAP4FetchDataItem[fdFlags] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + for Ln := 0 to LastCmdResult.Text.Count-1 do begin + if ParseLastCmdResult(LastCmdResult.Text[Ln], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdEnvelope]]) then begin + if LN >= AMsgList.Count then begin + LMsg := AMsgList.Add.Msg; + end else begin + LMsg := AMsgList.Messages[LN]; + end; + ParseEnvelopeResult(LMsg, FLineStruct.IMAPValue); + LMsg.UID := FLineStruct.UID; + LMsg.Flags := FLineStruct.Flags; + end; + end; + Result := True; + end; +end; + +function TIdIMAP4.RetrieveText(const AMsgNum: Integer; var AText: string): Boolean; + //Retrieve a specific individual part of a message +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveText(AMsgNum, AText, False, False, False); +end; + +function TIdIMAP4.RetrieveText2(const AMsgNum: Integer; var AText: string): Boolean; + //Retrieve a specific individual part of a message +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveText(AMsgNum, AText, False, False, True); +end; + +function TIdIMAP4.RetrieveTextPeek(const AMsgNum: Integer; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveText(AMsgNum, AText, False, True, False); +end; + +function TIdIMAP4.RetrieveTextPeek2(const AMsgNum: Integer; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveText(AMsgNum, AText, False, True, True); +end; + +function TIdIMAP4.UIDRetrieveText(const AMsgUID: String; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieveText(IndyStrToInt(AMsgUID), AText, True, False, False); +end; + +function TIdIMAP4.UIDRetrieveText2(const AMsgUID: String; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieveText(IndyStrToInt(AMsgUID), AText, True, False, True); +end; + +function TIdIMAP4.UIDRetrieveTextPeek(const AMsgUID: String; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieveText(IndyStrToInt(AMsgUID), AText, True, True, False); +end; + +function TIdIMAP4.UIDRetrieveTextPeek2(const AMsgUID: String; var AText: string): Boolean; + {CC3: Added: Retrieve the text part of the message...} +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieveText(IndyStrToInt(AMsgUID), AText, True, True, True); +end; + +function TIdIMAP4.InternalRetrieveText(const AMsgNum: Integer; var AText: string; + AUseUID: Boolean; AUsePeek: Boolean; AUseFirstPartInsteadOfText: Boolean): Boolean; + {CC3: Added: Retrieve the text part of the message...} +var + LCmd: string; + LParts: TIdImapMessageParts; + LThePart: TIdImapMessagePart; + LCharSet: String; + LContentTransferEncoding: string; + LTextPart: integer; + LHelper: TIdIMAP4WorkHelper; + + procedure DoDecode(ADecoderClass: TIdDecoderClass = nil; AStripCRLFs: Boolean = False); + var + LDecoder: TIdDecoder; + LStream: TStream; + LStrippedStream: TStringStream; + LUnstrippedStream: TStringStream; + LEncoding: IIdTextEncoding; + begin + LStream := TMemoryStream.Create; + try + if ADecoderClass <> nil then begin + LDecoder := ADecoderClass.Create(Self); + try + LDecoder.DecodeBegin(LStream); + try + LUnstrippedStream := TStringStream.Create(''); + try + IOHandler.ReadStream(LUnstrippedStream, FLineStruct.ByteCount); //ReadStream uses OnWork, most other methods dont + {This is more complicated than quoted-printable because we + have to strip CRLFs that have been inserted by the MTA to + avoid overly long lines...} + if AStripCRLFs then begin + LStrippedStream := TStringStream.Create(''); + try + StripCRLFs(LUnstrippedStream, LStrippedStream); + LDecoder.Decode(LStrippedStream.DataString); + finally + FreeAndNil(LStrippedStream); + end; + end else begin + LDecoder.Decode(LUnstrippedStream.DataString); + end; + finally + FreeAndNil(LUnstrippedStream); + end; + finally + LDecoder.DecodeEnd; + end; + finally + FreeAndNil(LDecoder); + end; + end else begin + IOHandler.ReadStream(LStream, FLineStruct.ByteCount); //ReadStream uses OnWork, most other methods dont + end; + LStream.Position := 0; + if LCharSet <> '' then begin + LEncoding := CharsetToEncoding(LCharSet); + AText := ReadStringFromStream(LStream, -1, LEncoding{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_OSDefault{$ENDIF}); + end else begin + AText := ReadStringFromStream(LStream, -1, IndyTextEncoding_8Bit{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); + end; + finally + FreeAndNil(LStream); + end; + end; + +begin + Result := False; + AText := ''; {Do not Localize} + CheckConnectionState(csSelected); + LTextPart := 0; {The text part is usually part 1 but could be part 2} + if AUseFirstPartInsteadOfText then begin + {In this case, we need the body structure to find out what + encoding has been applied to part 1...} + LParts := TIdImapMessageParts.Create(nil); + try + if AUseUID then begin + if not UIDRetrieveStructure(IntToStr(AMsgNum), LParts) then begin + Exit; + end; + end else begin + if not RetrieveStructure(AMsgNum, LParts) then begin + Exit; + end; + end; + + {Get the info we want out of LParts...} + {Some emails have their first parts empty, so search for the first non-empty part.} + repeat + LThePart := LParts.Items[LTextPart]; + if (LThePart.FSize <> 0) then begin + Break; + end; + Inc(LTextPart); + until LTextPart >= LParts.Count - 1; + + LCharSet := LThePart.CharSet; + LContentTransferEncoding := LThePart.ContentTransferEncoding; + finally + FreeAndNil(LParts); + end; + end else begin + // TODO: detect LCharSet and LContentTransferEncoding... + end; + LCmd := ''; + if AUseUID then begin + LCmd := LCmd + IMAP4Commands[cmdUID] + ' '; {Do not Localize} + end; + LCmd := LCmd + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' ('; {Do not Localize} + if AUsePeek then begin + LCmd := LCmd + IMAP4FetchDataItem[fdBody]+'.PEEK'; {Do not Localize} + end else begin + LCmd := LCmd + IMAP4FetchDataItem[fdBody]; + end; + if not AUseFirstPartInsteadOfText then begin + LCmd := LCmd + '[TEXT])'; {Do not Localize} + end else begin + LCmd := LCmd + '[' + IntToStr(LTextPart+1) + '])'; {Do not Localize} + end; + + SendCmd(NewCmdCounter, LCmd, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + try + {For an invalid request (non-existent part or message), NIL is returned as the size...} + if (LastCmdResult.Text.Count < 1) + or (not ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], + [IMAP4FetchDataItem[fdBody]+'['+'TEXT'+']' , IMAP4FetchDataItem[fdBody]+'['+IntToStr(LTextPart+1)+']'])) {do not localize} + or (PosInStrArray(FLineStruct.IMAPValue, ['NIL', '""'], False) <> -1) {do not localize} + or (FLineStruct.ByteCount < 1) then + begin + GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False); + Result := False; + Exit; + end; + + LHelper := TIdIMAP4WorkHelper.Create(Self); + try + if TextIsSame(LContentTransferEncoding, 'base64') then begin {Do not Localize} + DoDecode(TIdDecoderMIME, True); + end else if TextIsSame(LContentTransferEncoding, 'quoted-printable') then begin {Do not Localize} + DoDecode(TIdDecoderQuotedPrintable); + end else if TextIsSame(LContentTransferEncoding, 'binhex40') then begin {Do not Localize} + DoDecode(TIdDecoderBinHex4); + end else begin + {Assume no encoding (8bit) or something we cannot decode...} + DoDecode(); + end; + finally + FreeAndNil(LHelper); + end; + IOHandler.ReadLnWait; {Remove last line, ')' or 'UID 1)'} + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveStructure(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveStructure(AMsgNum, AMsg, nil); +end; + +function TIdIMAP4.RetrieveStructure(const AMsgNum: Integer; AParts: TIdImapMessageParts): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieveStructure(AMsgNum, nil, AParts); +end; + +function TIdIMAP4.InternalRetrieveStructure(const AMsgNum: Integer; AMsg: TIdMessage; AParts: TIdImapMessageParts): Boolean; +var + LTheParts: TIdMessageParts; +begin + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdBodyStructure] + ')', + [IMAP4Commands[cmdFetch]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdBodyStructure]]) then begin + if AMsg <> nil then begin + LTheParts := AMsg.MessageParts; + end else begin + LTheParts := nil; + end; + ParseBodyStructureResult(FLineStruct.IMAPValue, LTheParts, AParts); + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch]], False) = IMAP_OK then begin + Result := True; + end; + end; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.RetrievePart(const AMsgNum: Integer; const APartNum: string; + ADestStream: TStream; AContentTransferEncoding: string): Boolean; +var + LDummy1: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + LDummy2: Integer; +begin + IsNumberValid(AMsgNum); + if ADestStream = nil then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(AMsgNum, APartNum, False, False, ADestStream, LDummy1, LDummy2, '', AContentTransferEncoding); {Do not Localize} +end; + +function TIdIMAP4.RetrievePart(const AMsgNum: Integer; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := RetrievePart(AMsgNum, IntToStr(APartNum), ABuffer, ABufferLength, AContentTransferEncoding); +end; + +// Retrieve a specific individual part of a message +function TIdIMAP4.RetrievePart(const AMsgNum: Integer; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrievePart(AMsgNum, APartNum, False, False, nil, ABuffer, ABufferLength, '', AContentTransferEncoding); {Do not Localize} +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.RetrievePartPeek(const AMsgNum: Integer; const APartNum: string; + ADestStream: TStream; AContentTransferEncoding: string): Boolean; +var + LDummy1: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + LDummy2: Integer; +begin + IsNumberValid(AMsgNum); + if ADestStream = nil then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(AMsgNum, APartNum, False, True, ADestStream, LDummy1, LDummy2, '', AContentTransferEncoding); {Do not Localize} +end; + +function TIdIMAP4.RetrievePartPeek(const AMsgNum: Integer; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := RetrievePartPeek(AMsgNum, IntToStr(APartNum), ABuffer, ABufferLength, AContentTransferEncoding); +end; + +//Retrieve a specific individual part of a message +function TIdIMAP4.RetrievePartPeek(const AMsgNum: Integer; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrievePart(AMsgNum, APartNum, False, True, nil, ABuffer, ABufferLength, '', AContentTransferEncoding); {Do not Localize} +end; + +// Retrieve a specific individual part of a message +function TIdIMAP4.UIDRetrievePart(const AMsgUID: String; const APartNum: string; + var ADestStream: TStream; AContentTransferEncoding: string): Boolean; +var + LDummy1: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + LDummy2: Integer; +begin + IsUIDValid(AMsgUID); + if ADestStream = nil then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, False, ADestStream, LDummy1, LDummy2, '', AContentTransferEncoding); {Do not Localize} +end; + +function TIdIMAP4.UIDRetrievePart(const AMsgUID: String; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := UIDRetrievePart(AMsgUID, IntToStr(APartNum), ABuffer, ABufferLength, AContentTransferEncoding); +end; + +// Retrieve a specific individual part of a message +function TIdIMAP4.UIDRetrievePart(const AMsgUID: String; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsUIDValid(AMsgUID); + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, False, nil, ABuffer, ABufferLength, '', AContentTransferEncoding); {Do not Localize} +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.UIDRetrievePartPeek(const AMsgUID: String; const APartNum: string; + var ADestStream: TStream; AContentTransferEncoding: string): Boolean; +var + LDummy1: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + LDummy2: Integer; +begin + IsUIDValid(AMsgUID); + if ADestStream = nil then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, True, ADestStream, LDummy1, LDummy2, '', AContentTransferEncoding); {Do not Localize} +end; + +function TIdIMAP4.UIDRetrievePartPeek(const AMsgUID: String; const APartNum: Integer; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := UIDRetrievePartPeek(AMsgUID, IntToStr(APartNum), ABuffer, ABufferLength, AContentTransferEncoding); +end; + +function TIdIMAP4.UIDRetrievePartPeek(const AMsgUID: String; const APartNum: string; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; AContentTransferEncoding: string): Boolean; + //Retrieve a specific individual part of a message +begin + IsUIDValid(AMsgUID); + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, True, nil, ABuffer, ABufferLength, '', AContentTransferEncoding); {Do not Localize} +end; + +function TIdIMAP4.RetrievePartToFile(const AMsgNum: Integer; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := RetrievePartToFile(AMsgNum, IntToStr(APartNum), ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.RetrievePartToFile(const AMsgNum: Integer; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +var + LDummy: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; +begin + IsNumberValid(AMsgNum); + if Length(ADestFileNameAndPath) = 0 then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(AMsgNum, APartNum, False, False, nil, + LDummy, ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +function TIdIMAP4.RetrievePartToFilePeek(const AMsgNum: Integer; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := RetrievePartToFilePeek(AMsgNum, IntToStr(APartNum), ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.RetrievePartToFilePeek(const AMsgNum: Integer; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +var + LDummy: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; +begin + IsNumberValid(AMsgNum); + if Length(ADestFileNameAndPath) = 0 then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(AMsgNum, APartNum, False, True, nil, + LDummy, ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +function TIdIMAP4.UIDRetrievePartToFile(const AMsgUID: String; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := UIDRetrievePartToFile(AMsgUID, IntToStr(APartNum), ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.UIDRetrievePartToFile(const AMsgUID: String; const APartNum: string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +var + LDummy: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; +begin + IsUIDValid(AMsgUID); + if Length(ADestFileNameAndPath) = 0 then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, False, nil, + LDummy, ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +function TIdIMAP4.UIDRetrievePartToFilePeek(const AMsgUID: String; const APartNum: Integer; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +begin + IsNumberValid(APartNum); + Result := UIDRetrievePartToFilePeek(AMsgUID, IntToStr(APartNum), ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +// retrieve a specific individual part of a message +function TIdIMAP4.UIDRetrievePartToFilePeek(const AMsgUID: String; const APartNum: {Integer} string; + ALength: Integer; ADestFileNameAndPath: string; AContentTransferEncoding: string): Boolean; +var + LDummy: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; +begin + IsUIDValid(AMsgUID); + if Length(ADestFileNameAndPath) = 0 then begin + Result := False; + Exit; + end; + Result := InternalRetrievePart(IndyStrToInt(AMsgUID), APartNum, True, True, + nil, LDummy, ALength, ADestFileNameAndPath, AContentTransferEncoding); +end; + +// retrieve a specific individual part of a message +// TODO: remove the ABufferLength output parameter under DOTNET, it is redundant... +function TIdIMAP4.InternalRetrievePart(const AMsgNum: Integer; const APartNum: {Integer} string; + AUseUID: Boolean; AUsePeek: Boolean; ADestStream: TStream; + var ABuffer: {$IFDEF DOTNET}TIdBytes{$ELSE}PByte{$ENDIF}; + var ABufferLength: Integer; {NOTE: var args cannot have default params} + ADestFileNameAndPath: string; + AContentTransferEncoding: string): Boolean; +var + LCmd: string; + bCreatedStream: Boolean; + LDestStream: TStream; +// LPartSizeParam: string; + LHelper: TIdIMAP4WorkHelper; + + procedure DoDecode(ADecoderClass: TIdDecoderClass = nil; AStripCRLFs: Boolean = False); + var + LDecoder: TIdDecoder; + LStream: TStream; + LStrippedStream: TStringStream; + LUnstrippedStream: TStringStream; + begin + if LDestStream = nil then begin + LStream := TMemoryStream.Create; + end else begin + LStream := LDestStream; + end; + try + if ADecoderClass <> nil then begin + LDecoder := ADecoderClass.Create(Self); + try + LDecoder.DecodeBegin(LStream); + try + LUnstrippedStream := TStringStream.Create(''); + try + IOHandler.ReadStream(LUnstrippedStream, ABufferLength); //ReadStream uses OnWork, most other methods dont + {This is more complicated than quoted-printable because we + have to strip CRLFs that have been inserted by the MTA to + avoid overly long lines...} + if AStripCRLFs then begin + LStrippedStream := TStringStream.Create(''); + try + StripCRLFs(LUnstrippedStream, LStrippedStream); + LDecoder.Decode(LStrippedStream.DataString); + finally + FreeAndNil(LStrippedStream); + end; + end else begin + LDecoder.Decode(LUnstrippedStream.DataString); + end; + finally + FreeAndNil(LUnstrippedStream); + end; + finally + LDecoder.DecodeEnd; + end; + finally + FreeAndNil(LDecoder); + end; + end else begin + IOHandler.ReadStream(LStream, ABufferLength); //ReadStream uses OnWork, most other methods dont + end; + if LDestStream = nil then begin + ABufferLength := LStream.Size; + {$IFDEF DOTNET} + //ABuffer is a TIdBytes. + SetLength(ABuffer, ABufferLength); + if ABufferLength > 0 then begin + LStream.Position := 0; + ReadTIdBytesFromStream(LStream, ABuffer, ABufferLength); + end; + {$ELSE} + //ABuffer is a PByte. + GetMem(ABuffer, ABufferLength); + if ABufferLength > 0 then begin + LStream.Position := 0; + LStream.ReadBuffer(ABuffer^, ABufferLength); + end; + {$ENDIF} + end; + finally + if LDestStream = nil then begin + FreeAndNil(LStream); + end; + end; + end; + +begin + {CCC: Make sure part number is valid since it is now passed as a string...} + IsImapPartNumberValid(APartNum); + Result := False; + ABuffer := nil; + ABufferLength := 0; + CheckConnectionState(csSelected); + LCmd := ''; + if AUseUID then begin + LCmd := LCmd + IMAP4Commands[cmdUID] + ' '; {Do not Localize} + end; + LCmd := LCmd + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' ('; {Do not Localize} + if AUsePeek then begin + LCmd := LCmd + IMAP4FetchDataItem[fdBody]+'.PEEK'; {Do not Localize} + end else begin + LCmd := LCmd + IMAP4FetchDataItem[fdBody]; + end; + LCmd := LCmd + '[' + APartNum + '])'; {Do not Localize} + + SendCmd(NewCmdCounter, LCmd, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + //LPartSizeParam := ''; {Do not Localize} + if ( (LastCmdResult.Text.Count < 1) or + (not ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [])) + or (PosInStrArray(FLineStruct.IMAPValue, ['NIL', '""'], False) <> -1) {do not localize} + or (FLineStruct.ByteCount < 1) ) then + begin + GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False); + Result := False; + Exit; + end; + {CC4: Some messages have an empty first part. These respond as: + 17 FETCH (BODY[1] "" UID 20) + instead of the more normal: + 17 FETCH (BODY[1] {11} {This bracket is not part of the response! + ... + UID 20) + } + ABufferLength := FLineStruct.ByteCount; + bCreatedStream := False; + + if ADestStream = nil then + begin + if Length(ADestFileNameAndPath) = 0 then begin + {User wants to write it to a memory block...} + LDestStream := nil; + end else begin + {User wants to write it to a file...} + LDestStream := TIdFileCreateStream.Create(ADestFileNameAndPath); + bCreatedStream := True; + end; + end else + begin + {User wants to write it to a stream ...} + LDestStream := ADestStream; + end; + try + LHelper := TIdIMAP4WorkHelper.Create(Self); + try + if TextIsSame(AContentTransferEncoding, 'base64') then begin {Do not Localize} + DoDecode(TIdDecoderMIME, True); + end else if TextIsSame(AContentTransferEncoding, 'quoted-printable') then begin {Do not Localize} + DoDecode(TIdDecoderQuotedPrintable); + end else if TextIsSame(AContentTransferEncoding, 'binhex40') then begin {Do not Localize} + DoDecode(TIdDecoderBinHex4); + end else begin + {Assume no encoding (8bit) or something we cannot decode...} + DoDecode; + end; + finally + FreeAndNil(LHelper); + end; + finally + if bCreatedStream then begin + FreeAndNil(LDestStream); + end; + end; + IOHandler.ReadLnWait; {Remove last line, ')' or 'UID 1)'} + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.UIDRetrieveStructure(const AMsgUID: String; AMsg: TIdMessage): Boolean; +begin + IsUIDValid(AMsgUID); + Result := UIDInternalRetrieveStructure(AMsgUID, AMsg, nil); +end; + +function TIdIMAP4.UIDRetrieveStructure(const AMsgUID: String; AParts: TIdImapMessageParts): Boolean; +begin + IsUIDValid(AMsgUID); + Result := UIDInternalRetrieveStructure(AMsgUID, nil, AParts); +end; + +function TIdIMAP4.UIDInternalRetrieveStructure(const AMsgUID: String; AMsg: TIdMessage; AParts: TIdImapMessageParts): Boolean; +var + //LSlRetrieve : TStringList; + //LStr: string; + LTheParts: TIdMessageParts; +begin + Result := False; + CheckConnectionState(csSelected); + + //Note: The normal single-line response may be split for huge bodystructures, + //allow for this by setting ASingleLineMayBeSplit to True... + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdBodyStructure] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], + True, True); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdBodyStructure]]) then begin + if AMsg <> nil then begin + LTheParts := AMsg.MessageParts; + end else begin + LTheParts := nil; + end; + ParseBodyStructureResult(FLineStruct.IMAPValue, LTheParts, AParts); + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + Result := True; + end; + end; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveHeader(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; +var + LStr: string; +begin + Result := False; + IsNumberValid(AMsgNum); + CheckConnectionState(csSelected); + + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdRFC822Header] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Header]]) + and (FLineStruct.ByteCount > 0) then + begin + BeginWork(wmRead, FLineStruct.ByteCount); //allow ReadString to use OnWork + try + LStr := IOHandler.ReadString(FLineStruct.ByteCount); + finally + EndWork(wmRead); + end; + {CC2: Clear out body so don't get multiple copies of bodies} + AMsg.Clear; + AMsg.Headers.Text := LStr; + AMsg.ProcessHeaders; + LStr := IOHandler.ReadLnWait; {Remove trailing line after the message, probably a ')' } + ParseLastCmdResultButAppendInfo(LStr); //There may be a UID or FLAGS in this + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch]], False) = IMAP_OK then begin + Result := True; + end; + end; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.UIDRetrieveHeader(const AMsgUID: String; AMsg: TIdMessage): Boolean; +var + LStr: string; +begin + Result := False; + IsUIDValid(AMsgUID); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdRFC822Header] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Header]]) + and (FLineStruct.ByteCount > 0) then + begin + BeginWork(wmRead, FLineStruct.ByteCount); //allow ReadString to use OnWork + try + LStr := IOHandler.ReadString(FLineStruct.ByteCount); + finally + EndWork(wmRead); + end; + {CC2: Clear out body so don't get multiple copies of bodies} + AMsg.Clear; + AMsg.Headers.Text := LStr; + AMsg.ProcessHeaders; + LStr := IOHandler.ReadLnWait; {Remove trailing line after the message, probably a ')' } + ParseLastCmdResultButAppendInfo(LStr); //There may be a UID or FLAGS in this + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + Result := True; + end; + end; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.RetrievePartHeader(const AMsgNum: Integer; const APartNum: string; AHeaders: TIdHeaderList): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrievePartHeader(AMsgNum, APartNum, False, AHeaders); +end; + +function TIdIMAP4.UIDRetrievePartHeader(const AMsgUID: String; const APartNum: string; AHeaders: TIdHeaderList): Boolean; +begin + IsUIDValid(AMsgUID); + Result := InternalRetrievePartHeader(IndyStrToInt(AMsgUID), APartNum, True, AHeaders); +end; + +function TIdIMAP4.InternalRetrievePartHeader(const AMsgNum: Integer; const APartNum: string; + const AUseUID: Boolean; AHeaders: TIdHeaderList): Boolean; +var + LCmd: string; +begin + Result := False; + CheckConnectionState(csSelected); + LCmd := ''; + if AUseUID then begin + LCmd := LCmd + IMAP4Commands[cmdUID] + ' '; {Do not Localize} + end; + LCmd := LCmd + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdBody] + '[' + APartNum + '.' + IMAP4FetchDataItem[fdHeader] + '])'; {Do not Localize} + + SendCmd(NewCmdCounter, LCmd, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + if LastCmdResult.Text.Count > 0 then begin + if ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], []) + and (PosInStrArray(FLineStruct.IMAPValue, ['NIL', '""'], False) = -1) + and (FLineStruct.ByteCount > 0) then + begin + {CC4: Some messages have an empty first part. These respond as: + 17 FETCH (BODY[1] "" UID 20) + instead of the more normal: + 17 FETCH (BODY[1] {11} {This bracket is not part of the response! + ... + UID 20) + } + BeginWork(wmRead, FLineStruct.ByteCount); //allow ReadString to use OnWork + try + AHeaders.Text := IOHandler.ReadString(FLineStruct.ByteCount); + finally + EndWork(wmRead); + end; + end; + end; + IOHandler.ReadLnWait; + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +//This code was just pulled up from IdMessageClient so that logging could be added. +function TIdIMAP4.ReceiveHeader(AMsg: TIdMessage; const AAltTerm: string = ''): string; +begin + repeat + Result := IOHandler.ReadLn; + // Exchange Bug: Exchange sometimes returns . when getting a message instead of + // '' then a . - That is there is no seperation between the header and the message for an + // empty message. + if ((Length(AAltTerm) = 0) and (Result = '.')) or (Result = AAltTerm) then begin + Break; + end else if Length(Result) <> 0 then begin + AMsg.Headers.Append(Result); + end; + until False; + AMsg.ProcessHeaders; +end; + +function TIdIMAP4.Retrieve(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieve(AMsgNum, False, False, AMsg); +end; + +//Retrieves a whole message "raw" and saves it to file, while marking it read. +function TIdIMAP4.RetrieveNoDecodeToFile(const AMsgNum: Integer; ADestFile: string): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsNumberValid(AMsgNum); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(AMsgNum, False, False, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToFile(ADestFile); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file +function TIdIMAP4.RetrieveNoDecodeToFilePeek(const AMsgNum: Integer; ADestFile: string): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsNumberValid(AMsgNum); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(AMsgNum, False, True, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToFile(ADestFile); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file, while marking it read. +function TIdIMAP4.RetrieveNoDecodeToStream(const AMsgNum: Integer; AStream: TStream): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsNumberValid(AMsgNum); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(AMsgNum, False, False, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToStream(AStream); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file +function TIdIMAP4.RetrieveNoDecodeToStreamPeek(const AMsgNum: Integer; AStream: TStream): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsNumberValid(AMsgNum); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(AMsgNum, False, True, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToStream(AStream); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +function TIdIMAP4.RetrievePeek(const AMsgNum: Integer; AMsg: TIdMessage): Boolean; +begin + IsNumberValid(AMsgNum); + Result := InternalRetrieve(AMsgNum, False, True, AMsg); +end; + +function TIdIMAP4.UIDRetrieve(const AMsgUID: String; AMsg: TIdMessage): Boolean; +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieve(IndyStrToInt(AMsgUID), True, False, AMsg); +end; + +//Retrieves a whole message "raw" and saves it to file, while marking it read. +function TIdIMAP4.UIDRetrieveNoDecodeToFile(const AMsgUID: String; ADestFile: string): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsUIDValid(AMsgUID); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(IndyStrToInt(AMsgUID), True, False, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToFile(ADestFile); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file. +function TIdIMAP4.UIDRetrieveNoDecodeToFilePeek(const AMsgUID: String; ADestFile: string): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsUIDValid(AMsgUID); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(IndyStrToInt(AMsgUID), True, True, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToFile(ADestFile); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file, while marking it read. +function TIdIMAP4.UIDRetrieveNoDecodeToStream(const AMsgUID: String; AStream: TStream): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsUIDValid(AMsgUID); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(IndyStrToInt(AMsgUID), True, False, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToStream(AStream); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +//Retrieves a whole message "raw" and saves it to file. +function TIdIMAP4.UIDRetrieveNoDecodeToStreamPeek(const AMsgUID: String; AStream: TStream): Boolean; +var + LMsg: TIdMessage; +begin + Result := False; + IsUIDValid(AMsgUID); + LMsg := TIdMessage.Create(nil); + try + LMsg.NoDecode := True; + LMsg.NoEncode := True; + if InternalRetrieve(IndyStrToInt(AMsgUID), True, True, LMsg) then begin + {RLebeau 12/09/2012: NOT currently using the same workaround here that + is being used in AppendMsg() to avoid SMTP dot transparent output from + TIdMessage.SaveToStream(). The reason for this is because I don't + know how this method is being used and I don't want to break anything + that may be depending on that transparent output being generated...} + LMsg.SaveToStream(AStream); + Result := True; + end; + finally + FreeAndNil(LMsg); + end; +end; + +function TIdIMAP4.UIDRetrievePeek(const AMsgUID: String; AMsg: TIdMessage): Boolean; +begin + IsUIDValid(AMsgUID); + Result := InternalRetrieve(IndyStrToInt(AMsgUID), True, True, AMsg); +end; + +function TIdIMAP4.InternalRetrieve(const AMsgNum: Integer; AUseUID: Boolean; AUsePeek: Boolean; AMsg: TIdMessage): Boolean; +var + LStr: String; + LCmd: string; + LDestStream: TStream; + LHelper: TIdIMAP4WorkHelper; +begin + Result := False; + CheckConnectionState(csSelected); + LCmd := ''; + if AUseUID then begin + LCmd := LCmd + IMAP4Commands[cmdUID] + ' '; {Do not Localize} + end; + LCmd := LCmd + IMAP4Commands[cmdFetch] + ' ' + IntToStr ( AMsgNum ) + ' ('; {Do not Localize} + if AUsePeek then begin + LCmd := LCmd + IMAP4FetchDataItem[fdBodyPeek]; {Do not Localize} + end else begin + LCmd := LCmd + IMAP4FetchDataItem[fdRFC822]; {Do not Localize} + end; + LCmd := LCmd + ')'; {Do not Localize} + + SendCmd(NewCmdCounter, LCmd, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], True, False); + if LastCmdResult.Code = IMAP_OK then begin + {CC3: Catch "Connection reset by peer"...} + try + //Leave 3rd param as [] because ParseLastCmdResult can get a number of odd + //replies ( variants on Body[] )... + if (LastCmdResult.Text.Count < 1) or + (not ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [])) then + begin + Exit; + end; + {CC8: Retrieve via byte count instead of looking for terminator, + which was impossible to get working with all the different IMAP + servers because some left the terminator (LExpectedResponse) at + the end of a message line, so you could not decide if it was + part of the message or the terminator.} + AMsg.Clear; + if FLineStruct.ByteCount > 0 then begin + {Use a temporary memory block to suck the message into...} + // TODO: use TIdTCPStream instead and let TIdIOHandlerStreamMsg below read + // from this IOHandler directly so we don't have to waste memory reading + // potentially large messages... + LDestStream := TMemoryStream.Create; + try + LHelper := TIdIMAP4WorkHelper.Create(Self); + try + IOHandler.ReadStream(LDestStream, FLineStruct.ByteCount); //ReadStream uses OnWork, most other methods dont + finally + FreeAndNil(LHelper); + end; + {Feed stream into the standard message parser...} + LDestStream.Position := 0; + + {RLebeau 12/09/2012: this is a workaround to a design limitation in + TIdMessage.LoadFromStream(). It assumes the stream data is always + in an escaped format using SMTP dot transparency, but that is not + the case in IMAP! Until this design is corrected, we have to use a + workaround for now. This logic is copied from TIdMessage.LoadFromStream() + and slightly tweaked...} + + //AMsg.LoadFromStream(LDestStream); + {$IFDEF HAS_CLASS_HELPER} + AMsg.LoadFromStream(LDestStream, False, False); + {$ELSE} + TIdMessageHelper_LoadFromStream(AMsg, LDestStream, False, False); + {$ENDIF} + finally + FreeAndNil(LDestStream); + end; + end; + LStr := IOHandler.ReadLnWait; {Remove trailing line after the message, probably a ')' } + ParseLastCmdResultButAppendInfo(LStr); //There may be a UID or FLAGS in this + if GetInternalResponse(LastCmdCounter, [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]], False) = IMAP_OK then begin + AMsg.UID := FLineStruct.UID; + AMsg.Flags := FLineStruct.Flags; + Result := True; + end; + except + on E: EIdSocketError do begin + if ((E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET)) then begin + FConnectionState := csUnexpectedlyDisconnected; + end; + raise; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveAllHeaders(AMsgList: TIdMessageCollection): Boolean; +begin + Result := InternalRetrieveHeaders(AMsgList, -1); +end; + +function TIdIMAP4.RetrieveFirstHeaders(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; +begin + Result := InternalRetrieveHeaders(AMsgList, ACount); +end; + +function TIdIMAP4.InternalRetrieveHeaders(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; +var + LMsgItem : TIdMessageItem; + Ln : Integer; +begin + {CC2: This may get a response of "OK completed" if there are no messages} + CheckConnectionState(csSelected); + Result := False; + if AMsgList <> nil then begin + if (ACount < 0) or (ACount > FMailBox.TotalMsgs) then begin + ACount := FMailBox.TotalMsgs; + end; + // TODO: can this be accomplished using a single FETCH, similar to RetrieveAllEnvelopes()? + for Ln := 1 to ACount do begin + LMsgItem := AMsgList.Add; + if not RetrieveHeader(Ln, LMsgItem.Msg) then begin + Exit; + end; + end; + Result := True; + end; +end; + +function TIdIMAP4.RetrieveAllMsgs(AMsgList: TIdMessageCollection): Boolean; +begin + Result := InternalRetrieveMsgs(AMsgList, -1); +end; + +function TIdIMAP4.RetrieveFirstMsgs(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; +begin + Result := InternalRetrieveMsgs(AMsgList, ACount); +end; + +function TIdIMAP4.InternalRetrieveMsgs(AMsgList: TIdMessageCollection; ACount: Integer): Boolean; +var + LMsgItem : TIdMessageItem; + Ln : Integer; +begin + {CC2: This may get a response of "OK completed" if there are no messages} + CheckConnectionState(csSelected); + Result := False; + if AMsgList <> nil then begin + if (ACount < 0) or (ACount > FMailBox.TotalMsgs) then begin + ACount := FMailBox.TotalMsgs; + end; + // TODO: can this be accomplished using a single FETCH, similar to RetrieveAllEnvelopes()? + for Ln := 1 to ACount do begin + LMsgItem := AMsgList.Add; + if not Retrieve(Ln, LMsgItem.Msg) then begin + Exit; + end; + end; + Result := True; + end; +end; + +function TIdIMAP4.DeleteMsgs(const AMsgNumList: array of Integer): Boolean; +begin + Result := StoreFlags(AMsgNumList, sdAdd, [mfDeleted]); +end; + +function TIdIMAP4.UIDDeleteMsg(const AMsgUID: String): Boolean; +begin + IsUIDValid(AMsgUID); + Result := UIDStoreFlags(AMsgUID, sdAdd, [mfDeleted]); +end; + +function TIdIMAP4.UIDDeleteMsgs(const AMsgUIDList: array of String): Boolean; +begin + Result := UIDStoreFlags(AMsgUIDList, sdAdd, [mfDeleted]); +end; + +function TIdIMAP4.RetrieveMailBoxSize: Integer; +var + Ln : Integer; +begin + CheckConnectionState(csSelected); + Result := -1; + {CC2: This should not be checking FMailBox.TotalMsgs because the server may + have added messages to the mailbox unknown to us, and we are going to ask the + server anyway (if it's empty, we will return 0 anyway} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' 1:*' + ' (' + IMAP4FetchDataItem[fdRFC822Size] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + Result := 0; + for Ln := 0 to FMailBox.TotalMsgs - 1 do begin + if ParseLastCmdResult(LastCmdResult.Text[Ln], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Size]]) then begin + Result := Result + IndyStrToInt( FLineStruct.IMAPValue ); + end else begin + {CC2: Return -1, not 0, if we cannot parse the result...} + Result := -1; + Exit; + end; + end; + end; +end; + +function TIdIMAP4.UIDRetrieveMailBoxSize: Integer; +var + Ln : Integer; +begin + CheckConnectionState(csSelected); + Result := -1; + {CC2: This should not be checking FMailBox.TotalMsgs because the server may + have added messages to the mailbox unknown to us, and we are going to ask the + server anyway (if it's empty, we will return 0 anyway} + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' 1:*' + ' (' + IMAP4FetchDataItem[fdRFC822Size] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]]); + if LastCmdResult.Code = IMAP_OK then begin + Result := 0; + for Ln := 0 to FMailBox.TotalMsgs - 1 do begin + if ParseLastCmdResult(LastCmdResult.Text[Ln], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Size]]) then begin + Result := Result + IndyStrToInt(FLineStruct.IMAPValue); + end else begin + {CC2: Return -1, not 0, if we cannot parse the result...} + Result := -1; + Break; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveMsgSize(const AMsgNum: Integer): Integer; +begin + Result := -1; + IsNumberValid(AMsgNum); + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr ( AMsgNum ) + ' (' + IMAP4FetchDataItem[fdRFC822Size] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Size]]) then begin + Result := IndyStrToInt(FLineStruct.IMAPValue); + end; + end; +end; + +function TIdIMAP4.UIDRetrieveMsgSize(const AMsgUID: String): Integer; +begin + IsUIDValid(AMsgUID); + Result := -1; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdRFC822Size] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdRFC822Size]]) then begin + Result := IndyStrToInt(FLineStruct.IMAPValue); + end; + end; +end; + +function TIdIMAP4.CheckMsgSeen(const AMsgNum: Integer): Boolean; +var + LFlags: TIdMessageFlagsSet; +begin + IsNumberValid(AMsgNum); + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr(AMsgNum) + ' (' + IMAP4FetchDataItem[fdFlags] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdFlags]]) then begin + LFlags := FLineStruct.Flags; + if mfSeen in LFlags then begin + Result := True; + end; + end; + end; +end; + +function TIdIMAP4.UIDCheckMsgSeen(const AMsgUID: String): Boolean; +var + LFlags: TIdMessageFlagsSet; +begin + IsUIDValid(AMsgUID); + {Default to unseen, so if get no flags back (i.e. no \Seen flag) + we return False (i.e. we return it is unseen) + Some servers return nothing at all if no flags set (the better ones return an empty set).} + Result := False; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdFlags] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdFlags]]) then begin + LFlags := FLineStruct.Flags; + if mfSeen in LFlags then begin + Result := True; + end; + end; + end; +end; + +function TIdIMAP4.RetrieveFlags(const AMsgNum: Integer; var AFlags: {Pointer}TIdMessageFlagsSet): Boolean; +begin + IsNumberValid(AMsgNum); + Result := False; + {CC: Empty set to avoid returning resuts from a previous call if call fails} + AFlags := []; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdFetch] + ' ' + IntToStr (AMsgNum) + ' (' + IMAP4FetchDataItem[fdFlags] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdFlags]]) then begin + AFlags := FLineStruct.Flags; + Result := True; + end; + end; +end; + +function TIdIMAP4.UIDRetrieveFlags(const AMsgUID: String; var AFlags: TIdMessageFlagsSet): Boolean; +begin + IsUIDValid(AMsgUID); + Result := False; + {BUG FIX: Empty set to avoid returning resuts from a previous call if call fails} + AFlags := []; + CheckConnectionState(csSelected); + SendCmd(NewCmdCounter, + IMAP4Commands[cmdUID] + ' ' + IMAP4Commands[cmdFetch] + ' ' + AMsgUID + ' (' + IMAP4FetchDataItem[fdFlags] + ')', {Do not Localize} + [IMAP4Commands[cmdFetch], IMAP4Commands[cmdUID]]); + if LastCmdResult.Code = IMAP_OK then begin + if (LastCmdResult.Text.Count > 0) and + ParseLastCmdResult(LastCmdResult.Text[0], IMAP4Commands[cmdFetch], [IMAP4FetchDataItem[fdFlags]]) then begin + AFlags := FLineStruct.Flags; + Result := True; + end; + end; +end; + +function TIdIMAP4.GetConnectionStateName: String; +begin + case FConnectionState of + csAny : Result := RSIMAP4ConnectionStateAny; + csNonAuthenticated : Result := RSIMAP4ConnectionStateNonAuthenticated; + csAuthenticated : Result := RSIMAP4ConnectionStateAuthenticated; + csSelected : Result := RSIMAP4ConnectionStateSelected; + csUnexpectedlyDisconnected : Result := RSIMAP4ConnectionStateUnexpectedlyDisconnected; + end; +end; + +{ TIdIMAP4 Commands } + +{ Parser Functions... } + +{This recursively parses down. It gets either a line like: + + "text" "plain" ("charset" "ISO-8859-1") NIL NIL "7bit" 40 1 NIL NIL NIL + + which it parses into AThisImapPart, and we are done (at the end of the + recursive calls), or a line like: + + ("text" "plain"...NIL)("text" "html"...NIL) "alternative" ("boundary" "----bdry") NIL NIL + + when we need to add "alternative" and the boundary to this part, but recurse + down for the 1st two parts. } + +procedure TIdIMAP4.ParseImapPart(ABodyStructure: string; + AImapParts: TIdImapMessageParts; AThisImapPart: TIdImapMessagePart; AParentImapPart: TIdImapMessagePart; //ImapPart version + APartNumber: integer); +var + LNextImapPart: TIdImapMessagePart; + LSubParts: TStringList; + LPartNumber: integer; +begin + ABodyStructure := Trim(ABodyStructure); + AThisImapPart.FUnparsedEntry := ABodyStructure; + if ABodyStructure[1] <> '(' then begin {Do not Localize} + //We are at the bottom. Parse the low-level '"text" "plain"...' into this part. + ParseBodyStructurePart(ABodyStructure, nil, AThisImapPart); + if AParentImapPart = nil then begin + //This is the top-level part, and it is "text" "plain" etc, so it is not MIME... + AThisImapPart.Encoding := mePlainText; + AThisImapPart.ImapPartNumber := '1'; {Do not Localize} + AThisImapPart.ParentPart := -1; + end else begin + AThisImapPart.Encoding := meMIME; + AThisImapPart.ImapPartNumber := AParentImapPart.ImapPartNumber+'.'+IntToStr(APartNumber); {Do not Localize} + //If we are the first level down in MIME, the parent part was '', so trim... + if AThisImapPart.ImapPartNumber[1] = '.' then begin {Do not Localize} + AThisImapPart.ImapPartNumber := Copy(AThisImapPart.ImapPartNumber, 2, MaxInt); + end; + AThisImapPart.ParentPart := AParentImapPart.Index; + end; + end else begin + AThisImapPart.Encoding := meMIME; + if AParentImapPart = nil then begin + AThisImapPart.ImapPartNumber := ''; + AThisImapPart.ParentPart := -1; + end else begin + AThisImapPart.ImapPartNumber := AParentImapPart.ImapPartNumber+'.'+IntToStr(APartNumber); {Do not Localize} + //If we are the first level down in MIME, the parent part was '', so trim... + if AThisImapPart.ImapPartNumber[1] = '.' then begin {Do not Localize} + AThisImapPart.ImapPartNumber := Copy(AThisImapPart.ImapPartNumber, 2, MaxInt); + end; + AThisImapPart.ParentPart := AParentImapPart.Index; + end; + LSubParts := TStringList.Create; + try + ParseIntoBrackettedQuotedAndUnquotedParts(ABodyStructure, LSubParts, True); + LPartNumber := 1; + while (LSubParts.Count > 0) and (LSubParts[0] <> '') and (LSubParts[0][1] = '(') do begin {Do not Localize} + LNextImapPart := AImapParts.Add; + ParseImapPart(Copy(LSubParts[0], 2, Length(LSubParts[0])-2), AImapParts, LNextImapPart, AThisImapPart, LPartNumber); + LSubParts.Delete(0); + Inc(LPartNumber); + end; + if LSubParts.Count > 0 then begin + //LSubParts now (only) holds the params for this part... + AThisImapPart.FBodyType := LowerCase(GetNextQuotedParam(LSubParts[0], True)); //mixed, alternative + end else begin + AThisImapPart.FBodyType := ''; + end; + finally + FreeAndNil(LSubParts); + end; + end; +end; + +{ WARNING: Not used by writer, may have bugs. + + Version of ParseImapPart except using TIdMessageParts. + Added for compatibility with TIdMessage.MessageParts, + but does not have enough functionality for many IMAP functions. } + +procedure TIdIMAP4.ParseMessagePart(ABodyStructure: string; + AMessageParts: TIdMessageParts; AThisMessagePart: TIdMessagePart; AParentMessagePart: TIdMessagePart; //MessageParts version + APartNumber: integer); +var + LNextMessagePart: TIdMessagePart; + LSubParts: TStringList; + LPartNumber: integer; +begin + ABodyStructure := Trim(ABodyStructure); + if ABodyStructure[1] <> '(' then begin {Do not Localize} + //We are at the bottom. Parse this into this part. + ParseBodyStructurePart(ABodyStructure, AThisMessagePart, nil); + if AParentMessagePart = nil then begin + //This is the top-level part, and it is "text" "plain" etc, so it is not MIME... + AThisMessagePart.ParentPart := -1; + end else begin + AThisMessagePart.ParentPart := AParentMessagePart.Index; + end; + end else begin + LSubParts := TStringList.Create; + try + ParseIntoBrackettedQuotedAndUnquotedParts(ABodyStructure, LSubParts, True); + LPartNumber := 1; + while (LSubParts.Count > 0) and (LSubParts[0] <> '') and (LSubParts[0][1] = '(') do begin {Do not Localize} + LNextMessagePart := TIdAttachmentMemory.Create(AMessageParts); + ParseMessagePart(Copy(LSubParts[0], 2, Length(LSubParts[0])-2), AMessageParts, LNextMessagePart, AThisMessagePart, LPartNumber); + LSubParts.Delete(0); + Inc(LPartNumber); + end; + //LSubParts now (only) holds the params for this part... + if AParentMessagePart = nil then begin + AThisMessagePart.ParentPart := -1; + end else begin + AThisMessagePart.ParentPart := AParentMessagePart.Index; + end; + finally + FreeAndNil(LSubParts); + end; + end; +end; + +{CC2: Function added to support individual part retreival} +{ + If it's a single-part message, it won't be enclosed in brackets - it will be: + "body type": "TEXT", "application", "image", "MESSAGE" (followed by subtype RFC822 for envelopes, ignore) + "body subtype": "PLAIN", "octet-stream", "tiff", "html" + "body parameter parenthesized list": bracketted list of pairs ("CHARSET" "US-ASCII" "NAME" "cc.tif" "format" "flowed"), ("charset" "ISO-8859-1") + "body id": NIL, 986767766767887@fg.com + "body description": NIL, "Compiler diff" + "body encoding": "7bit" "8bit" "binary" (NO encoding used with these), "quoted-printable" "base64" "ietf-token" "x-token" + "body size" 2279 + "body lines" 48 (only present for some types, only those with "body type=text" and "body subtype=plain" that I found, if not present it WONT be a NIL, it just won't be there! However, it won't be needed) + NIL + ("inline" ("filename" "classbd.h")), ("attachment" ("filename" "DEGDAY.WB3")) + NIL + Example: + * 4 FETCH (BODYSTRUCTURE ("text" "plain" ("charset" "ISO-8859-1") NIL NIL "7bit" 40 1 NIL NIL NIL)) + --------------------------------------------------------------------------- + For most multi-part messages, each part will be bracketted: + ( (part 1 stuff) (part 2 stuff) "mixed" (boundary) NIL NIL ) + Example: + * 1 FETCH (BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii" "format" "flowed") + NIL NIL "7bit" 52 3 NIL NIL NIL)("text" "plain" ("name" "tnkin.txt") NIL NIL + "7bit" 28421 203 NIL ("inline" ("filename" "tnkin.txt")) NIL) "mixed" + ("boundary" "------------070105030104060407030601") NIL NIL)) + --------------------------------------------------------------------------- + Some multiparts are bracketted again. This is the "alternative" encoding, + part 1 has two parts, a plain-text part and a html part: + ( ( (part 1a stuff) (part 1b stuff) "alternative" (boundary) NIL NIL ) (part 2 stuff) "mixed" (boundary) NIL NIL ) + 1 2 2 1 + Example: + * 50 FETCH (BODYSTRUCTURE ((("text" "plain" ("charset" "ISO-8859-1") NIL NIL + "quoted-printable" 415 12 NIL NIL NIL)("text" "html" ("charset" "ISO-8859-1") + NIL NIL "quoted-printable" 1034 25 NIL NIL NIL) "alternative" ("boundary" + "----=_NextPart_001_0027_01C33A37.33CFE220") NIL NIL)("application" "x-zip-compressed" + ("name" "IdIMAP4.zip") NIL NIL "base64" 20572 NIL ("attachment" ("filename" + "IdIMAP4.zip")) NIL) "mixed" ("boundary" "----=_NextPart_000_0026_01C33A37.33CFE220") + NIL NIL) UID 62) +} +procedure TIdIMAP4.ParseBodyStructureResult(ABodyStructure: string; ATheParts: TIdMessageParts; AImapParts: TIdImapMessageParts); +begin + {CC7: New code uses a different parsing method that allows for multisection parts.} + if AImapParts <> nil then begin //Just sort out the ImapParts version for now + ParseImapPart(ABodyStructure, AImapParts, AImapParts.Add, nil, -1); + end; + if ATheParts <> nil then begin + ParseMessagePart(ABodyStructure, ATheParts, TIdAttachmentMemory.Create(ATheParts), nil, -1); + end; +end; + +procedure TIdIMAP4.ParseTheLine(ALine: string; APartsList: TStrings); +var + LTempList: TStringList; + LN: integer; + LStr, LWord: string; +begin + {Parse it and see what we get...} + LTempList := TStringList.Create; + try + ParseIntoParts(ALine, LTempList); + {Copy any parts from LTempList into the list of parts LPartsList...} + for LN := 0 to LTempList.Count-1 do begin + LStr := LTempList.Strings[LN]; + LWord := LowerCase(GetNextWord(LStr)); + if CharEquals(LStr, 1, '(') or (PosInStrArray(LWord, ['"text"', '"image"', '"application"'], False) <> -1) then begin {Do not Localize} + APartsList.Add(LStr); + end; + end; + finally + FreeAndNil(LTempList); + end; +end; + +procedure TIdIMAP4.ParseBodyStructurePart(APartString: string; AThePart: TIdMessagePart; + AImapPart: TIdImapMessagePart); + {CC3: Function added to support individual part retreival} +var + LParams: TStringList; +// LContentDispositionStuff: string; + LCharSet: String; + LFilename: string; + LDescription: string; + LTemp: string; + LSize: integer; + LPos: Integer; +begin + {Individual parameters may be strings like "text", NIL, a number, or bracketted pairs like + ("CHARSET" "US-ASCII" "NAME" "cc.tif" "format" "flowed")...} + {There are three common line formats, with differing numbers of parameters: + (a) "TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 2879 69 NIL NIL NIL + (a) "TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 2879 69 NIL NIL + (c) "TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 2879 69 + Note the last one only has 7 parameters, need to watch we don't index past the 7th!} + LParams := TStringList.Create; + try + ParseIntoParts(APartString, LParams); + {Build up strings into same format as used by message decoders...} + {Content Disposition: If present, may be at index 8 or 9...} + {CC8: Altered to allow for case where it may not be present at all (get "List + index out of bounds" error if try to access non-existent LParams[9])...} +// LContentDispositionStuff := ''; {Do not Localize} +// if LParams.Count > 9 then begin {Have an LParams[9]} +// if TextIsSame(LParams[9], 'NIL') then begin {Do not Localize} + {It's NIL at 9, must be at 8...} +// if TextIsSame(LParams[8], 'NIL') then begin {Do not Localize} +// LContentDispositionStuff := LParams[8]; +// end; +// end else begin + {It's not NIL, must be valid...} +// LContentDispositionStuff := LParams[9]; +// end; +// end else if LParams.Count > 8 then begin {Have an LParams[8]} +// if TextIsSame(LParams[8], 'NIL') then begin {Do not Localize} +// LContentDispositionStuff := LParams[8]; +// end; +// end; + + {Find and clean up the filename, if present...} + LFilename := ''; {Do not Localize} + LPos := IndyPos('"NAME"', UpperCase(APartString)); {Do not Localize} + if LPos > 0 then begin + LTemp := Copy(APartString, LPos+7, MaxInt); + LFilename := GetNextQuotedParam(LTemp, False); + end else + begin + LPos := IndyPos('"FILENAME"', UpperCase(APartString)); {Do not Localize} + if LPos > 0 then begin + LTemp := Copy(APartString, LPos+11, MaxInt); + LFilename := GetNextQuotedParam(LTemp, False); + end; + end; + {If the filename starts and ends with double-quotes, remove them...} + if Length(LFilename) > 1 then begin + if TextStartsWith(LFilename, '"') and TextEndsWith(LFilename, '"') then begin {Do not Localize} + LFilename := Copy(LFilename, 2, Length(LFilename)-2); + end; + end; + {CC7: The filename may be encoded, so decode it...} + if Length(LFilename) > 1 then begin + LFilename := DecodeHeader(LFilename); + end; + + LCharSet := ''; + if IndyPos('"CHARSET"', UpperCase(LParams[2])) > 0 then begin {Do not Localize} + LTemp := Copy(LParams[2], IndyPos('"CHARSET" ', UpperCase(LParams[2]))+10, MaxInt); {Do not Localize} + LCharSet := GetNextQuotedParam(LTemp, True); + end; + + LSize := 0; + if (not TextIsSame(LParams[6], 'NIL')) and (Length(LParams[6]) <> 0) then begin + LSize := IndyStrToInt(LParams[6]); {Do not Localize} + end; + + LDescription := ''; {Do not Localize} + if (LParams.Count > 9) and (not TextIsSame(LParams[9], 'NIL')) then begin {Do not Localize} + LDescription := GetNextQuotedParam(LParams[9], False); + end else if (LParams.Count > 8) and (not TextIsSame(LParams[8], 'NIL')) then begin {Do not Localize} + LDescription := GetNextQuotedParam(LParams[8], False); + end; + + if AThePart <> nil then begin + {Put into the same format as TIdMessage MessageParts...} + AThePart.ContentType := LParams[0]+'/'+LParams[1]+ParseBodyStructureSectionAsEquates(LParams[2]); {Do not Localize} + AThePart.ContentTransfer := LParams[5]; + //Watch out for BinHex4.0, the encoding is inferred from the Content-Type... + if IsHeaderMediaType(AThePart.ContentType, 'application/mac-binhex40') then begin {do not localize} + AThePart.ContentTransfer := 'binhex40'; {do not localize} + end; + AThePart.DisplayName := LFilename; + end; + + if AImapPart <> nil then begin + AImapPart.FBodyType := LParams[0]; + AImapPart.FBodySubType := LParams[1]; + AImapPart.FFileName := LFilename; + AImapPart.FDescription := LDescription; + AImapPart.FCharSet := LCharSet; + AImapPart.FContentTransferEncoding := LParams[5]; + AImapPart.FSize := LSize; + //Watch out for BinHex4.0, the encoding is inferred from the Content-Type... + if ( (TextIsSame(AImapPart.FBodyType, 'application')) {do not localize} + and (TextIsSame(AImapPart.FBodySubType, 'mac-binhex40')) ) then begin {do not localize} + AImapPart.FContentTransferEncoding := 'binhex40'; {do not localize} + end; + end; + finally + FreeAndNil(LParams); + end; +end; + +function ResolveQuotedSpecials(const AParam: string): string; +begin + // Handle quoted_specials, RFC1730 + // \ with other chars than " or \ after, looks illegal in RFC1730, but leave them untouched + // TODO: use StringsReplace() instead + //Result := StringsReplace(AParam, ['\"', '\\'], ['"', '\']); + Result := ReplaceAll(AParam, '\"', '"'); + Result := ReplaceAll(Result, '\\', '\'); +end; + +procedure TIdIMAP4.ParseIntoParts(APartString: string; AParams: TStrings); +var + LInPart: Integer; + LStartPos: Integer; + //don't rename this LParam. That's the same asa windows identifier + LParamater: string; + LBracketLevel: Integer; + Ln: Integer; + LInQuotesInsideBrackets: Boolean; + LInQuotedSpecial: Boolean; +begin + LStartPos := 0; {Stop compiler whining} + LBracketLevel := 0; {Stop compiler whining} + LInQuotesInsideBrackets := False; {Stop compiler whining} + LInQuotedSpecial := False; {Stop compiler whining} + LInPart := 0; {0 is not in a part, 1 is in a quote-delimited part, 2 is in a bracketted parameter-pair list} + for Ln := 1 to Length(APartString) do begin + if (LInPart = 1) or ((LInPart = 2) and LInQuotesInsideBrackets) then begin + if LInQuotedSpecial then begin + LInQuotedSpecial := False; + end + else if APartString[Ln] = '\' then begin {Do not Localize} + LInQuotedSpecial := True; + end + else if APartString[Ln] = '"' then begin {Do not Localize} + if LInPart = 1 then begin + LParamater := Copy(APartString, LStartPos+1, Ln-LStartPos-1); + AParams.Add(ResolveQuotedSpecials(LParamater)); + LInPart := 0; + end else begin + LInQuotesInsideBrackets := False; + end; + end; + end else if LInPart = 2 then begin + //We have to watch out that we don't close this entry on a closing bracket within + //quotes, like ("Blah" "Blah)Blah"), so monitor if we are in quotes within brackets. + if APartString[Ln] = '"' then begin {Do not Localize} + LInQuotesInsideBrackets := True; + LInQuotedSpecial := False; + end + else if APartString[Ln] = '(' then begin {Do not Localize} + Inc(LBracketLevel); + end + else if APartString[Ln] = ')' then begin {Do not Localize} + Dec(LBracketLevel); + if LBracketLevel = 0 then begin + LParamater := Copy(APartString, LStartPos+1, Ln-LStartPos-1); + AParams.Add(LParamater); + LInPart := 0; + end; + end; + end else if LInPart = 3 then begin + if APartString[Ln] = 'L' then begin {Do not Localize} + LParamater := Copy(APartString, LStartPos, Ln-LStartPos+1); + AParams.Add(LParamater); + LInPart := 0; + end; + end else if LInPart = 4 then begin + if not IsNumeric(APartString[Ln]) then begin + LParamater := Copy(APartString, LStartPos, Ln-LStartPos); + AParams.Add(LParamater); + LInPart := 0; + end; + end else if APartString[Ln] = '"' then begin {Do not Localize} + {Start of a quoted param like "text"} + LStartPos := Ln; + LInPart := 1; + LInQuotedSpecial := False; + end else if APartString[Ln] = '(' then begin {Do not Localize} + {Start of a set of paired parameter/value strings within brackets, + such as ("charset" "us-ascii"). Note these can be nested (bracket pairs + within bracket pairs) } + LStartPos := Ln; + LInPart := 2; + LBracketLevel := 1; + LInQuotesInsideBrackets := False; + end else if TextIsSame(APartString[Ln], 'N') then begin {Do not Localize} + {Start of a NIL entry} + LStartPos := Ln; + LInPart := 3; + end else if IsNumeric(APartString[Ln]) then begin + {Start of a numeric entry like 12345} + LStartPos := Ln; + LInPart := 4; + end; + end; + {We could be in a numeric entry when we hit the end of the line...} + if LInPart = 4 then begin + LParamater := Copy(APartString, LStartPos, MaxInt); + AParams.Add(LParamater); + end; +end; + +procedure TIdIMAP4.ParseIntoBrackettedQuotedAndUnquotedParts(APartString: string; AParams: TStrings; AKeepBrackets: Boolean); +var + LInPart: Integer; + LStartPos: Integer; + //don't rename this back to LParam, that's a Windows identifier. + LParamater: string; + LBracketLevel: Integer; + Ln: Integer; + LInQuotesInsideBrackets: Boolean; + LInQuotedSpecial: Boolean; +begin + {Break: + * LIST (\UnMarked \AnotherFlag) "/" "Mailbox name" + into: + * + LIST + (\UnMarked \AnotherFlag) + "/" + "Mailbox name" + If AKeepBrackets is false, return '\UnMarked \AnotherFlag' instead of '(\UnMarked \AnotherFlag)' + } + AParams.Clear; + LStartPos := 0; {Stop compiler whining} + LBracketLevel := 0; {Stop compiler whining} + LInQuotesInsideBrackets := False; {Stop compiler whining} + LInQuotedSpecial := False; {Stop compiler whining} + LInPart := 0; {0 is not in a part, 1 is in a quote-delimited part, 2 is in a bracketted part, 3 is a word} + APartString := Trim(APartString); + for Ln := 1 to Length(APartString) do begin + if (LInPart = 1) or ((LInPart = 2) and LInQuotesInsideBrackets) then begin + if LInQuotedSpecial then begin + LInQuotedSpecial := False; + end + else if APartString[Ln] = '\' then begin {Do not Localize} + LInQuotedSpecial := True; + end + else if APartString[Ln] = '"' then begin {Do not Localize} + if LInPart = 1 then begin + LParamater := Copy(APartString, LStartPos+1, Ln-LStartPos-1); + AParams.Add(ResolveQuotedSpecials(LParamater)); + LInPart := 0; + end else begin + LInQuotesInsideBrackets := False; + end; + end; + end else if LInPart = 2 then begin + //We have to watch out that we don't close this entry on a closing bracket within + //quotes, like ("Blah" "Blah)Blah"), so monitor if we are in quotes within brackets. + if APartString[Ln] = '"' then begin {Do not Localize} + LInQuotesInsideBrackets := True; + LInQuotedSpecial := False; + end + else if APartString[Ln] = '(' then begin {Do not Localize} + Inc(LBracketLevel); + end + else if APartString[Ln] = ')' then begin {Do not Localize} + Dec(LBracketLevel); + if LBracketLevel = 0 then begin + if AKeepBrackets then begin + LParamater := Copy(APartString, LStartPos, Ln-LStartPos+1); + end else begin + LParamater := Copy(APartString, LStartPos+1, Ln-LStartPos-1); + end; + AParams.Add(LParamater); + LInPart := 0; + end; + end; + end else if LInPart = 3 then begin + if APartString[Ln] = ' ' then begin {Do not Localize} + LParamater := Copy(APartString, LStartPos, Ln-LStartPos); + AParams.Add(LParamater); + LInPart := 0; + end; + end else if APartString[Ln] = '"' then begin {Do not Localize} + {Start of a quoted param like "text"} + LStartPos := Ln; + LInPart := 1; + LInQuotedSpecial := False; + end else if APartString[Ln] = '(' then begin {Do not Localize} + {Start of a set of paired parameter/value strings within brackets, + such as ("charset" "us-ascii"). Note these can be nested (bracket pairs + within bracket pairs) } + LStartPos := Ln; + LInPart := 2; + LBracketLevel := 1; + LInQuotesInsideBrackets := False; + end else if APartString[Ln] <> ' ' then begin {Do not Localize} + {Start of an entry like 12345} + LStartPos := Ln; + LInPart := 3; + end; + end; + {We could be in an entry when we hit the end of the line...} + if LInPart = 3 then begin + LParamater := Copy(APartString, LStartPos, MaxInt); + AParams.Add(LParamater); + end else if LInPart = 2 then begin + if AKeepBrackets then begin + LParamater := Copy(APartString, LStartPos, MaxInt); + end else begin + LParamater := Copy(APartString, LStartPos+1, MaxInt); + end; + if (not AKeepBrackets) and TextEndsWith(LParamater, ')') then begin {Do not Localize} + LParamater := Copy(LParamater, 1, Length(LParamater)-1); + end; + AParams.Add(LParamater); + end else if LInPart = 1 then begin + LParamater := Copy(APartString, LStartPos+1, MaxInt); + if TextEndsWith(LParamater, '"') then begin {Do not Localize} + LParamater := Copy(LParamater, 1, Length(LParamater)-1); + end; + AParams.Add(ResolveQuotedSpecials(LParamater)); + end; +end; + +function TIdIMAP4.ParseBodyStructureSectionAsEquates(AParam: string): string; + {Convert: + "Name1" "Value1" "Name2" "Value2" + to: + ; Name1="Value1"; Name2="Value2" + } +var + LParse: TStringList; + LN: integer; +begin + Result := ''; {Do not Localize} + if (Length(AParam) = 0) or TextIsSame(AParam, 'NIL') then begin {Do not Localize} + Exit; + end; + LParse := TStringList.Create; + try + BreakApartParamsInQuotes(AParam, LParse); + if LParse.Count < 2 then begin + Exit; + end; + if ((LParse.Count mod 2) <> 0) then begin + Exit; + end; + for LN := 0 to ((LParse.Count div 2)-1) do begin + Result := Result + '; ' + Copy(LParse[LN*2], 2, Length(LParse[LN*2])-2) + '=' + LParse[(LN*2)+1]; {Do not Localize} + end; + finally + FreeAndNil(LParse); + end; +end; + +function TIdIMAP4.ParseBodyStructureSectionAsEquates2(AParam: string): string; + {Convert: + "Name1" ("Name2" "Value2") + to: + Name1; Name2="Value2" + } +var + LParse: TStringList; + LParams: string; +begin + Result := ''; {Do not Localize} + if (Length(AParam) = 0) or TextIsSame(AParam, 'NIL') then begin {Do not Localize} + Exit; + end; + LParse := TStringList.Create; + try + BreakApart(AParam, ' ', LParse); {Do not Localize} + if LParse.Count < 3 then begin + Exit; + end; + LParams := Copy(AParam, Pos('(', AParam)+1, MaxInt); {Do not Localize} + LParams := Copy(LParams, 1, Length(LParams)-1); + LParams := ParseBodyStructureSectionAsEquates(LParams); + if Length(LParams) = 0 then begin {Do not Localize} + Result := Copy(LParse[0], 2, Length(LParse[0])-2) + LParams; + end; + finally + FreeAndNil(LParse); + end; +end; + +function TIdIMAP4.GetNextWord(AParam: string): string; +var + LPos: integer; +begin + Result := ''; {Do not Localize} + AParam := Trim(AParam); + LPos := Pos(' ', AParam); {Do not Localize} + if LPos = 0 then begin + Exit; + end; + Result := Copy(AParam, 1, LPos-1); +end; + +function TIdIMAP4.GetNextQuotedParam(AParam: string; ARemoveQuotes: Boolean): string; +{If AParam is: +"file name.ext" NIL NIL +then this returns: +"file name.ext" +Note it returns the quotes, UNLESS ARemoveQuotes is True. +Also note that if AParam does NOT start with a quote, it returns the next word. +} +var + LN: integer; + LPos: integer; +begin + Result := ''; + {CCB: Modified code so it did not access past the end of the string if + AParam was not actually in quotes (e.g. the MIME boundary parameter + is only optionally in quotes).} + LN := 1; + {Skip any preceding spaces...} + //TODO: use TrimLeft(AParam) instead + while (LN <= Length(AParam)) and (AParam[LN] = ' ') do begin {Do not Localize} + LN := LN + 1; + end; + if LN > Length(AParam) then begin + Exit; + end; + if AParam[LN] <> '"' then begin {Do not Localize} + {Not actually enclosed in quotes. Must be a single word.} + // TODO: use Fetch(AParam) instead + AParam := Copy(AParam, LN, MaxInt); + LPos := Pos(' ', AParam); {Do not Localize} + if LPos > 0 then begin + {Strip off this word...} + Result := Copy(AParam, 1, LPos-1); + end else begin + {This is the last word on the line, return it all...} + Result := AParam; + end; + end else begin + {It starts with a quote...} + // TODO: use Fetch(AParam, '"') instead + // TODO: do we need to handle escaped characters? + AParam := Copy(AParam, LN, MaxInt); + LN := 2; + while (LN <= Length(AParam)) and (AParam[LN] <> '"') do begin {Do not Localize} + LN := LN + 1; + end; + Result := Copy(AParam, 1, LN); + if ARemoveQuotes then begin + Result := Copy(Result, 2, Length(Result)-2); + end; + end; +end; + +procedure TIdIMAP4.BreakApartParamsInQuotes(const AParam: string; AParsedList: TStrings); +var + Ln : Integer; + LStartPos: Integer; +begin + LStartPos := -1; + AParsedList.Clear; + for Ln := 1 to Length(AParam) do begin + if AParam[LN] = '"' then begin {Do not Localize} + if LStartPos > -1 then begin + {The end of a quoted parameter...} + AParsedList.Add(Copy(AParam, LStartPos, LN-LStartPos+1)); + LStartPos := -1; + end else begin + {The start of a quoted parameter...} + LStartPos := Ln; + end; + end; + end; +end; + +procedure TIdIMAP4.ParseExpungeResult(AMB: TIdMailBox; ACmdResultDetails: TStrings); +var + Ln : Integer; + LSlExpunge : TStringList; +begin + SetLength(AMB.DeletedMsgs, 0); + LSlExpunge := TStringList.Create; + try + if ACmdResultDetails.Count > 1 then begin + for Ln := 0 to ACmdResultDetails.Count - 1 do begin + BreakApart(ACmdResultDetails[Ln], ' ', LSlExpunge); {Do not Localize} + if TextIsSame(LSlExpunge[1], IMAP4Commands[cmdExpunge]) then begin + SetLength(AMB.DeletedMsgs, (Length(AMB.DeletedMsgs) + 1)); + AMB.DeletedMsgs[Length(AMB.DeletedMsgs) - 1] := IndyStrToInt(LSlExpunge[0]); + end; + LSlExpunge.Clear; + end; + end; + finally + FreeAndNil(LSlExpunge); + end; +end; + +procedure TIdIMAP4.ParseMessageFlagString(AFlagsList: String; var AFlags: TIdMessageFlagsSet); + {CC5: Note this only supports the system flags defined in RFC 2060.} +var + LSlFlags : TStringList; + Ln, I : Integer; +begin + AFlags := []; + LSlFlags := TStringList.Create; + try + BreakApart(AFlagsList, ' ', LSlFlags); {Do not Localize} + for Ln := 0 to LSlFlags.Count-1 do begin + I := PosInStrArray( + LSlFlags[Ln], + [MessageFlags[mfAnswered], MessageFlags[mfFlagged], MessageFlags[mfDeleted], MessageFlags[mfDraft], MessageFlags[mfSeen], MessageFlags[mfRecent]], + False); + case I of + 0..5: Include(AFlags, TIdMessageFlags(I)); + end; + end; + finally + FreeAndNil(LSlFlags); + end; +end; + +procedure TIdIMAP4.ParseMailBoxAttributeString(AAttributesList: String; var AAttributes: TIdMailBoxAttributesSet); +var + LSlAttributes : TStringList; + Ln : Integer; + I: Integer; +begin + AAttributes := []; + LSlAttributes := TStringList.Create; + try + BreakApart(AAttributesList, ' ', LSlAttributes); {Do not Localize} + for Ln := 0 to LSlAttributes.Count - 1 do begin + I := PosInStrArray( + LSlAttributes[Ln], + [MailBoxAttributes[maNoinferiors], MailBoxAttributes[maNoselect], MailBoxAttributes[maMarked], MailBoxAttributes[maUnmarked]], + False); + case I of + 0..3: Include(AAttributes, TIdMailBoxAttributes(I)); + end; + end; + finally + FreeAndNil(LSlAttributes); + end; +end; + +procedure TIdIMAP4.ParseSearchResult(AMB: TIdMailBox; ACmdResultDetails: TStrings); +var Ln: Integer; + LSlSearch: TStringList; +begin + LSlSearch := TStringList.Create; + try + SetLength(AMB.SearchResult, 0); + if ACmdResultDetails.Count > 0 then begin + if Pos(IMAP4Commands[cmdSearch], ACmdResultDetails[0]) > 0 then begin + BreakApart(ACmdResultDetails[0], ' ', LSlSearch); {Do not Localize} + for Ln := 1 to LSlSearch.Count - 1 do begin + // TODO: for a UID search, store LSlSearch[Ln] as-is without converting it to an Integer... + SetLength(AMB.SearchResult, (Length(AMB.SearchResult) + 1)); + AMB.SearchResult[Length(AMB.SearchResult) - 1] := IndyStrToInt(LSlSearch[Ln]); + end; + end; + end; + finally + FreeAndNil(LSlSearch); + end; +end; + +procedure TIdIMAP4.ParseStatusResult(AMB: TIdMailBox; ACmdResultDetails: TStrings); +var + Ln: Integer; + LRespStr : String; + LStatStr: String; + LStatPos: Integer; + LSlStatus : TStringList; +begin + LSlStatus := TStringList.Create; + try + if ACmdResultDetails.Count > 0 then + begin + // TODO: convert server response to uppercase? + LRespStr := Trim(ACmdResultDetails[0]); + LStatPos := Pos(IMAP4Commands[cmdStatus], LRespStr); + if (LStatPos > 0) then + begin + LStatStr := Trim(Copy(LRespStr, + LStatPos+Length(IMAP4Commands[cmdStatus]), Length(LRespStr))); + AMB.Name := Trim(Fetch(LStatStr, '(', True)); {do not localize} + if TextEndsWith(LStatStr, ')') then begin {do not localize} + IdDelete(LStatStr, Length(LStatStr), 1); + end; + BreakApart(LStatStr, ' ', LSlStatus); {do not localize} + // find status data items by name, values are on following line + Ln := LSlStatus.IndexOf(IMAP4StatusDataItem[mdMessages]); + if Ln <> -1 then begin + AMB.TotalMsgs := IndyStrToInt(LSlStatus[Ln + 1]); + end; + Ln := LSlStatus.IndexOf(IMAP4StatusDataItem[mdRecent]); + if Ln <> -1 then begin + AMB.RecentMsgs := IndyStrToInt(LSlStatus[Ln + 1]); + end; + Ln := LSlStatus.IndexOf(IMAP4StatusDataItem[mdUnseen]); + if Ln <> -1 then begin + AMB.UnseenMsgs := IndyStrToInt(LSlStatus[Ln + 1]); + end; + Ln := LSlStatus.IndexOf(IMAP4StatusDataItem[mdUIDNext]); + if Ln <> -1 then begin + AMB.UIDNext := LSlStatus[Ln + 1]; + end; + Ln := LSlStatus.IndexOf(IMAP4StatusDataItem[mdUIDValidity]); + if Ln <> -1 then begin + AMB.UIDValidity := LSlStatus[Ln + 1]; + end; + end; + end; + finally + FreeAndNil(LSlStatus); + end; +end; + +procedure TIdIMAP4.ParseSelectResult(AMB : TIdMailBox; ACmdResultDetails: TStrings); +var + Ln : Integer; + LStr : String; + LFlags: TIdMessageFlagsSet; + LLine: String; + LPos: Integer; +begin + AMB.Clear; + for Ln := 0 to ACmdResultDetails.Count - 1 do begin + LLine := ACmdResultDetails[Ln]; + LPos := Pos(' EXISTS', LLine); {Do not Localize} + if LPos > 0 then begin + AMB.TotalMsgs := IndyStrToInt(Copy(LLine, 1, LPos - 1)); + Continue; + end; + LPos := Pos(' RECENT', LLine); {Do not Localize} + if LPos > 0 then begin + AMB.RecentMsgs := IndyStrToInt(Copy(LLine, 1, LPos - 1)); {Do not Localize} + Continue; + end; + LPos := Pos('[UIDVALIDITY ', LLine); {Do not Localize} + if LPos > 0 then begin + Inc(LPos, 13); + AMB.UIDValidity := Trim(Copy(LLine, LPos, (Integer(PosIdx(']', LLine, LPos)) - LPos))); {Do not Localize} + Continue; + end; + LPos := Pos('[UIDNEXT ', LLine); {Do not Localize} + if LPos > 0 then begin + Inc(LPos, 9); + AMB.UIDNext := Trim(Copy(LLine, LPos, (Integer(PosIdx(']', LLine, LPos)) - LPos))); {Do not Localize} + Continue; + end; + LPos := Pos('[PERMANENTFLAGS ', LLine); {Do not Localize} + if LPos > 0 then begin {Do not Localize} + LPos := PosIdx('(', LLine, LPos + 16) + 1; {Do not Localize} + ParseMessageFlagString(Copy(LLine, LPos, Integer(PosIdx(')', LLine, LPos)) - LPos), LFlags); {Do not Localize} + AMB.ChangeableFlags := LFlags; + Continue; + end; + LPos := Pos('FLAGS ', LLine); {Do not Localize} + if LPos > 0 then begin + LPos := PosIdx('(', LLine, LPos + 6) + 1; {Do not Localize} + ParseMessageFlagString(Copy(LLine, LPos, (Integer(PosIdx(')', LLine, LPos)) - LPos)), LFlags); {Do not Localize} + AMB.Flags := LFlags; + Continue; + end; + LPos := Pos('[UNSEEN ', LLine); {Do not Localize} + if LPos> 0 then begin + Inc(LPos, 8); + AMB.FirstUnseenMsg := IndyStrToInt(Copy(LLine, LPos, (Integer(PosIdx(']', LLine, LPos)) - LPos))); {Do not Localize} + Continue; + end; + LPos := Pos('[READ-', LLine); {Do not Localize} + if LPos > 0 then begin + Inc(LPos, 6); + LStr := Trim(Copy(LLine, LPos, Integer(PosIdx(']', LLine, LPos)) - LPos)); {Do not Localize} + {CCB: AMB.State ambiguous unless coded response received - default to msReadOnly...} + if TextIsSame(LStr, 'WRITE') then begin {Do not Localize} + AMB.State := msReadWrite; + end else {if TextIsSame(LStr, 'ONLY') then} begin {Do not Localize} + AMB.State := msReadOnly; + end; + Continue; + end; + LPos := Pos('[ALERT]', LLine); {Do not Localize} + if LPos > 0 then begin + LStr := Trim(Copy(LLine, LPos + 7, MaxInt)); + if Length(LStr) <> 0 then begin + DoAlert(LStr); + end; + Continue; + end; + end; +end; + +procedure TIdIMAP4.ParseListResult(AMBList: TStrings; ACmdResultDetails: TStrings); +begin + InternalParseListResult(IMAP4Commands[cmdList], AMBList, ACmdResultDetails); +end; + +procedure TIdIMAP4.InternalParseListResult(ACmd: string; AMBList: TStrings; ACmdResultDetails: TStrings); +var Ln : Integer; + LSlRetrieve : TStringList; + LStr : String; + LWord: string; +begin + AMBList.Clear; + LSlRetrieve := TStringList.Create; + try + for Ln := 0 to ACmdResultDetails.Count - 1 do begin + LStr := ACmdResultDetails[Ln]; + //Todo: Get mail box attributes here + {CC2: Could put mailbox attributes in AMBList's Objects property?} + {The line is of the form: + * LIST (\UnMarked \AnotherFlag) "/" "Mailbox name" + } + {CCA: code modified because some servers return NIL as the mailbox + separator, i.e.: + * LIST (\UnMarked \AnotherFlag) NIL "Mailbox name" + } + + ParseIntoBrackettedQuotedAndUnquotedParts(LStr, LSlRetrieve, False); + if LSlRetrieve.Count > 3 then begin + //Make sure 1st word is LIST (may be an unsolicited response)... + if TextIsSame(LSlRetrieve[0], {IMAP4Commands[cmdList]} ACmd) then begin + {Get the mailbox separator...} + LWord := Trim(LSlRetrieve[LSlRetrieve.Count-2]); + if TextIsSame(LWord, 'NIL') or (LWord = '') then begin {Do not Localize} + FMailBoxSeparator := #0; + end else begin + FMailBoxSeparator := LWord[1]; + end; + {Now get the mailbox name...} + LWord := Trim(LSlRetrieve[LSlRetrieve.Count-1]); + AMBList.Add(DoMUTFDecode(LWord)); + end; + end; + end; + finally + FreeAndNil(LSlRetrieve); + end; +end; + +procedure TIdIMAP4.ParseLSubResult(AMBList: TStrings; ACmdResultDetails: TStrings); +begin + InternalParseListResult(IMAP4Commands[cmdLSub], AMBList, ACmdResultDetails); +end; + +procedure TIdIMAP4.ParseEnvelopeResult(AMsg: TIdMessage; ACmdResultStr: String); + + procedure DecodeEnvelopeAddress(const AAddressStr: String; AEmailAddressItem: TIdEmailAddressItem); overload; + var + LStr, LTemp: String; + I: Integer; + {$IFNDEF DOTNET} + LPChar: PChar; + {$ENDIF} + begin + if TextStartsWith(AAddressStr, '(') and TextEndsWith(AAddressStr, ')') and {Do not Localize} + Assigned(AEmailAddressItem) then begin + LStr := Copy(AAddressStr, 2, Length (AAddressStr) - 2); + //Gets the name part + if TextStartsWith(LStr, 'NIL ') then begin {Do not Localize} + LStr := Copy(LStr, 5, MaxInt); {Do not Localize} + end + else if TextStartsWith(LStr, '{') then begin {Do not Localize} + LStr := Copy(LStr, Pos('}', LStr) + 1, MaxInt); {Do not Localize} + I := Pos('" ', LStr); + AEmailAddressItem.Name := Copy(LStr, 1, I-1); {Do not Localize} + LStr := Copy(LStr, I+2, MaxInt); {Do not Localize} + end else begin + I := Pos('" ', LStr); + LTemp := Copy(LStr, 1, I); + {$IFDEF DOTNET} + AEmailAddressItem.Name := Copy(LTemp, 2, Length(LTemp)-2); {ExtractQuotedStr ( LTemp, '"' ); {Do not Localize} + {$ELSE} + LPChar := PChar(LTemp); + AEmailAddressItem.Name := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + LStr := Copy(LStr, I+2, MaxInt); {Do not Localize} + end; + //Gets the source root part + if TextStartsWith(LStr, 'NIL ') then begin {Do not Localize} + LStr := Copy(LStr, 5, MaxInt); {Do not Localize} + end else begin + I := Pos('" ', LStr); + LTemp := Copy(LStr, 1, I); + {$IFDEF DOTNET} + AEmailAddressItem.Name := Copy(LTemp, 2, Length(LTemp)-2); {AnsiExtractQuotedStr ( LTemp, '"' ); {Do not Localize} + {$ELSE} + LPChar := PChar(LTemp); + AEmailAddressItem.Name := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + LStr := Copy(LStr, I+2, MaxInt); {Do not Localize} + end; + //Gets the mailbox name part + if TextStartsWith(LStr, 'NIL ') then begin {Do not Localize} + LStr := Copy(LStr, 5, MaxInt); {Do not Localize} + end else begin + I := Pos('" ', LStr); + LTemp := Copy(LStr, 1, I); {Do not Localize} + {$IFDEF DOTNET} + AEmailAddressItem.Address := Copy(LTemp, 2, Length(LTemp)-2); {AnsiExtractQuotedStr ( LTemp, '"' ); {Do not Localize} + {$ELSE} + LPChar := PChar(LTemp); + AEmailAddressItem.Address := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + LStr := Copy(LStr, I+2, MaxInt); {Do not Localize} + end; + //Gets the host name part + if not TextIsSame(LStr, 'NIL') then begin {Do not Localize} + LTemp := Copy(LStr, 1, MaxInt); + {$IFDEF DOTNET} + AEmailAddressItem.Address := AEmailAddressItem.Address + '@' + {Do not Localize} + Copy(LTemp, 2, Length(LTemp)-2); {AnsiExtractQuotedStr ( LTemp, '"' ); {Do not Localize} + {$ELSE} + LPChar := PChar(LTemp); + AEmailAddressItem.Address := AEmailAddressItem.Address + '@' + {Do not Localize} + AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + end; + end; + end; + + procedure DecodeEnvelopeAddress(const AAddressStr: String; AEmailAddressList: TIdEmailAddressList); overload; + var + LStr: String; + I: Integer; + begin + if TextStartsWith(AAddressStr, '(') and TextEndsWith(AAddressStr, ')') and {Do not Localize} + Assigned(AEmailAddressList) then begin + LStr := Copy(AAddressStr, 2, Length (AAddressStr) - 2); + repeat + I := Pos(')', LStr); + if I = 0 then begin + Break; + end; + DecodeEnvelopeAddress(Copy(LStr, 1, I), AEmailAddressList.Add); {Do not Localize} + LStr := Trim(Copy(LStr, I+1, MaxInt)); {Do not Localize} + until False; + end; + end; + +var + LStr, LTemp: String; + I: Integer; + {$IFNDEF DOTNET} + LPChar: PChar; + {$ENDIF} +begin + //The fields of the envelope structure are in the + //following order: date, subject, from, sender, + //reply-to, to, cc, bcc, in-reply-to, and message-id. + //The date, subject, in-reply-to, and message-id + //fields are strings. The from, sender, reply-to, + //to, cc, and bcc fields are parenthesized lists of + //address structures. + + //An address structure is a parenthesized list that + //describes an electronic mail address. The fields + //of an address structure are in the following order: + //personal name, [SMTP] at-domain-list (source + //route), mailbox name, and host name. + + //* 4 FETCH (ENVELOPE ("Sun, 15 Jul 2001 02:56:45 -0700 (PDT)" "Your Borland Commu + //nity Account Activation Code" (("Borland Community" NIL "mailbot" "borland.com") + //) NIL NIL (("" NIL "name" "company.com")) NIL NIL NIL "<200107150956.CAA1 + //8152@borland.com>")) + + {CC5: Cleared out any existing fields to avoid mangling new entries with old/stale ones.} + //Extract envelope date field + AMsg.Date := 0; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos('" ', ACmdResultStr); {Do not Localize} + LTemp := Copy(ACmdResultStr, 1, I); + {$IFDEF DOTNET} + LStr := Copy(LTemp, 2, Length(LTemp)-2); + {$ELSE} + LPChar := PChar(LTemp); + LStr := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + AMsg.Date := GMTToLocalDateTime(LStr); + ACmdResultStr := Copy(ACmdResultStr, I+2, MaxInt); + end; + //Extract envelope subject field + AMsg.Subject := ''; {Do not Localize} + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + if TextStartsWith(ACmdResultStr, '{') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, Pos('}', ACmdResultStr) + 1, MaxInt); {Do not Localize} + I := Pos(' ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I-1); + AMsg.Subject := LStr; + ACmdResultStr := Copy(ACmdResultStr, I+1, MaxInt); {Do not Localize} + end else begin + I := Pos('" ', ACmdResultStr); {Do not Localize} + LTemp := Copy(ACmdResultStr, 1, I); + {$IFDEF DOTNET} + LStr := Copy(LTemp, 2, Length(LTemp)-2); + {$ELSE} + LPChar := PChar(LTemp); + LStr := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + AMsg.Subject := LStr; + ACmdResultStr := Copy(ACmdResultStr, I+2, MaxInt); {Do not Localize} + end; + end; + //Extract envelope from field + AMsg.FromList.Clear; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos(')) ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I+1); + DecodeEnvelopeAddress(LStr, AMsg.FromList); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope sender field + AMsg.Sender.Name := ''; {Do not Localize} + AMsg.Sender.Address := ''; {Do not Localize} + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + {CC5: Fix parsing of sender...} + I := Pos(')) ', ACmdResultStr); + LStr := Copy(ACmdResultStr, 2, I-1); + DecodeEnvelopeAddress(LStr, AMsg.Sender); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope reply-to field + AMsg.ReplyTo.Clear; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos(')) ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I+1); + DecodeEnvelopeAddress(LStr, AMsg.ReplyTo); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope to field + AMsg.Recipients.Clear; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos(')) ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I+1); + DecodeEnvelopeAddress(LStr, AMsg.Recipients); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope cc field + AMsg.CCList.Clear; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos(')) ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I+1); + DecodeEnvelopeAddress(LStr, AMsg.CCList); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope bcc field + AMsg.BccList.Clear; + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos(')) ', ACmdResultStr); {Do not Localize} + LStr := Copy(ACmdResultStr, 1, I+1); + DecodeEnvelopeAddress(LStr, AMsg.BccList); + ACmdResultStr := Copy(ACmdResultStr, I+3, MaxInt); + end; + //Extract envelope in-reply-to field + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + I := Pos('" ', ACmdResultStr); {Do not Localize} + LTemp := Copy(ACmdResultStr, 1, I); + {$IFDEF DOTNET} + LStr := Copy(LTemp, 2, Length(LTemp)-2); + {$ELSE} + LPChar := PChar(LTemp); + LStr := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + AMsg.InReplyTo := LStr; + ACmdResultStr := Copy(ACmdResultStr, I+2, MaxInt); + end; + //Extract envelope message-id field + AMsg.MsgId := ''; {Do not Localize} + if TextStartsWith(ACmdResultStr, 'NIL ') then begin {Do not Localize} + ACmdResultStr := Copy(ACmdResultStr, 5, MaxInt); + end else begin + {$IFDEF DOTNET} + LStr := Copy(ACmdResultStr, 2, Length(ACmdResultStr)-2); + {$ELSE} + LPChar := PChar(ACmdResultStr); + LStr := AnsiExtractQuotedStr(LPChar, '"'); {Do not Localize} + {$ENDIF} + AMsg.MsgId := Trim(LStr); + end; +end; + +function TIdIMAP4.ParseLastCmdResult(ALine: string; AExpectedCommand: string; AExpectedIMAPFunction: array of string): Boolean; +var + LPos: integer; + LWord: string; + LWords: TStringList; + LN: Integer; + LWordInExpectedIMAPFunction: Boolean; +begin + Result := False; + LWordInExpectedIMAPFunction := False; + FLineStruct.HasStar := False; + FLineStruct.MessageNumber := ''; + FLineStruct.Command := ''; + FLineStruct.UID := ''; + FLineStruct.Complete := True; + FLineStruct.IMAPFunction := ''; + FLineStruct.IMAPValue := ''; + FLineStruct.ByteCount := -1; + ALine := Trim(ALine); //Can get garbage like a spurious CR at start + //Look for (optional) * at start... + LPos := Pos(' ', ALine); {Do not Localize} + if LPos < 1 then begin + Exit; //Nothing on this line + end; + LWord := Copy(ALine, 1, LPos-1); + if LWord = '*' then begin {Do not Localize} + FLineStruct.HasStar := True; + ALine := Copy(ALine, LPos+1, MaxInt); + LPos := Pos(' ', ALine); {Do not Localize} + if LPos < 1 then begin + Exit; //Line ONLY had a * + end; + LWord := Copy(ALine, 1, LPos-1); + end; + //Look for (optional) message number next... + if IsNumeric(LWord) then begin + FLineStruct.MessageNumber := LWord; + ALine := Copy(ALine, LPos+1, MaxInt); + LPos := Pos(' ', ALine); {Do not Localize} + if LPos < 1 then begin + Exit; //Line ONLY had a * 67 + end; + LWord := Copy(ALine, 1, LPos-1); + end; + //We should have a valid IMAP command word now, like FETCH, LIST or SEARCH... + if PosInStrArray(LWord, IMAP4Commands) = -1 then begin + Exit; //Should have been a command, give up. + end; + FLineStruct.Command := LWord; + if ((AExpectedCommand = '') or (FLineStruct.Command = AExpectedCommand)) then begin + Result := True; + end; + ALine := Copy(ALine, Length(LWord)+2, MaxInt); + if ALine[1] <> '(' then begin {Do not Localize} + //This is a line like '* SEARCH 34 56', the '34 56' is the value (result)... + FLineStruct.IMAPValue := ALine; + Exit; + end; + //This is a line like '* 9 FETCH (UID 47 RFC822.SIZE 3456)', i.e. with a bracketted response. + //See is it complete (has a closing bracket) or does it continue on other lines... + ALine := Copy(ALine, 2, MaxInt); + if TextEndsWith(ALine, ')') then begin {Do not Localize} + ALine := Copy(ALine, 1, Length(ALine) - 1); //Strip trailing bracket + FLineStruct.Complete := True; + end else begin + FLineStruct.Complete := False; + end; + //These words left may occur in different order. Find & delete those we know. + LWords := TStringList.Create; + try + ParseIntoBrackettedQuotedAndUnquotedParts(ALine, LWords, False); + if LWords.Count > 0 then begin + //See does it have a trailing byte count... + LWord := LWords[LWords.Count-1]; + if TextStartsWith(LWord, '{') and TextEndsWith(LWord, '}') then begin + //It ends in a byte count... + LWord := Copy(LWord, 2, Length(LWord)-2); + if TextIsSame(LWord, 'NIL') then begin {do not localize} + FLineStruct.ByteCount := 0; + end else begin + FLineStruct.ByteCount := IndyStrToInt(LWord); + end; + LWords.Delete(LWords.Count-1); + end; + end; + if not FLineStruct.Complete then begin + //The command in this case should be the last word... + if LWords.Count > 0 then begin + FLineStruct.IMAPFunction := LWords[LWords.Count-1]; + LWords.Delete(LWords.Count-1); + end; + end; + //See is the UID present... + LPos := LWords.IndexOf(IMAP4FetchDataItem[fdUID]); {Do not Localize} + if LPos <> -1 then begin + //The UID is the word after 'UID'... + if LPos < LWords.Count-1 then begin + FLineStruct.UID := LWords[LPos+1]; + LWords.Delete(LPos+1); + LWords.Delete(LPos); + end; + if PosInStrArray(IMAP4FetchDataItem[fdUID], AExpectedIMAPFunction) > -1 then begin + LWordInExpectedIMAPFunction := True; + end; + end; + //See are the FLAGS present... + LPos := LWords.IndexOf(IMAP4FetchDataItem[fdFlags]); {Do not Localize} + if LPos <> -1 then begin + //The FLAGS are in the "word" (really a string) after 'FLAGS'... + if LPos < LWords.Count-1 then begin + ParseMessageFlagString(LWords[LPos+1], FLineStruct.Flags); + LWords.Delete(LPos+1); + LWords.Delete(LPos); + end; + if PosInStrArray(IMAP4FetchDataItem[fdFlags], AExpectedIMAPFunction) > -1 then begin + LWordInExpectedIMAPFunction := True; + end; + end; + if Length(AExpectedIMAPFunction) > 0 then begin + //See is what we want present. + for LN := 0 to Length(AExpectedIMAPFunction)-1 do begin + //First check if we got it already in IMAPFunction... + if TextIsSame(FLineStruct.IMAPFunction, AExpectedIMAPFunction[LN]) then begin + LWordInExpectedIMAPFunction := True; + Break; + end; + //Now check if it is in any remaining words... + LPos := LWords.IndexOf(AExpectedIMAPFunction[LN]); {Do not Localize} + if LPos <> -1 then begin + FLineStruct.IMAPFunction := LWords[LPos]; + LWordInExpectedIMAPFunction := True; + if LPos < LWords.Count-1 then begin + //There is a parameter after our function... + FLineStruct.IMAPValue := LWords[LPos+1]; + end; + Break; + end; + end; + end else begin + //See is there function/value items left. There may not be, such as + //'* 9 FETCH (UID 45)' in response to a GetUID request. + if FLineStruct.Complete then begin + if LWords.Count > 1 then begin + FLineStruct.IMAPFunction := LWords[LWords.Count-2]; + FLineStruct.IMAPValue := LWords[LWords.Count-1]; + end; + end; + end; + Result := False; + if (AExpectedCommand = '') or (FLineStruct.Command = AExpectedCommand) then begin + //The AExpectedCommand is correct, now need to check the AExpectedIMAPFunction... + if (Length(AExpectedIMAPFunction) = 0) or LWordInExpectedIMAPFunction then begin + Result := True; + end; + end; + finally + FreeAndNil(LWords); + end; +end; + +{This ADDS any parseable info from ALine to FLineStruct (set up from a previous ParseLastCmdResult call)} +procedure TIdIMAP4.ParseLastCmdResultButAppendInfo(ALine: string); +var + LPos: integer; + LWords: TStringList; +begin + ALine := Trim(ALine); //Can get garbage like a spurious CR at start + {We may have an initial or ending bracket, like ") UID 5" or "UID 5)"} + if TextStartsWith(ALine, ')') then begin {Do not Localize} + ALine := Trim(Copy(ALine, 2, MaxInt)); + end; + if TextEndsWith(ALine, ')') then begin {Do not Localize} + ALine := Trim(Copy(ALine, 1, Length(ALine)-1)); + end; + //These words left may occur in different order. Find & delete those we know. + LWords := TStringList.Create; + try + ParseIntoBrackettedQuotedAndUnquotedParts(ALine, LWords, False); + //See is the UID present... + LPos := LWords.IndexOf('UID'); {Do not Localize} + if LPos <> -1 then begin + //The UID is the word after 'UID'... + FLineStruct.UID := LWords[LPos+1]; + LWords.Delete(LPos+1); + LWords.Delete(LPos); + end; + //See are the FLAGS present... + LPos := LWords.IndexOf('FLAGS'); {Do not Localize} + if LPos <> -1 then begin + //The FLAGS are in the "word" (really a string) after 'FLAGS'... + ParseMessageFlagString(LWords[LPos+1], FLineStruct.Flags); + LWords.Delete(LPos+1); + LWords.Delete(LPos); + end; + finally + FreeAndNil(LWords); + end; +end; + +{ ...Parser Functions } + +function TIdIMAP4.ArrayToNumberStr(const AMsgNumList: array of Integer): String; +var + Ln : Integer; +begin + for Ln := 0 to Length(AMsgNumList) - 1 do begin + Result := Result + IntToStr(AMsgNumList[Ln]) + ','; {Do not Localize} + end; + SetLength(Result, (Length(Result) - 1 )); +end; + +function TIdIMAP4.MessageFlagSetToStr(const AFlags: TIdMessageFlagsSet): String; +begin + Result := ''; + if AFlags = [] then begin + Exit; + end; + if mfAnswered in AFlags then begin + Result := Result + MessageFlags[mfAnswered] + ' '; {Do not Localize} + end; + if mfFlagged in AFlags then begin + Result := Result + MessageFlags[mfFlagged] + ' '; {Do not Localize} + end; + if mfDeleted in AFlags then begin + Result := Result + MessageFlags[mfDeleted] + ' '; {Do not Localize} + end; + if mfDraft in AFlags then begin + Result := Result + MessageFlags[mfDraft] + ' '; {Do not Localize} + end; + if mfSeen in AFlags then begin + Result := Result + MessageFlags[mfSeen] + ' '; {Do not Localize} + end; + Result := Trim(Result); +end; + +procedure TIdIMAP4.StripCRLFs(ASourceStream, ADestStream: TStream); +var + LByte: TIdBytes; + LNumSourceBytes: TIdStreamSize; + LBytesRead: Int64; +begin + SetLength(LByte, 1); + ASourceStream.Position := 0; + ADestStream.Size := 0; + LNumSourceBytes := ASourceStream.Size; + LBytesRead := 0; + while LBytesRead < LNumSourceBytes do begin + TIdStreamHelper.ReadBytes(ASourceStream, LByte, 1); + if not ByteIsInEOL(LByte, 0) then begin + TIdStreamHelper.Write(ADestStream, LByte, 1); + end; + Inc(LBytesRead); + end; +end; + +procedure TIdIMAP4.StripCRLFs(var AText: string); +var + LPos: integer; + LLen: integer; + LTemp: string; + LDestPos: integer; +begin + //Optimised with the help of Guus Creuwels. + LPos := 1; + LLen := Length(AText); + SetLength(LTemp, LLen); + LDestPos := 1; + while LPos <= LLen do begin + if AText[LPos] = #13 then begin + //Don't GPF if this is the last char in the string... + if LPos < LLen then begin + if AText[LPos+1] = #10 then begin + Inc(LPos, 2); + end else begin + LTemp[LDestPos] := AText[LPos]; + Inc(LPos); + Inc(LDestPos); + end; + end else begin + LTemp[LDestPos] := AText[LPos]; + Inc(LPos); + Inc(LDestPos); + end; + end else begin + LTemp[LDestPos] := AText[LPos]; + Inc(LPos); + Inc(LDestPos); + end; + end; + SetLength(LTemp, LDestPos - 1); + AText := LTemp; +end; + +procedure TIdIMAP4.ReceiveBody(AMsg: TIdMessage; const ADelim: string = '.'); {Do not Localize} +var + LMsgEnd: Boolean; + LActiveDecoder: TIdMessageDecoder; + LLine: string; + LCheckForOptionalImapFlags: Boolean; + LDelim: string; + + {CC7: The following define SContentType is from IdMessageClient. It is defined here also + (with only local scope) because the one in IdMessageClient is defined locally + there also, so we cannot get at it.} +const + SContentType = 'Content-Type'; {do not localize} + + // TODO - move this procedure into TIdIOHandler as a new Capture method? + procedure CaptureAndDecodeCharset; + var + LMStream: TMemoryStream; + begin + LMStream := TMemoryStream.Create; + try + IOHandler.Capture(LMStream, LDelim, True, IndyTextEncoding_8Bit{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); + LMStream.Position := 0; + // TODO: when String is AnsiString, TIdMessageClient uses AMsg.ChaarSet as + // the destination encoding, should this be doing the same? Otherwise, we + // could just use AMsg.Body.LoadFromStream() instead... + ReadStringsAsCharSet(LMStream, AMsg.Body, AMsg.CharSet{$IFDEF STRING_IS_ANSI}, IndyTextEncoding_8Bit{$ENDIF}); + finally + LMStream.Free; + end; + end; + + function IsContentTypeHtml(const AContentType: String) : Boolean; + begin + Result := IsHeaderMediaTypes(AContentType, ['text/html', 'text/html-sandboxed','application/xhtml+xml']); {do not localize} + end; + + procedure ProcessTextPart(var VDecoder: TIdMessageDecoder); + var + LDestStream: TMemoryStream; + Li: integer; + LTxt: TIdText; + LNewDecoder: TIdMessageDecoder; + {$IFDEF STRING_IS_ANSI} + LAnsiEncoding: IIdTextEncoding; + {$ENDIF} + LContentType, LCharSet: string; + begin + LDestStream := TMemoryStream.Create; + try + LNewDecoder := VDecoder.ReadBody(LDestStream, LMsgEnd); + try + LDestStream.Position := 0; + LTxt := TIdText.Create(AMsg.MessageParts); + try + // if the Content-Type is HTML and does not specify a charset, parse + // the HTML looking for a tag that specifies a charset... + + // TODO: if the media type is not a 'text/...' based XML type, ignore + // the charset from the headers, if present, and parse the XML itself... + + LContentType := VDecoder.Headers.Values[SContentType]; + { + if IsContentTypeAppXml(LContentType) then begin + LCharSet := DetectXmlCharset(LDestStream); + LDestStream.Position := 0; + end else + begin + } + LCharSet := LTxt.GetCharSet(LContentType); + if (LCharSet = '') and IsContentTypeHtml(LContentType) then begin + ParseMetaHTTPEquiv(LDestStream, nil, LCharSet); + LDestStream.Position := 0; + end; + //end; + + LTxt.ContentType := LContentType; + LTxt.CharSet := LCharSet; + LTxt.ContentID := VDecoder.Headers.Values['Content-ID']; {Do not Localize} + LTxt.ContentLocation := VDecoder.Headers.Values['Content-Location']; {Do not Localize} + LTxt.ContentDescription := VDecoder.Headers.Values['Content-Description']; {Do not Localize} + LTxt.ContentDisposition := VDecoder.Headers.Values['Content-Disposition']; {Do not Localize} + LTxt.ContentTransfer := VDecoder.Headers.Values['Content-Transfer-Encoding']; {Do not Localize} + for Li := 0 to VDecoder.Headers.Count-1 do begin + if LTxt.Headers.IndexOfName(VDecoder.Headers.Names[Li]) < 0 then begin + LTxt.ExtraHeaders.AddValue( + VDecoder.Headers.Names[Li], + IndyValueFromIndex(VDecoder.Headers, Li) + ); + end; + end; + {$IFDEF STRING_IS_ANSI} + LAnsiEncoding := CharsetToEncoding(LCharSet); + {$ENDIF} + ReadStringsAsCharset(LDestStream, LTxt.Body, LCharSet{$IFDEF STRING_IS_ANSI}, LAnsiEncoding{$ENDIF}); + except + //this should also remove the Item from the TCollection. + //Note that Delete does not exist in the TCollection. + LTxt.Free; + raise; + end; + except + LNewDecoder.Free; + raise; + end; + VDecoder.Free; + VDecoder := LNewDecoder; + finally + FreeAndNil(LDestStream); + end; + end; + + procedure ProcessAttachment(var VDecoder: TIdMessageDecoder); + var + LDestStream: TStream; + Li: integer; + LAttachment: TIdAttachment; + LNewDecoder: TIdMessageDecoder; + begin + AMsg.DoCreateAttachment(VDecoder.Headers, LAttachment); + Assert(Assigned(LAttachment), 'Attachment must not be unassigned here!'); {Do not Localize} + try + LNewDecoder := nil; + try + LDestStream := LAttachment.PrepareTempStream; + try + LNewDecoder := VDecoder.ReadBody(LDestStream, LMsgEnd); + finally + LAttachment.FinishTempStream; + end; + LAttachment.ContentType := VDecoder.Headers.Values[SContentType]; + LAttachment.ContentTransfer := VDecoder.Headers.Values['Content-Transfer-Encoding']; {Do not Localize} + LAttachment.ContentDisposition := VDecoder.Headers.Values['Content-Disposition']; {Do not Localize} + LAttachment.ContentID := VDecoder.Headers.Values['Content-ID']; {Do not Localize} + LAttachment.ContentLocation := VDecoder.Headers.Values['Content-Location']; {Do not Localize} + LAttachment.ContentDescription := VDecoder.Headers.Values['Content-Description']; {Do not Localize} + LAttachment.Filename := VDecoder.Filename; + for Li := 0 to VDecoder.Headers.Count-1 do begin + if LAttachment.Headers.IndexOfName(VDecoder.Headers.Names[Li]) < 0 then begin + LAttachment.ExtraHeaders.AddValue( + VDecoder.Headers.Names[Li], + IndyValueFromIndex(VDecoder.Headers, Li) + ); + end; + end; + except + LNewDecoder.Free; + raise; + end; + except + //this should also remove the Item from the TCollection. + //Note that Delete does not exist in the TCollection. + LAttachment.Free; + raise; + end; + VDecoder.Free; + VDecoder := LNewDecoder; + end; + +Begin + {CC3: If IMAP calls this ReceiveBody, it prepends IMAP to delim, e.g. 'IMAP)', + to flag that this routine should expect IMAP FLAGS entries.} + LCheckForOptionalImapFlags := False; {CC3: IMAP hack inserted lines start here...} + LDelim := ADelim; + if TextStartsWith(ADelim, 'IMAP') then begin {do not localize} + LCheckForOptionalImapFlags := True; + LDelim := Copy(ADelim, 5, MaxInt); + end; {CC3: ...IMAP hack inserted lines end here} + LMsgEnd := False; + if AMsg.NoDecode then begin + CaptureAndDecodeCharSet; + end else begin + BeginWork(wmRead); + try + LActiveDecoder := nil; + try + repeat + LLine := IOHandler.ReadLn; + {CC3: Check for optional flags before delimiter in the case of IMAP...} + if LLine = LDelim then begin {CC3: IMAP hack ADelim -> LDelim} + Break; + end; {CC3: IMAP hack inserted lines start here...} + if LCheckForOptionalImapFlags and TextStartsWith(LLine, ' FLAGS (\') {do not localize} + and TextEndsWith(LLine, LDelim) then begin + Break; + end; + if LActiveDecoder = nil then begin + LActiveDecoder := TIdMessageDecoderList.CheckForStart(AMsg, LLine); + end; + if LActiveDecoder = nil then begin + {CC9: Per RFC821, the sender is required to add a prefixed '.' to any + line in an email that starts with '.' and the receiver is + required to strip it off. This ensures that the end-of-message + line '.' cannot appear in the message body.} + if TextStartsWith(LLine, '..') then begin {Do not Localize} + Delete(LLine,1,1); + end; + AMsg.Body.Add(LLine); + end else begin + while LActiveDecoder <> nil do begin + LActiveDecoder.SourceStream := TIdTCPStream.Create(Self); + LActiveDecoder.ReadHeader; + case LActiveDecoder.PartType of + mcptText: ProcessTextPart(LActiveDecoder); + mcptAttachment: ProcessAttachment(LActiveDecoder); + mcptIgnore: FreeAndNil(LActiveDecoder); + mcptEOF: begin FreeAndNil(LActiveDecoder); LMsgEnd := True; end; + end; + end; + end; + until LMsgEnd; + finally + FreeAndNil(LActiveDecoder); + end; + finally + EndWork(wmRead); + end; + end; +end; + +{########### Following only used by CONNECT? ###############} +function TIdIMAP4.GetResponse: string; +{CC: The purpose of this is to keep reading & accumulating lines until we hit +a line that has a valid response (that terminates the reading). We call +"FLastCmdResult.FormattedReply := LResponse;" to parse out the response we +received. + +The response sequences we need to deal with are: + +1) Many commands just give a simple result to the command issued: + C41 OK Completed +2) Some commands give you data first, then the result: + * LIST (\UnMarked) "/" INBOX + * LIST (\UnMarked) "/" Junk + * LIST (\UnMarked) "/" Junk/Subbox1 + C42 OK Completed +3) Some responses have a result but * instead of a command number (like C42): + * OK CommuniGate Pro IMAP Server 3.5.7 ready +4) Some have neither a * nor command number, but start with a result: + + Send the additional command text +or: + BAD Bad parameter + +Because you may get data first, which you need to skip, you need to +accept all the above possibilities. + +We MUST stop when we find a valid response code, like OK. +} +var + LLine: String; + LResponse: TStringList; + LWord: string; + LPos: integer; + LBuf: string; +begin + Result := ''; {Do not Localize} + LResponse := TStringList.Create; + try + repeat + LLine := IOHandler.ReadLnWait; + if LLine <> '' then begin {Do not Localize} + {It is not an empty line, add it to our list of stuff received (it is + not our job to interpret it)} + LResponse.Add(LLine); + {See if the last LLine contained a response code like OK or BAD.} + LPos := Pos(' ', LLine); {Do not Localize} + if LPos <> 0 then begin + {There are at least two words on this line...} + LWord := Trim(Copy(LLine, 1, LPos-1)); + LBuf := Trim(Copy(LLine, LPos+1, MaxInt)); {The rest of the line, without the 1st word} + end else begin + {No space, so this line is a single word. A bit weird, but it + could be just an OK...} + LWord := LLine; {A bit pedantic, but emphasises we have a word, not a line} + LBuf := ''; {Do not Localize} + end; + LPos := PosInStrArray(LWord, VALID_TAGGEDREPLIES); {Do not Localize} + if LPos > -1 then begin + {We got a valid response code as the first word...} + Result := LWord; + FLastCmdResult.FormattedReply := LResponse; + Exit; + end; + if Length(LBuf) = 0 then begin {Do not Localize} + Continue; {We hit a line with just one word which is not a valid IMAP response} + end; + {In all other cases, any valid response should be the second word...} + LPos := Pos(' ', LBuf); {Do not Localize} + if LPos <> 0 then begin + {There are at least three words on this line...} + LWord := Trim(Copy(LBuf, 1, LPos-1)); + LBuf := Trim(Copy(LBuf, LPos+1, MaxInt)); {The rest of the line, without the 1st word} + end else begin + {No space, so this line is two single words.} + LWord := LLine; {A bit pedantic, but emphasises we have a word, not a line} + LBuf := ''; {Do not Localize} + end; + LPos := PosInStrArray(LWord, VALID_TAGGEDREPLIES); {Do not Localize} + if LPos > -1 then begin + {We got a valid response code as the second word...} + Result := LWord; + FLastCmdResult.FormattedReply := LResponse; + Exit; + end; + end; + until False; + finally + FreeAndNil(LResponse); + end; +end; + +end. + diff --git a/indy/Protocols/IdIMAP4Server.pas b/indy/Protocols/IdIMAP4Server.pas new file mode 100644 index 0000000..04d4471 --- /dev/null +++ b/indy/Protocols/IdIMAP4Server.pas @@ -0,0 +1,2700 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Prior revision history + + Rev 1.31 2/9/2005 11:44:20 AM JPMugaas + Fixed compiler problem and removed some warnings about virtual + methods hiding stuff in the base class. + + Rev 1.30 2/8/05 6:20:16 PM RLebeau + Added additional overriden methods. + + Rev 1.29 10/26/2004 11:08:06 PM JPMugaas + Updated refs. + + Rev 1.28 10/21/2004 1:49:12 PM BGooijen + Raid 214213 + + Rev 1.27 09/06/2004 09:54:56 CCostelloe + Kylix 3 patch + + Rev 1.26 2004.05.20 11:37:34 AM czhower + IdStreamVCL + + Rev 1.25 4/8/2004 11:49:56 AM BGooijen + Fix for D5 + + Rev 1.24 03/03/2004 01:16:20 CCostelloe + Yet another check-in as part of continuing development + + Rev 1.23 01/03/2004 23:32:24 CCostelloe + Another check-in as part of continuing development + + Rev 1.22 3/1/2004 12:55:28 PM JPMugaas + Updated for problem with new code. + + Rev 1.21 26/02/2004 02:01:14 CCostelloe + Another intermediate check-in, approx half of functions are debugged + + Rev 1.20 24/02/2004 10:34:50 CCostelloe + Storage-specific code moved to IdIMAP4ServerDemo + + Rev 1.19 2/22/2004 12:09:54 AM JPMugaas + Fixes for IMAP4Server compile failure in DotNET. This also fixes + a potential problem where file handles can be leaked in the server + needlessly. + + Rev 1.18 12/02/2004 02:40:56 CCostelloe + Minor bugfix + + Rev 1.17 12/02/2004 02:24:30 CCostelloe + Completed revision, apart from parts support and BODYSTRUCTURE, not + yet debugged. + + Rev 1.16 05/02/2004 00:25:32 CCostelloe + This version actually works! + + Rev 1.15 2/4/2004 2:37:38 AM JPMugaas + Moved more units down to the implementation clause in the units to + make them easier to compile. + + Rev 1.14 2/3/2004 4:12:42 PM JPMugaas + Fixed up units so they should compile. + + Rev 1.13 1/29/2004 9:07:54 PM JPMugaas + Now uses TIdExplicitTLSServer so it can take advantage of that framework. + + Rev 1.12 1/21/2004 3:11:02 PM JPMugaas + InitComponent + + Rev 1.11 27/12/2003 22:28:48 ANeillans + Design fix, Login event only passed the username (first param) + + Rev 1.10 2003.10.21 9:13:08 PM czhower + Now compiles. + + Rev 1.9 10/19/2003 6:00:24 PM DSiders + Added localization coimments. + + Rev 1.8 9/19/2003 03:29:58 PM JPMugaas + Now should compile again. + + Rev 1.7 07/09/2003 12:29:08 CCostelloe + Warning that variable LIO is declared but never used in + TIdIMAP4Server.DoCommandSTARTTLS fixed. + + Rev 1.6 7/20/2003 6:20:06 PM SPerry + Switched to IdCmdTCPServer, also some modifications + + Rev 1.5 3/14/2003 10:44:36 PM BGooijen + Removed warnings, changed StartSSL to PassThrough:=false; + + Rev 1.4 3/14/2003 10:04:10 PM BGooijen + Removed TIdServerIOHandlerSSLBase.PeerPassthrough, the ssl is now + enabled in the server-protocol-files + + Rev 1.3 3/13/2003 09:49:20 AM JPMugaas + Now uses an abstract SSL base class instead of OpenSSL so + 3rd-party vendors can plug-in their products. + + Rev 1.2 2/24/2003 09:03:14 PM JPMugaas + + Rev 1.1 2/6/2003 03:18:14 AM JPMugaas + Updated components that compile with Indy 10. + + Rev 1.0 11/13/2002 07:55:02 AM JPMugaas + + 2002-Apr-21 - J. Berg + use fetch() + + 2000-May-18 - J. Peter Mugaas + Ported to Indy + + 2000-Jan-13 - MTL + Moved to new Palette Scheme (Winshoes Servers) + + 1999-Aug-26 - Ray Malone + Started unit +} + +unit IdIMAP4Server; + +{ +TODO (ex RFC 3501): + +Dont allow & to be used as a mailbox separator. + + Certain server data (unsolicited responses) MUST be recorded, + see Server Responses section. + + UIDs must be unique to a mailbox AND any subsequent mailbox with + the same name - record in a text file. + +\Recent cannot be changed by STORE or APPEND. + +COPY should preserve the date of the original message. + + + TODO (ccostelloe): + +Add a file recording the UIDVALIDITY in each mailbox. + +Emails should be ordered in date order. + +Optional date/time param to be implemented in APPEND. + + Consider integrating IdUserAccounts into login mechanism + (or per-user passwords). + +Implement utf mailbox encoding. + +Implement * in message numbers. + + Implement multiple-option FETCH commands (will need breaking out some + options which are abbreviations into their subsets). + +Need some method of preserving flags permanently. +} + +{ + IMPLEMENTATION NOTES: + +Major rewrite started 2nd February 2004, Ciaran Costelloe, ccostelloe@flogas.ie. +Prior to this, it was a simple wrapper class with a few problems. + + Note that IMAP servers should return BAD for an unknown command or + invalid arguments (synthax errors and unsupported commands) and BAD + if the command is valid but there was some problem in executing + (e.g. trying a change an email's flag if it is a read-only mailbox). + + FUseDefaultMechanismsForUnassignedCommands defaults to True: if you + set it to False, you need to implement command handlers for all the + commands you need to implement. If True, this class implements a + default mechanism and provides default behaviour for all commands. + It does not include any filesystem-specific functions, which you + need to implement. + + The default behaviour uses a default password of 'admin' - change this + if you have any consideration for security! + + FSaferMode defaults to False: you should probably leave it False for + testing, because this generates diagnostically-useful error messages. + However, setting it True generates minimal responses for the greeting + and for login failures, making life more difficult for a hacker. + + WARNING: you should also implement one of the Indy-provided more-secure + logins than the default plaintext password login! + + You may want to assign handlers to the OnBeforeCmd and OnBeforeSend + events to easily log data in & out of the server. + + WARNING: TIdIMAP4PeerContext has a TIdMailBox which holds various + status info, including UIDs in its message collection. Do NOT use the + message collection for loading messages into, or you may thrash message + UIDs or flags! +} + +interface + +{$i IdCompilerDefines.inc} + +{$IFDEF DOTNET} +{$I IdUnitPlatformOff.inc} +{$I IdSymbolPlatformOff.inc} +{$ENDIF} + +uses + Classes, + IdAssignedNumbers, + IdCustomTCPServer, //for TIdServerContext + IdCmdTCPServer, + IdContext, + IdCommandHandlers, + IdException, + IdExplicitTLSClientServerBase, + IdIMAP4, //For some defines like TIdIMAP4ConnectionState + IdMailBox, + IdMessage, + IdReply, + IdReplyIMAP4, + IdTCPConnection, + IdYarn; + +const + DEF_IMAP4_IMPLICIT_TLS = False; + +type + TIMAP4CommandEvent = procedure(AContext: TIdContext; const ATag, ACmd: String) of object; + TIdIMAP4CommandBeforeEvent = procedure(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext) of object; + TIdIMAP4CommandBeforeSendEvent = procedure(AContext: TIdContext; AData: string) of object; + + //For default mechanisms.. + TIdIMAP4DefMech1 = function(ALoginName, AMailbox: string): Boolean of object; + TIdIMAP4DefMech2 = function(ALoginName, AMailBoxName: string; AMailBox: TIdMailBox): Boolean of object; + TIdIMAP4DefMech3 = function(ALoginName, AMailbox: string): string of object; + TIdIMAP4DefMech4 = function(ALoginName, AOldMailboxName, ANewMailboxName: string): Boolean of object; + TIdIMAP4DefMech5 = function(ALoginName, AMailBoxName: string; AMailBoxNames: TStrings; AMailBoxFlags: TStrings): Boolean of object; + TIdIMAP4DefMech6 = function(ALoginName, AMailbox: string; AMessage: TIdMessage): Boolean of object; + TIdIMAP4DefMech7 = function(ALoginName, ASourceMailBox, AMessageUID, ADestinationMailbox: string): Boolean of object; + TIdIMAP4DefMech8 = function(ALoginName, AMailbox: string; AMessage: TIdMessage): integer of object; + TIdIMAP4DefMech9 = function(ALoginName, AMailbox: string; AMessage, ATargetMessage: TIdMessage): Boolean of object; + TIdIMAP4DefMech10 = function(ALoginName, AMailbox: string; AMessage: TIdMessage; ALines: TStrings): Boolean of object; + TIdIMAP4DefMech11 = function(ASender: TIdCommand; AReadOnly: Boolean): Boolean of object; + TIdIMAP4DefMech12 = function(AParams: TStrings; AMailBoxParam: Integer): Boolean of object; + TIdIMAP4DefMech13 = function(ALoginName, AMailBoxName, ANewUIDNext: string): Boolean of object; + TIdIMAP4DefMech14 = function(ALoginName, AMailBoxName, AUID: string): string of object; + + EIdIMAP4ServerException = class(EIdException); + EIdIMAP4ImplicitTLSRequiresSSL = class(EIdIMAP4ServerException); + + { custom IMAP4 context } + TIdIMAP4PeerContext = class(TIdServerContext) + protected + FConnectionState : TIdIMAP4ConnectionState; + FLoginName: string; + FMailBox: TIdMailBox; + FIMAP4Tag: String; + FLastCommand: TIdReplyIMAP4; //Used to record the client command we are currently processing + function GetUsingTLS: Boolean; + public + constructor Create( + AConnection: TIdTCPConnection; + AYarn: TIdYarn; + AList: TIdContextThreadList = nil + ); override; + destructor Destroy; override; + property ConnectionState: TIdIMAP4ConnectionState read FConnectionState; + property UsingTLS : Boolean read GetUsingTLS; + property IMAP4Tag: String read FIMAP4Tag; + property MailBox: TIdMailBox read FMailBox; + property LoginName: string read FLoginName write FLoginName; + end; + + { TIdIMAP4Server } + TIdIMAP4Server = class(TIdExplicitTLSServer) + protected + // + FSaferMode: Boolean; //See IMPLEMENTATION NOTES above + FUseDefaultMechanismsForUnassignedCommands: Boolean; //See IMPLEMENTATION NOTES above + FRootPath: string; //See IMPLEMENTATION NOTES above + FDefaultPassword: string; //See IMPLEMENTATION NOTES above + FMailBoxSeparator: Char; + // + fOnDefMechDoesImapMailBoxExist: TIdIMAP4DefMech1; + fOnDefMechCreateMailBox: TIdIMAP4DefMech1; + fOnDefMechDeleteMailBox: TIdIMAP4DefMech1; + fOnDefMechIsMailBoxOpen: TIdIMAP4DefMech1; + fOnDefMechSetupMailbox: TIdIMAP4DefMech2; + fOnDefMechNameAndMailBoxToPath: TIdIMAP4DefMech3; + fOnDefMechGetNextFreeUID: TIdIMAP4DefMech3; + fOnDefMechRenameMailBox: TIdIMAP4DefMech4; + fOnDefMechListMailBox: TIdIMAP4DefMech5; + fOnDefMechDeleteMessage: TIdIMAP4DefMech6; + fOnDefMechCopyMessage: TIdIMAP4DefMech7; + fOnDefMechGetMessageSize: TIdIMAP4DefMech8; + fOnDefMechGetMessageHeader: TIdIMAP4DefMech9; + fOnDefMechGetMessageRaw: TIdIMAP4DefMech10; + fOnDefMechOpenMailBox: TIdIMAP4DefMech11; + fOnDefMechReinterpretParamAsMailBox: TIdIMAP4DefMech12; + fOnDefMechUpdateNextFreeUID: TIdIMAP4DefMech13; + fOnDefMechGetFileNameToWriteAppendMessage: TIdIMAP4DefMech14; + // + fOnBeforeCmd: TIdIMAP4CommandBeforeEvent; + fOnBeforeSend: TIdIMAP4CommandBeforeSendEvent; + fOnCommandCAPABILITY: TIMAP4CommandEvent; + fONCommandNOOP: TIMAP4CommandEvent; + fONCommandLOGOUT: TIMAP4CommandEvent; + fONCommandAUTHENTICATE: TIMAP4CommandEvent; + fONCommandLOGIN: TIMAP4CommandEvent; + fONCommandSELECT: TIMAP4CommandEvent; + fONCommandEXAMINE: TIMAP4CommandEvent; + fONCommandCREATE: TIMAP4CommandEvent; + fONCommandDELETE: TIMAP4CommandEvent; + fONCommandRENAME: TIMAP4CommandEvent; + fONCommandSUBSCRIBE: TIMAP4CommandEvent; + fONCommandUNSUBSCRIBE: TIMAP4CommandEvent; + fONCommandLIST: TIMAP4CommandEvent; + fONCommandLSUB: TIMAP4CommandEvent; + fONCommandSTATUS: TIMAP4CommandEvent; + fONCommandAPPEND: TIMAP4CommandEvent; + fONCommandCHECK: TIMAP4CommandEvent; + fONCommandCLOSE: TIMAP4CommandEvent; + fONCommandEXPUNGE: TIMAP4CommandEvent; + fONCommandSEARCH: TIMAP4CommandEvent; + fONCommandFETCH: TIMAP4CommandEvent; + fONCommandSTORE: TIMAP4CommandEvent; + fONCommandCOPY: TIMAP4CommandEvent; + fONCommandUID: TIMAP4CommandEvent; + fONCommandX: TIMAP4CommandEvent; + fOnCommandError: TIMAP4CommandEvent; + // + function CreateExceptionReply: TIdReply; override; + function CreateGreeting: TIdReply; override; + function CreateHelpReply: TIdReply; override; + function CreateMaxConnectionReply: TIdReply; override; + function CreateReplyUnknownCommand: TIdReply; override; + // + //The following are internal commands that help support the IMAP protocol... + procedure InitializeCommandHandlers; override; + function GetReplyClass:TIdReplyClass; override; + function GetRepliesClass:TIdRepliesClass; override; + procedure SendGreeting(AContext: TIdContext; AGreeting: TIdReply); override; + procedure SendWrongConnectionState(ASender: TIdCommand); + procedure SendUnsupportedCommand(ASender: TIdCommand); + procedure SendIncorrectNumberOfParameters(ASender: TIdCommand); + procedure SendUnassignedDefaultMechanism(ASender: TIdCommand); + procedure DoReplyUnknownCommand(AContext: TIdContext; AText: string); override; + procedure SendErrorOpenedReadOnly(ASender: TIdCommand); + procedure SendOkReply(ASender: TIdCommand; const AText: string); + procedure SendBadReply(ASender: TIdCommand; const AText: string); overload; + procedure SendBadReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); overload; + procedure SendNoReply(ASender: TIdCommand; const AText: string = ''); overload; + procedure SendNoReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); overload; + // + //The following are used internally by the default mechanism... + function ExpungeRecords(ASender: TIdCommand): Boolean; + function MessageSetToMessageNumbers(AUseUID: Boolean; ASender: TIdCommand; AMessageNumbers: TStrings; AMessageSet: string): Boolean; + function GetRecordForUID(const AUID: String; AMailBox: TIdMailBox): Integer; + procedure ProcessFetch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); + procedure ProcessCopy(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); + function ProcessStore(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings): Boolean; + procedure ProcessSearch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); + function FlagStringToFlagList(AFlagList: TStrings; AFlagString: string): Boolean; + function StripQuotesIfNecessary(AName: string): string; + function ReassembleParams(ASeparator: char; AParams: TStrings; AParamToReassemble: integer): Boolean; + function ReinterpretParamAsMailBox(AParams: TStrings; AMailBoxParam: integer): Boolean; + function ReinterpretParamAsFlags(AParams: TStrings; AFlagsParam: integer): Boolean; + function ReinterpretParamAsQuotedStr(AParams: TStrings; AFlagsParam: integer): Boolean; + function ReinterpretParamAsDataItems(AParams: TStrings; AFlagsParam: integer): Boolean; + // + //The following are used internally by our default mechanism and are copies of + //the same function in TIdIMAP4 (move to a base class?)... + function MessageFlagSetToStr(const AFlags: TIdMessageFlagsSet): String; + // + //DoBeforeCmd & DoSendReply are useful for a server to log all commands and + //responses for debugging... + procedure DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext); + procedure DoSendReply(AContext: TIdContext; const AData: string); overload; + procedure DoSendReply(AContext: TIdContext; const AFormat: string; const Args: array of const); overload; + // + //Command handlers... + procedure DoCmdHandlersException(ACommand: String; AContext: TIdContext); + procedure DoCommandCAPABILITY(ASender: TIdCommand); + procedure DoCommandNOOP(ASender: TIdCommand); + procedure DoCommandLOGOUT(ASender: TIdCommand); + procedure DoCommandAUTHENTICATE(ASender: TIdCommand); + procedure DoCommandLOGIN(ASender: TIdCommand); + procedure DoCommandSELECT(ASender: TIdCommand); + procedure DoCommandEXAMINE(ASender: TIdCommand); + procedure DoCommandCREATE(ASender: TIdCommand); + procedure DoCommandDELETE(ASender: TIdCommand); + procedure DoCommandRENAME(ASender: TIdCommand); + procedure DoCommandSUBSCRIBE(ASender: TIdCommand); + procedure DoCommandUNSUBSCRIBE(ASender: TIdCommand); + procedure DoCommandLIST(ASender: TIdCommand); + procedure DoCommandLSUB(ASender: TIdCommand); + procedure DoCommandSTATUS(ASender: TIdCommand); + procedure DoCommandAPPEND(ASender: TIdCommand); + procedure DoCommandCHECK(ASender: TIdCommand); + procedure DoCommandCLOSE(ASender: TIdCommand); + procedure DoCommandEXPUNGE(ASender: TIdCommand); + procedure DoCommandSEARCH(ASender: TIdCommand); + procedure DoCommandFETCH(ASender: TIdCommand); + procedure DoCommandSTORE(ASender: TIdCommand); + procedure DoCommandCOPY(ASender: TIdCommand); + procedure DoCommandUID(ASender: TIdCommand); + procedure DoCommandX(ASender: TIdCommand); + procedure DoCommandSTARTTLS(ASender: TIdCommand); + // common code for command handlers + procedure MustUseTLS(ASender: TIdCommand); + // + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + destructor Destroy; override; + published + property DefaultPort default IdPORT_IMAP4; + property SaferMode: Boolean read FSaferMode write FSaferMode default False; + property UseDefaultMechanismsForUnassignedCommands: Boolean read FUseDefaultMechanismsForUnassignedCommands write FUseDefaultMechanismsForUnassignedCommands default True; + property RootPath: string read FRootPath write FRootPath; + property DefaultPassword: string read FDefaultPassword write FDefaultPassword; + property MailBoxSeparator: Char read FMailBoxSeparator; + {Default mechansisms} + property OnDefMechDoesImapMailBoxExist: TIdIMAP4DefMech1 read fOnDefMechDoesImapMailBoxExist write fOnDefMechDoesImapMailBoxExist; + property OnDefMechCreateMailBox: TIdIMAP4DefMech1 read fOnDefMechCreateMailBox write fOnDefMechCreateMailBox; + property OnDefMechDeleteMailBox: TIdIMAP4DefMech1 read fOnDefMechDeleteMailBox write fOnDefMechDeleteMailBox; + property OnDefMechIsMailBoxOpen: TIdIMAP4DefMech1 read fOnDefMechIsMailBoxOpen write fOnDefMechIsMailBoxOpen; + property OnDefMechSetupMailbox: TIdIMAP4DefMech2 read fOnDefMechSetupMailbox write fOnDefMechSetupMailbox; + property OnDefMechNameAndMailBoxToPath: TIdIMAP4DefMech3 read fOnDefMechNameAndMailBoxToPath write fOnDefMechNameAndMailBoxToPath; + property OnDefMechGetNextFreeUID: TIdIMAP4DefMech3 read fOnDefMechGetNextFreeUID write fOnDefMechGetNextFreeUID; + property OnDefMechRenameMailBox: TIdIMAP4DefMech4 read fOnDefMechRenameMailBox write fOnDefMechRenameMailBox; + property OnDefMechListMailBox: TIdIMAP4DefMech5 read fOnDefMechListMailBox write fOnDefMechListMailBox; + property OnDefMechDeleteMessage: TIdIMAP4DefMech6 read fOnDefMechDeleteMessage write fOnDefMechDeleteMessage; + property OnDefMechCopyMessage: TIdIMAP4DefMech7 read fOnDefMechCopyMessage write fOnDefMechCopyMessage; + property OnDefMechGetMessageSize: TIdIMAP4DefMech8 read fOnDefMechGetMessageSize write fOnDefMechGetMessageSize; + property OnDefMechGetMessageHeader: TIdIMAP4DefMech9 read fOnDefMechGetMessageHeader write fOnDefMechGetMessageHeader; + property OnDefMechGetMessageRaw: TIdIMAP4DefMech10 read fOnDefMechGetMessageRaw write fOnDefMechGetMessageRaw; + property OnDefMechOpenMailBox: TIdIMAP4DefMech11 read fOnDefMechOpenMailBox write fOnDefMechOpenMailBox; + property OnDefMechReinterpretParamAsMailBox: TIdIMAP4DefMech12 read fOnDefMechReinterpretParamAsMailBox write fOnDefMechReinterpretParamAsMailBox; + property OnDefMechUpdateNextFreeUID: TIdIMAP4DefMech13 read fOnDefMechUpdateNextFreeUID write fOnDefMechUpdateNextFreeUID; + property OnDefMechGetFileNameToWriteAppendMessage: TIdIMAP4DefMech14 read fOnDefMechGetFileNameToWriteAppendMessage write fOnDefMechGetFileNameToWriteAppendMessage; + { Events } + property OnBeforeCmd: TIdIMAP4CommandBeforeEvent read fOnBeforeCmd write fOnBeforeCmd; + property OnBeforeSend: TIdIMAP4CommandBeforeSendEvent read fOnBeforeSend write fOnBeforeSend; + property OnCommandCAPABILITY: TIMAP4CommandEvent read fOnCommandCAPABILITY write fOnCommandCAPABILITY; + property OnCommandNOOP: TIMAP4CommandEvent read fONCommandNOOP write fONCommandNOOP; + property OnCommandLOGOUT: TIMAP4CommandEvent read fONCommandLOGOUT write fONCommandLOGOUT; + property OnCommandAUTHENTICATE: TIMAP4CommandEvent read fONCommandAUTHENTICATE write fONCommandAUTHENTICATE; + property OnCommandLOGIN: TIMAP4CommandEvent read fONCommandLOGIN write fONCommandLOGIN; + property OnCommandSELECT: TIMAP4CommandEvent read fONCommandSELECT write fONCommandSELECT; + property OnCommandEXAMINE:TIMAP4CommandEvent read fOnCommandEXAMINE write fOnCommandEXAMINE; + property OnCommandCREATE: TIMAP4CommandEvent read fONCommandCREATE write fONCommandCREATE; + property OnCommandDELETE: TIMAP4CommandEvent read fONCommandDELETE write fONCommandDELETE; + property OnCommandRENAME: TIMAP4CommandEvent read fOnCommandRENAME write fOnCommandRENAME; + property OnCommandSUBSCRIBE: TIMAP4CommandEvent read fONCommandSUBSCRIBE write fONCommandSUBSCRIBE; + property OnCommandUNSUBSCRIBE: TIMAP4CommandEvent read fONCommandUNSUBSCRIBE write fONCommandUNSUBSCRIBE; + property OnCommandLIST: TIMAP4CommandEvent read fONCommandLIST write fONCommandLIST; + property OnCommandLSUB: TIMAP4CommandEvent read fOnCommandLSUB write fOnCommandLSUB; + property OnCommandSTATUS: TIMAP4CommandEvent read fONCommandSTATUS write fONCommandSTATUS; + property OnCommandAPPEND: TIMAP4CommandEvent read fOnCommandAPPEND write fOnCommandAPPEND; + property OnCommandCHECK: TIMAP4CommandEvent read fONCommandCHECK write fONCommandCHECK; + property OnCommandCLOSE: TIMAP4CommandEvent read fOnCommandCLOSE write fOnCommandCLOSE; + property OnCommandEXPUNGE: TIMAP4CommandEvent read fONCommandEXPUNGE write fONCommandEXPUNGE; + property OnCommandSEARCH: TIMAP4CommandEvent read fOnCommandSEARCH write fOnCommandSEARCH; + property OnCommandFETCH: TIMAP4CommandEvent read fONCommandFETCH write fONCommandFETCH; + property OnCommandSTORE: TIMAP4CommandEvent read fOnCommandSTORE write fOnCommandSTORE; + property OnCommandCOPY: TIMAP4CommandEvent read fOnCommandCOPY write fOnCommandCOPY; + property OnCommandUID: TIMAP4CommandEvent read fONCommandUID write fONCommandUID; + property OnCommandX: TIMAP4CommandEvent read fOnCommandX write fOnCommandX; + property OnCommandError: TIMAP4CommandEvent read fOnCommandError write fOnCommandError; + end; + +implementation + +uses + IdGlobal, + IdGlobalProtocols, + IdMessageCollection, + IdResourceStrings, + IdResourceStringsProtocols, + IdSSL, + IdStream, + SysUtils; + +function TIdIMAP4Server.GetReplyClass: TIdReplyClass; +begin + Result := TIdReplyIMAP4; +end; + +function TIdIMAP4Server.GetRepliesClass: TIdRepliesClass; +begin + Result := TIdRepliesIMAP4; +end; + +procedure TIdIMAP4Server.SendGreeting(AContext: TIdContext; AGreeting: TIdReply); +begin + if FSaferMode then begin + DoSendReply(AContext, '* OK'); {Do not Localize} + end else begin + DoSendReply(AContext, '* OK Indy IMAP server version ' + GetIndyVersion); {Do not Localize} + end; +end; + +procedure TIdIMAP4Server.SendWrongConnectionState(ASender: TIdCommand); +begin + SendNoReply(ASender, 'Wrong connection state'); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendErrorOpenedReadOnly(ASender: TIdCommand); +begin + SendNoReply(ASender, 'Mailbox was opened read-only'); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendUnsupportedCommand(ASender: TIdCommand); +begin + SendBadReply(ASender, 'Unsupported command'); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendIncorrectNumberOfParameters(ASender: TIdCommand); +begin + SendBadReply(ASender, 'Incorrect number of parameters'); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendUnassignedDefaultMechanism(ASender: TIdCommand); +begin + SendBadReply(ASender, 'Server internal error: unassigned procedure'); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendOkReply(ASender: TIdCommand; const AText: string); +begin + DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' OK ' + AText); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendBadReply(ASender: TIdCommand; const AText: string); +begin + DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' BAD ' + AText); {Do not Localize} +end; + +procedure TIdIMAP4Server.SendBadReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); +begin + SendBadReply(ASender, IndyFormat(AFormat, Args)); +end; + +procedure TIdIMAP4Server.SendNoReply(ASender: TIdCommand; const AText: string = ''); +begin + if AText <> '' then begin + DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' NO ' + AText); {Do not Localize} + end else begin + DoSendReply(ASender.Context, TIdIMAP4PeerContext(ASender.Context).FLastCommand.SequenceNumber + ' NO'); {Do not Localize} + end; +end; + +procedure TIdIMAP4Server.SendNoReply(ASender: TIdCommand; const AFormat: string; const Args: array of const); +begin + SendNoReply(ASender, IndyFormat(AFormat, Args)); +end; + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdIMAP4Server.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdIMAP4Server.InitComponent; +begin + inherited InitComponent; + //Todo: Not sure which number is appropriate. Should be tested + FImplicitTLSProtPort := IdPORT_IMAP4S; //Id_PORT_imap4_ssl_dp; + FRegularProtPort := IdPORT_IMAP4; + DefaultPort := IdPORT_IMAP4; + ContextClass := TIdIMAP4PeerContext; + FSaferMode := False; + FUseDefaultMechanismsForUnassignedCommands := True; +{$IFDEF UNIX} + FRootPath := GPathDelim + 'var' + GPathDelim + 'imapmail'; {Do not Localize} +{$ELSE} + FRootPath := GPathDelim + 'imapmail'; {Do not Localize} +{$ENDIF} + FDefaultPassword := 'admin'; {Do not Localize} + FMailBoxSeparator := '.'; {Do not Localize} +end; + +destructor TIdIMAP4Server.Destroy; +begin + inherited Destroy; +end; + +function TIdIMAP4Server.CreateExceptionReply: TIdReply; +begin + Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(IMAP_BAD, 'Unknown Internal Error'); {do not localize} +end; + +function TIdIMAP4Server.CreateGreeting: TIdReply; +begin + Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(IMAP_OK, 'Welcome'); {do not localize} +end; + +function TIdIMAP4Server.CreateHelpReply: TIdReply; +begin + Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(IMAP_OK, 'Help follows'); {do not localize} +end; + +function TIdIMAP4Server.CreateMaxConnectionReply: TIdReply; +begin + Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(IMAP_BAD, 'Too many connections. Try again later.'); {do not localize} +end; + +function TIdIMAP4Server.CreateReplyUnknownCommand: TIdReply; +begin + Result := TIdReplyIMAP4.CreateWithReplyTexts(nil, ReplyTexts); + Result.SetReply(IMAP_BAD, 'Unknown command'); {do not localize} +end; + +constructor TIdIMAP4PeerContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); +begin + inherited Create(AConnection, AYarn, AList); + FMailBox := TIdMailBox.Create; + FLastCommand := TIdReplyIMAP4.Create(nil); + FConnectionState := csAny; +end; + +destructor TIdIMAP4PeerContext.Destroy; +begin + FreeAndNil(FLastCommand); + FreeAndNil(FMailBox); + inherited Destroy; +end; + +function TIdIMAP4PeerContext.GetUsingTLS: Boolean; +begin + if Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin + Result := not TIdSSLIOHandlerSocketBase(Connection.IOHandler).PassThrough; + end else begin + Result := False; + end; +end; + +procedure TIdIMAP4Server.DoReplyUnknownCommand(AContext: TIdContext; AText: string); +//AText is ignored by TIdIMAP4Server +var + LText: string; +begin + LText := TIdIMAP4PeerContext(AContext).FLastCommand.SequenceNumber; + if LText = '' then begin + //This should not happen! + LText := '*'; {Do not Localize} + end; + DoSendReply(AContext, LText + ' NO Unknown command'); {Do not Localize} +end; + +function TIdIMAP4Server.ExpungeRecords(ASender: TIdCommand): Boolean; +var + LN: integer; + LMessage: TIdMessage; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //Delete all records that have the deleted flag set... + LN := 0; + Result := True; + while LN < LContext.MailBox.MessageList.Count do begin + LMessage := LContext.MailBox.MessageList.Messages[LN]; + if mfDeleted in LMessage.Flags then begin + if not OnDefMechDeleteMessage(LContext.LoginName, LContext.MailBox.Name, LMessage) then + begin + Result := False; + end; + LContext.MailBox.MessageList.Delete(LN); + LContext.MailBox.TotalMsgs := LContext.MailBox.TotalMsgs - 1; + end else begin + Inc(LN); + end; + end; +end; + +function TIdIMAP4Server.MessageSetToMessageNumbers(AUseUID: Boolean; ASender: TIdCommand; + AMessageNumbers: TStrings; AMessageSet: string): Boolean; +{AMessageNumbers may be '7' or maybe '2:4' (2, 3 & 4) or maybe '2,4,6' (2, 4 & 6) +or maybe '1:*'} +var + LPos: integer; + LStart: integer; + LN: integer; + LEnd: integer; + LTemp: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + AMessageNumbers.Clear; + //See is it a sequence like 2:4 ... + LPos := IndyPos(':', AMessageSet); {Do not Localize} + if LPos > 0 then begin + LTemp := Copy(AMessageSet, 1, LPos-1); + LStart := IndyStrToInt(LTemp); + LTemp := Copy(AMessageSet, LPos+1, MAXINT); + if LTemp = '*' then begin {Do not Localize} + if AUseUID then begin + LEnd := IndyStrToInt(LContext.MailBox.UIDNext)-1; + for LN := LStart to LEnd do begin + AMessageNumbers.Add(IntToStr(LN)); + end; + end else begin + LEnd := LContext.MailBox.MessageList.Count; + for LN := LStart to LEnd do begin + AMessageNumbers.Add(IntToStr(LN)); + end; + end; + end else begin + LEnd := IndyStrToInt(LTemp); + for LN := LStart to LEnd do begin + AMessageNumbers.Add(IntToStr(LN)); + end; + end; + end else begin + //See is it a comma-separated list... + LPos := IndyPos(',', AMessageSet); {Do not Localize} + if LPos = 0 then begin + AMessageNumbers.Add(AMessageSet); + end else begin + BreakApart(AMessageSet, ',', AMessageNumbers); {Do not Localize} + end; + end; + Result := True; +end; + +//Return -1 if not found +function TIdIMAP4Server.GetRecordForUID(const AUID: String; AMailBox: TIdMailBox): Integer; +var + LN, LUID: Integer; +begin + // TODO: do string comparisons instead so that conversions are not needed? + LUID := IndyStrToInt(AUID); + for LN := 0 to AMailBox.MessageList.Count-1 do begin + if IndyStrToInt(AMailBox.MessageList.Messages[LN].UID) = LUID then begin + Result := LN; + Exit; + end; + end; + Result := -1; +end; + +function TIdIMAP4Server.StripQuotesIfNecessary(AName: string): string; +begin + if Length(AName) > 0 then begin + if (AName[1] = '"') and (AName[Length(Result)] = '"') then begin {Do not Localize} + Result := Copy(AName, 2, Length(AName)-2); + Exit; + end; + end; + Result := AName; +end; + +function TIdIMAP4Server.ReassembleParams(ASeparator: Char; AParams: TStrings; + AParamToReassemble: Integer): Boolean; +var + LEndSeparator: char; + LTemp: string; + LN: integer; + LReassembledParam: string; +begin + Result := False; + case ASeparator of + '(': LEndSeparator := ')'; {Do not Localize} + '[': LEndSeparator := ']'; {Do not Localize} + else LEndSeparator := ASeparator; + end; + LTemp := AParams[AParamToReassemble]; + if (LTemp = '') or (LTemp[1] <> ASeparator) then begin + Exit; + end; + if LTemp[Length(LTemp)] = LEndSeparator then begin + AParams[AParamToReassemble] := Copy(LTemp, 2, Length(LTemp)-2); + Result := True; + Exit; + end; + LReassembledParam := Copy(LTemp, 2, MAXINT); + LN := AParamToReassemble + 1; + repeat + if LN >= AParams.Count - 1 then begin + Result := False; + Exit; //Error + end; + LTemp := AParams[LN]; + AParams.Delete(LN); + if LTemp[Length(LTemp)] = LEndSeparator then begin + AParams[AParamToReassemble] := LReassembledParam + ' ' + Copy(LTemp, 1, Length(LTemp)-1); {Do not Localize} + Result := True; + Exit; //This is example 1 + end; + LReassembledParam := LReassembledParam + ' ' + LTemp; {Do not Localize} + until False; +end; + +//This reorganizes the parameter list on the basis that AMailBoxParam is a +//mailbox name, which may (if enclosed in quotes) be in more than one param. +//Example 1: '43' '"My' 'Documents"' '5' -> '43' 'My Documents' '5' +//Example 2: '43' '"MyDocs"' '5' -> '43' 'MyDocs' '5' +//Example 3: '43' 'MyDocs' '5' -> '43' 'MyDocs' '5' +function TIdIMAP4Server.ReinterpretParamAsMailBox(AParams: TStrings; AMailBoxParam: Integer): Boolean; +var + LTemp: string; +begin + if (AMailBoxParam < 0) or (AMailBoxParam >= AParams.Count) then begin + Result := False; + Exit; + end; + LTemp := AParams[AMailBoxParam]; + if LTemp = '' then begin + Result := False; + Exit; + end; + if LTemp[1] <> '"' then begin {Do not Localize} + Result := True; + Exit; //This is example 3, no change. + end; + Result := ReassembleParams('"', AParams, AMailBoxParam); {Do not Localize} +end; + +function TIdIMAP4Server.ReinterpretParamAsFlags(AParams: TStrings; AFlagsParam: Integer): Boolean; +begin + Result := ReassembleParams('(', AParams, AFlagsParam); {Do not Localize} +end; + +function TIdIMAP4Server.ReinterpretParamAsQuotedStr(AParams: TStrings; AFlagsParam: integer): Boolean; +begin + Result := ReassembleParams('"', AParams, AFlagsParam); {Do not Localize} +end; + +function TIdIMAP4Server.ReinterpretParamAsDataItems(AParams: TStrings; AFlagsParam: Integer): Boolean; +begin + Result := ReassembleParams('(', AParams, AFlagsParam); {Do not Localize} +end; + +function TIdIMAP4Server.FlagStringToFlagList(AFlagList: TStrings; AFlagString: string): Boolean; +var + LTemp: string; +begin + AFlagList.Clear; + if (AFlagString <> '') and (AFlagString[1] = '(') and (AFlagString[Length(AFlagString)] = ')') then begin {Do not Localize} + LTemp := Copy(AFlagString, 2, Length(AFlagString)-2); + BreakApart(LTemp, ' ', AFlagList); {Do not Localize} + Result := True; + end else begin + Result := False; + end; +end; + +procedure TIdIMAP4Server.ProcessFetch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); +//There are a pile of options for this. +var + LMessageNumbers: TStringList; + LDataItems: TStringList; + LM: integer; + LN: integer; + LLO: integer; + LRecord: integer; + LSize: integer; + LMessageToCheck, LMessageTemp: TIdMessage; + LMessageRaw: TStringList; + LTemp: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5) + LMessageNumbers := TStringList.Create; + try + if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin + SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize} + Exit; + end; + if not ReinterpretParamAsDataItems(AParams, 1) then begin + SendBadReply(ASender, 'Fetch data items parameter is invalid.'); {Do not Localize} + Exit; + end; + LDataItems := TStringList.Create; + try + BreakApart(AParams[1], ' ', LDataItems); + for LN := 0 to LMessageNumbers.Count-1 do begin + if AUseUID then begin + LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox); + if LRecord = -1 then begin //It is OK to skip non-existent UID records + Continue; + end; + end else begin + LRecord := IndyStrToInt(LMessageNumbers[LN])-1; + end; + if (LRecord < 0) or (LRecord > LContext.MailBox.MessageList.Count) then begin + SendBadReply(ASender, 'Message number %d does not exist', [LRecord+1]); {Do not Localize} + Exit; + end; + LMessageToCheck := LContext.MailBox.MessageList.Messages[LRecord]; + for LLO := 0 to LDataItems.Count-1 do begin + if TextIsSame(LDataItems[LLO], 'UID') then begin {Do not Localize} + //Format: + //C9 FETCH 490 (UID) + //* 490 FETCH (UID 6545) + //C9 OK Completed + DoSendReply(ASender.Context, '* FETCH (UID %s)', [LMessageToCheck.UID]); {Do not Localize} + end + else if TextIsSame(LDataItems[LLO], 'FLAGS') then begin {Do not Localize} + //Format: + //C10 UID FETCH 6545 (FLAGS) + //* 490 FETCH (FLAGS (\Recent) UID 6545) + //C10 OK Completed + if AUseUID then begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s)', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LMessageNumbers[LN]]); + end else begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s))', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags)]); + end; + end + else if TextIsSame(LDataItems[LLO], 'RFC822.HEADER') then begin {Do not Localize} + //Format: + //C11 UID FETCH 6545 (RFC822.HEADER) + //* 490 FETCH (UID 6545 RFC822.HEADER {1654} + //Return-Path: + //... + //Content-Type: multipart/alternative; + // boundary="----=_NextPart_000_70BE_C8606D03.F4EA24EE" + //C10 OK Completed + //We don't want to thrash UIDs and flags in MailBox message, so load into LMessage + LMessageTemp := TIdMessage.Create; + try + if not OnDefMechGetMessageHeader(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageTemp) then begin + SendNoReply(ASender, 'Failed to get message header'); {Do not Localize} + Exit; + end; + //Need to calculate the size of the headers... + LSize := 0; + for LM := 0 to LMessageTemp.Headers.Count-1 do begin + Inc(LSize, Length(LMessageTemp.Headers.Strings[LM]) + 2); //Allow for CR+LF + end; + if AUseUID then begin + DoSendReply(ASender.Context, '* %d FETCH (UID %s RFC822.HEADER {%d}', {Do not Localize} + [LRecord+1, LMessageNumbers[LN], LSize]); + end else begin + DoSendReply(ASender.Context, '* %d FETCH (RFC822.HEADER {%d}', {Do not Localize} + [LRecord+1, LSize]); + end; + for LM := 0 to LMessageTemp.Headers.Count-1 do begin + DoSendReply(ASender.Context, LMessageTemp.Headers.Strings[LM]); + end; + DoSendReply(ASender.Context, ')'); {Do not Localize} + //Finished with the headers, free the memory... + finally + FreeAndNil(LMessageTemp); + end; + end + else if TextIsSame(LDataItems[LLO], 'RFC822.SIZE') then begin {Do not Localize} + //Format: + //C12 UID FETCH 6545 (RFC822.SIZE) + //* 490 FETCH (UID 6545 RFC822.SIZE 3447) + //C12 OK Completed + LSize := OnDefMechGetMessageSize(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck); + if LSize = -1 then begin + SendNoReply(ASender, 'Failed to get message size'); {Do not Localize} + Exit; + end; + if AUseUID then begin + DoSendReply(ASender.Context, '* %d FETCH (UID %s RFC822.SIZE %d)', {Do not Localize} + [LRecord+1, LMessageNumbers[LN], LSize]); + end else begin + DoSendReply(ASender.Context, '* %d FETCH (RFC822.SIZE %d)', {Do not Localize} + [LRecord+1, LSize]); + end; + end + else if PosInStrArray(LDataItems[LLO], ['BODY.PEEK[]', 'BODY[]', 'RFC822', 'RFC822.PEEK'], False) <> -1 then {Do not Localize} + begin + //All are the same, except the return string is different... + LMessageRaw := TStringList.Create; + try + if not OnDefMechGetMessageRaw(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageRaw) then + begin + SendNoReply(ASender, 'Failed to get raw message'); {Do not Localize} + Exit; + end; + LSize := 0; + for LM := 0 to LMessageToCheck.Headers.Count-1 do begin + Inc(LSize, Length(LMessageRaw.Strings[LM]) + 2); //Allow for CR+LF + end; + Inc(LSize, 3); //The message terminator '.CRLF' + LTemp := Copy(AParams[1], 2, Length(AParams[1])-2); + if AUseUID then begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s %s {%d}', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LMessageNumbers[LN], LTemp, LSize]); + end else begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) %s {%d}', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessageToCheck.Flags), LTemp, LSize]); + end; + for LM := 0 to LMessageToCheck.Headers.Count-1 do begin + DoSendReply(ASender.Context, LMessageRaw.Strings[LM]); + end; + DoSendReply(ASender.Context, '.'); {Do not Localize} + DoSendReply(ASender.Context, ')'); {Do not Localize} + //Free the memory... + finally + FreeAndNil(LMessageRaw); + end; + end + else if TextIsSame(LDataItems[LLO], 'BODYSTRUCTURE') then begin {Do not Localize} + //Format: + //C49 UID FETCH 6545 (BODYSTRUCTURE) + //* 490 FETCH (UID 6545 BODYSTRUCTURE (("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 290 8 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1125 41 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" + //C12 OK Completed + SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize} + end + else if TextStartsWith(LDataItems[LLO], 'BODY[') or TextStartsWith(LDataItems[LLO], 'BODY.PEEK[') then begin {Do not Localize} + //Format: + //C50 UID FETCH 6545 (BODY[1]) + //* 490 FETCH (FLAGS (\Recent \Seen) UID 6545 BODY[1] {290} + //... + //) + //C50 OK Completed + SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize} + end + else begin + SendBadReply(ASender, 'Parameter not supported: ' + AParams[1]); {Do not Localize} + Exit; + end; + end; + end; + finally + FreeAndNil(LDataItems); + end; + finally + FreeAndNil(LMessageNumbers); + end; + SendOkReply(ASender, 'Completed'); {Do not Localize} +end; + +procedure TIdIMAP4Server.ProcessSearch(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); +//if AUseUID is True, return UIDs rather than relative message numbers. +var + LSearchString: string; + LN: Integer; + LM: Integer; + LItem: Integer; + LMessageToCheck, LMessageTemp: TIdMessage; + LHits: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //Watch out: you could become an old man trying to implement all the IMAP + //search options, just do a subset. + //Format: + //C1065 UID SEARCH FROM "visible" + //* SEARCH 5769 5878 + //C1065 OK Completed (2 msgs in 0.010 secs) + if AParams.Count < 2 then begin //The only search options we support are 2-param ones + SendIncorrectNumberOfParameters(ASender); + //LParams.Free; + Exit; + end; + LItem := PosInStrArray(AParams[0], ['FROM', 'TO', 'CC', 'BCC', 'SUBJECT'], False); + if LItem = -1 then begin {Do not Localize} + SendBadReply(ASender, 'Unsupported search method'); {Do not Localize} + Exit; + end; + //Reassemble the other params into a line, because "Ciaran Costelloe" will be params 1 & 2... + LSearchString := AParams[1]; + for LN := 2 to AParams.Count-1 do begin + LSearchString := LSearchString + ' ' + AParams[LN]; {Do not Localize} + end; + if (LSearchString[1] = '"') and (LSearchString[Length(LSearchString)] = '"') then begin {Do not Localize} + LSearchString := Copy(LSearchString, 2, Length(LSearchString)-2); + end; + + LHits := ''; + LMessageTemp := TIdMessage.Create; + try + for LN := 0 to LContext.MailBox.MessageList.Count-1 do begin + LMessageToCheck := LContext.MailBox.MessageList.Messages[LN]; + if not OnDefMechGetMessageHeader(LContext.LoginName, LContext.MailBox.Name, LMessageToCheck, LMessageTemp) then + begin + SendNoReply(ASender, 'Failed to get message header'); {Do not Localize} + Exit; + end; + case LItem of + 0: // FROM {Do not Localize} + begin + if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.From.Address)) > 0 then begin + if AUseUID then begin + LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize} + end else begin + LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize} + end; + end; + end; + 1: // TO {Do not Localize} + begin + for LM := 0 to LMessageTemp.Recipients.Count-1 do begin + if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.Recipients.Items[LM].Address)) > 0 then begin + if AUseUID then begin + LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize} + end else begin + LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize} + end; + Break; //Don't want more than 1 hit on this record + end; + end; + end; + 2: // CC {Do not Localize} + begin + for LM := 0 to LMessageTemp.Recipients.Count-1 do begin + if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.CCList.Items[LM].Address)) > 0 then begin + if AUseUID then begin + LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize} + end else begin + LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize} + end; + Break; //Don't want more than 1 hit on this record + end; + end; + end; + 3: // BCC {Do not Localize} + begin + for LM := 0 to LMessageTemp.Recipients.Count-1 do begin + if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.BCCList.Items[LM].Address)) > 0 then begin + if AUseUID then begin + LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize} + end else begin + LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize} + end; + Break; //Don't want more than 1 hit on this record + end; + end; + end; + else // SUBJECT {Do not Localize} + begin + if Pos(UpperCase(LSearchString), UpperCase(LMessageTemp.Subject)) > 0 then begin + if AUseUID then begin + LHits := LHits + LMessageToCheck.UID + ' '; {Do not Localize} + end else begin + LHits := LHits + IntToStr(LN+1) + ' '; {Do not Localize} + end; + end; + end; + end; + end; + finally + FreeAndNil(LMessageTemp); + end; + DoSendReply(ASender.Context, '* SEARCH ' + TrimRight(LHits)); {Do not Localize} + SendOkReply(ASender, 'Completed'); {Do not Localize} +end; + +procedure TIdIMAP4Server.ProcessCopy(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings); +var + LMessageNumbers: TStringList; + LN: Integer; + LRecord: integer; + LResult: Boolean; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //Format is "C1 COPY 2:4 MEETINGFOLDER" + if AParams.Count < 2 then begin + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(AParams, 1) then begin + SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5) + LMessageNumbers := TStringList.Create; + try + if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin + SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize} + Exit; + end; + if not Assigned(OnDefMechDoesImapMailBoxExist) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, AParams[1]) then begin + SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize} + Exit; + end; + LResult := True; + for LN := 0 to LMessageNumbers.Count-1 do begin + if AUseUID then begin + LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox); + if LRecord = -1 then begin //It is OK to skip non-existent UID records + Continue; + end; + end else begin + LRecord := IndyStrToInt(LMessageNumbers[LN])-1; + end; + if (LRecord < 0) or (LRecord >= LContext.MailBox.MessageList.Count) then begin + LResult := False; + end + else if not OnDefMechCopyMessage(LContext.LoginName, LContext.MailBox.Name, + LContext.MailBox.MessageList.Messages[LRecord].UID, AParams[1]) then + begin + LResult := False; + end; + end; + if LResult then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Copy failed for one or more messages'); {Do not Localize} + end; + finally + FreeAndNil(LMessageNumbers); + end; +end; + +function TIdIMAP4Server.ProcessStore(AUseUID: Boolean; ASender: TIdCommand; AParams: TStrings): Boolean; +const + LCMsgFlags: array[0..4] of TIdMessageFlags = ( mfAnswered, mfFlagged, mfDeleted, mfDraft, mfSeen ); +var + LMessageNumbers: TStringList; + LFlagList: TStringList; + LN: integer; + LM: integer; + LRecord: integer; + LFlag: integer; + LTemp: string; + LStoreMethod: TIdIMAP4StoreDataItem; + LSilent: Boolean; + LMessage: TIdMessage; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //Format is: + //C53 UID STORE 6545,6544 +FLAGS.SILENT (\Deleted) + //C53 OK Completed + Result := False; + if AParams.Count < 3 then begin + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + //First param is a message set, e.g. 41 or 2:5 (which is 2, 3, 4 & 5) + LMessageNumbers := TStringList.Create; + try + if not MessageSetToMessageNumbers(AUseUID, ASender, LMessageNumbers, AParams[0]) then begin + SendBadReply(ASender, 'Error in syntax of message set parameter'); {Do not Localize} + Exit; + end; + LTemp := AParams[1]; + if LTemp[1] = '+' then begin {Do not Localize} + LStoreMethod := sdAdd; + LTemp := Copy(LTemp, 2, MaxInt); + end else if LTemp[1] = '-' then begin {Do not Localize} + LStoreMethod := sdRemove; + LTemp := Copy(LTemp, 2, MaxInt); + end else begin + LStoreMethod := sdReplace; + end; + if TextIsSame(LTemp, 'FLAGS') then begin {Do not Localize} + LSilent := False; + end else if TextIsSame(LTemp, 'FLAGS.SILENT') then begin {Do not Localize} + LSilent := True; + end else begin + SendBadReply(ASender, 'Error in syntax of FLAGS parameter'); {Do not Localize} + Exit; + end; + LFlagList := TStringList.Create; + try + //Assemble remaining flags back into a string... + LTemp := AParams[2]; + for LN := 3 to AParams.Count-1 do begin + LTemp := ' ' + AParams[LN]; {Do not Localize} + end; + if not FlagStringToFlagList(LFlagList, LTemp) then begin + SendBadReply(ASender, 'Error in syntax of flag set parameter'); {Do not Localize} + Exit; + end; + for LN := 0 to LMessageNumbers.Count-1 do begin + if AUseUID then begin + LRecord := GetRecordForUID(LMessageNumbers[LN], LContext.MailBox); + if LRecord = -1 then begin //It is OK to skip non-existent UID records + Continue; + end; + end else begin + LRecord := IndyStrToInt(LMessageNumbers[LN])-1; + end; + if (LRecord < 0) or (LRecord > LContext.MailBox.MessageList.Count) then begin + SendBadReply(ASender, 'Message number %d does not exist', [LRecord+1]); {Do not Localize} + Exit; + end; + LMessage := LContext.MailBox.MessageList.Messages[LRecord]; + if LStoreMethod = sdReplace then begin + LMessage.Flags := []; + end; + for LM := 0 to LFlagList.Count-1 do begin + //Support \Answered \Flagged \Deleted \Draft \Seen + LFlag := PosInStrArray(LFlagList[LM], ['\Answered', '\Flagged', '\Deleted', '\Draft', '\Seen'], False); {Do not Localize} + if LFlag = -1 then begin + Continue; + end; + case LStoreMethod of + sdAdd, sdReplace: + begin + LMessage.Flags := LMessage.Flags + [LCMsgFlags[LFlag]]; + end; + sdRemove: + begin + LMessage.Flags := LMessage.Flags - [LCMsgFlags[LFlag]]; + end; + end; + end; + if not LSilent then begin + //In this case, send to the client the current flags. + //The response is '* 43 FETCH (FLAGS (\Seen))' with the UID version + //being '* 43 FETCH (FLAGS (\Seen) UID 1234)'. Note the first number is the + //relative message number in BOTH cases. + if AUseUID then begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s) UID %s)', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessage.Flags), LMessageNumbers[LN]]); + end else begin + DoSendReply(ASender.Context, '* %d FETCH (FLAGS (%s))', {Do not Localize} + [LRecord+1, MessageFlagSetToStr(LMessage.Flags)]); + end; + end; + end; + SendOkReply(ASender, 'STORE Completed'); {Do not Localize} + finally + FreeAndNil(LFlagList); + end; + finally + FreeAndNil(LMessageNumbers); + end; + Result := True; +end; + +procedure TIdIMAP4Server.InitializeCommandHandlers; +var + LCommandHandler: TIdCommandHandler; +begin + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CAPABILITY'; {do not localize} + LCommandHandler.OnCommand := DoCommandCAPABILITY; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'NOOP'; {do not localize} + LCommandHandler.OnCommand := DoCommandNOOP; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'LOGOUT'; {do not localize} + LCommandHandler.OnCommand := DoCommandLOGOUT; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'AUTHENTICATE'; {do not localize} + LCommandHandler.OnCommand := DoCommandAUTHENTICATE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'LOGIN'; {do not localize} + LCommandHandler.OnCommand := DoCommandLOGIN; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SELECT'; {do not localize} + LCommandHandler.OnCommand := DoCommandSELECT; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'EXAMINE'; {do not localize} + LCommandHandler.OnCommand := DoCommandEXAMINE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CREATE'; {do not localize} + LCommandHandler.OnCommand := DoCommandCREATE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'DELETE'; {do not localize} + LCommandHandler.OnCommand := DoCommandDELETE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'RENAME'; {do not localize} + LCommandHandler.OnCommand := DoCommandRENAME; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SUBSCRIBE'; {do not localize} + LCommandHandler.OnCommand := DoCommandSUBSCRIBE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'UNSUBSCRIBE'; {do not localize} + LCommandHandler.OnCommand := DoCommandUNSUBSCRIBE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'LIST'; {do not localize} + LCommandHandler.OnCommand := DoCommandLIST; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'LSUB'; {do not localize} + LCommandHandler.OnCommand := DoCommandLSUB; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'STATUS'; {do not localize} + LCommandHandler.OnCommand := DoCommandSTATUS; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'APPEND'; {do not localize} + LCommandHandler.OnCommand := DoCommandAPPEND; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CHECK'; {do not localize} + LCommandHandler.OnCommand := DoCommandCHECK; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'CLOSE'; {do not localize} + LCommandHandler.OnCommand := DoCommandCLOSE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'EXPUNGE'; {do not localize} + LCommandHandler.OnCommand := DoCommandEXPUNGE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SEARCH'; {do not localize} + LCommandHandler.OnCommand := DoCommandSEARCH; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'FETCH'; {do not localize} + LCommandHandler.OnCommand := DoCommandFETCH; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'STORE'; {do not localize} + LCommandHandler.OnCommand := DoCommandSTORE; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'COPY'; {do not localize} + LCommandHandler.OnCommand := DoCommandCOPY; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'UID'; {do not localize} + LCommandHandler.OnCommand := DoCommandUID; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'X'; {do not localize} + LCommandHandler.OnCommand := DoCommandX; + LCommandHandler.NormalReply.Code := IMAP_OK; + + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'STARTTLS'; {do not localize} + LCommandHandler.OnCommand := DoCommandSTARTTLS; + LCommandHandler.NormalReply.Code := IMAP_OK; + + FCommandHandlers.OnBeforeCommandHandler := DoBeforeCmd; + FCommandHandlers.OnCommandHandlersException := DoCmdHandlersException; +end; + +//Command handlers + +procedure TIdIMAP4Server.DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string; + AContext: TIdContext); +begin + TIdIMAP4PeerContext(AContext).FLastCommand.ParseRequest(AData); //Main purpose is to get sequence number, like C11 from 'C11 CAPABILITY' + TIdIMAP4PeerContext(AContext).FIMAP4Tag := Fetch(AData, ' '); + AData := Trim(AData); + if Assigned(FOnBeforeCmd) then begin + FOnBeforeCmd(ASender, AData, AContext); + end; +end; + +procedure TIdIMAP4Server.DoSendReply(AContext: TIdContext; const AData: string); +begin + if Assigned(FOnBeforeSend) then begin + FOnBeforeSend(AContext, AData); + end; + AContext.Connection.IOHandler.WriteLn(AData); +end; + +procedure TIdIMAP4Server.DoSendReply(AContext: TIdContext; const AFormat: string; const Args: array of const); +begin + DoSendReply(AContext, IndyFormat(AFormat, Args)); +end; + +procedure TIdIMAP4Server.DoCmdHandlersException(ACommand: String; AContext: TIdContext); +var + LTag, LCmd: String; +begin + if Assigned(FOnCommandError) then begin + LTag := Fetch(ACommand, ' '); + LCmd := Fetch(ACommand, ' '); + OnCommandError(AContext, LTag, LCmd); + end; +end; + +procedure TIdIMAP4Server.DoCommandCAPABILITY(ASender: TIdCommand); +begin + if Assigned(FOnCommandCAPABILITY) then begin + OnCommandCAPABILITY(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {Tell the client our capabilities...} + DoSendReply(ASender.Context, '* CAPABILITY IMAP4rev1 AUTH=PLAIN'); {Do not Localize} + SendOkReply(ASender, 'Completed'); {Do not Localize} +end; + +procedure TIdIMAP4Server.DoCommandNOOP(ASender: TIdCommand); +begin + if Assigned(FOnCommandNOOP) then begin + OnCommandNOOP(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {On most servers, this does nothing (they use a timeout to disconnect users, + irrespective of NOOP commands, so they always return OK. If you really + want to implement it, use a countdown timer to force disconnects but reset + the counter if ANY command received, including NOOP.} + SendOkReply(ASender, 'Completed'); {Do not Localize} +end; + +procedure TIdIMAP4Server.DoCommandLOGOUT(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if Assigned(FOnCommandLOGOUT) then begin + OnCommandLOGOUT(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {Be nice and say ByeBye first...} + DoSendReply(ASender.Context, '* BYE May your God go with you.'); {Do not Localize} + SendOkReply(ASender, 'Completed'); {Do not Localize} + LContext.Connection.Disconnect(False); + LContext.MailBox.Clear; + LContext.RemoveFromList; +end; + +procedure TIdIMAP4Server.DoCommandAUTHENTICATE(ASender: TIdCommand); +begin + if Assigned(FOnCommandAUTHENTICATE) then begin + { + Important, when usng TLS and FUseTLS=utUseRequireTLS, do not accept any authentication + information until TLS negotiation is completed. This insistance is a security feature. + + Some networks should choose security over interoperability while other places may + sacrafice interoperability over security. It comes down to sensible administrative + judgement. + } + if (FUseTLS = utUseRequireTLS) and (ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase).PassThrough then begin + MustUseTLS(ASender); + end else begin + OnCommandAUTHENTICATE(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams); + end; + end; +end; + +procedure TIdIMAP4Server.MustUseTLS(ASender: TIdCommand); +begin + DoSendReply(ASender.Context, 'NO ' + RSSMTPSvrReqSTARTTLS); {Do not Localize} + ASender.Disconnect := True; +end; + +procedure TIdIMAP4Server.DoCommandLOGIN(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + + if Assigned(fOnCommandLOGIN) then begin + { + Important, when using TLS and FUseTLS=utUseRequireTLS, do not accept any authentication + information until TLS negotiation is completed. This insistance is a security feature. + + Some networks should choose security over interoperability while other places may + sacrafice interoperability over security. It comes down to sensible administrative + judgement. + } + if (FUseTLS = utUseRequireTLS) and (ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase).PassThrough then begin + MustUseTLS(ASender); + end else begin + OnCommandLOGIN(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + end; + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechDoesImapMailBoxExist) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 2 then begin + //Incorrect number of params... + if FSaferMode then begin + SendNoReply(ASender); + end else begin + SendIncorrectNumberOfParameters(ASender); + end; + Exit; + end; + //See if we have a directory under FRootPath of that user's name... + //if DoesImapMailBoxExist(LParams[0], '') = False then begin + if not OnDefMechDoesImapMailBoxExist(LParams[0], '') then begin + if FSaferMode then begin + SendNoReply(ASender); + end else begin + SendNoReply(ASender, 'Unknown username'); {Do not Localize} + end; + Exit; + end; + //See is it the correct password... + if not TextIsSame(FDefaultPassword, LParams[1]) then begin + if FSaferMode then begin + SendNoReply(ASender); + end else begin + SendNoReply(ASender, 'Incorrect password'); {Do not Localize} + end; + Exit; + end; + //Successful login, change context's state to logged in... + LContext.LoginName := LParams[0]; + LContext.FConnectionState := csAuthenticated; + SendOkReply(ASender, 'Completed'); {Do not Localize} + finally + FreeAndNil(LParams); + end; +end; + +//SELECT and EXAMINE are the same except EXAMINE opens the mailbox read-only +procedure TIdIMAP4Server.DoCommandSELECT(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState = csSelected then begin + LContext.MailBox.Clear; + LContext.FConnectionState := csAuthenticated; + end; + if LContext.ConnectionState <> csAuthenticated then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandSELECT) then begin + OnCommandSELECT(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechOpenMailBox) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + if OnDefMechOpenMailBox(ASender, False) then begin //SELECT opens the mailbox read-write + LContext.FConnectionState := csSelected; + SendOkReply(ASender, '[READ-WRITE] Completed'); {Do not Localize} + end; +end; + +//SELECT and EXAMINE are the same except EXAMINE opens the mailbox read-only +procedure TIdIMAP4Server.DoCommandEXAMINE(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandEXAMINE) then begin + OnCommandEXAMINE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechOpenMailBox) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + if OnDefMechOpenMailBox(ASender, True) then begin //EXAMINE opens the mailbox read-only + LContext.FConnectionState := csSelected; + SendOkReply(ASender, '[READ-ONLY] Completed'); {Do not Localize} + end; +end; + +procedure TIdIMAP4Server.DoCommandCREATE(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + { + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + } + if Assigned(FOnCommandCREATE) then begin + OnCommandCREATE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechDoesImapMailBoxExist)) + or (not Assigned(OnDefMechCreateMailBox)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 1 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin + SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + if OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin + SendBadReply(ASender, 'Mailbox already exists.'); {Do not Localize} + Exit; + end; + if OnDefMechCreateMailBox(LContext.LoginName, LParams[0]) then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Create failed'); {Do not Localize} + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandDELETE(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + { + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + } + if Assigned(FOnCommandDELETE) then begin + OnCommandDELETE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechDoesImapMailBoxExist)) + or (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechDeleteMailBox)) + or (not Assigned(OnDefMechIsMailBoxOpen)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //Make sure we don't have the mailbox open by anyone + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 1 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin + SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + if OnDefMechIsMailBoxOpen(LContext.LoginName, LParams[0]) then begin + SendNoReply(ASender, 'Mailbox is in use.'); {Do not Localize} + Exit; + end; + if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin + SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize} + Exit; + end; + if OnDefMechDeleteMailBox(LContext.LoginName, LParams[0]) then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Delete failed'); {Do not Localize} + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandRENAME(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + { + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + } + if Assigned(FOnCommandRENAME) then begin + OnCommandRENAME(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechDoesImapMailBoxExist)) + or (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechRenameMailBox)) + or (not Assigned(OnDefMechIsMailBoxOpen)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //Make sure we don't have the mailbox open by anyone + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 2 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin + SendBadReply(ASender, 'First mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + if OnDefMechIsMailBoxOpen(LContext.LoginName, LParams[0]) then begin + SendNoReply(ASender, 'Mailbox is in use.'); {Do not Localize} + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 1) then begin + SendBadReply(ASender, 'Second mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin + SendNoReply(ASender, 'Mailbox to be renamed does not exist.'); {Do not Localize} + Exit; + end; + if OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[1]) then begin + SendNoReply(ASender, 'Destination mailbox already exists.'); {Do not Localize} + Exit; + end; + if OnDefMechRenameMailBox(LContext.LoginName, LParams[0], LParams[1]) then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Delete failed'); {Do not Localize} + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandSUBSCRIBE(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(FOnCommandSUBSCRIBE) then begin + OnCommandSUBSCRIBE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {Not clear exactly what this would do in this sample mechanism...} + SendUnsupportedCommand(ASender); +end; + +procedure TIdIMAP4Server.DoCommandUNSUBSCRIBE(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(FOnCommandUNSUBSCRIBE) then begin + OnCommandUNSUBSCRIBE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {Not clear exactly what this would do in this sample mechanism...} + SendUnsupportedCommand(ASender); +end; + +procedure TIdIMAP4Server.DoCommandLIST(ASender: TIdCommand); +var + LParams: TStringList; + LMailBoxNames: TStringList; + LMailBoxFlags: TStringList; + LN: integer; + LEntry: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandLIST) then begin + OnCommandLIST(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechListMailBox) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //The default mechanism only supports the following format: + // LIST "" * + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 2 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if LParams[1] <> '*' then begin {Do not Localize} + SendBadReply(ASender, 'Parameter not supported, 2nd (last) parameter must be *'); {Do not Localize} + Exit; + end; + LMailBoxNames := TStringList.Create; + try + LMailBoxFlags := TStringList.Create; + try + if OnDefMechListMailBox(LContext.LoginName, LParams[0], LMailBoxNames, LMailBoxFlags) then begin + for LN := 0 to LMailBoxNames.Count-1 do begin + //Replies are of the form: + //* LIST (\HasNoChildren) "." "INBOX.CreatedFolder" + LEntry := '* LIST ('; {Do not Localize} + if LMailBoxFlags[LN] <> '' then begin + LEntry := LEntry + LMailBoxFlags[LN]; + end; + LEntry := LEntry + ') "' + MailBoxSeparator + '" "' + LMailBoxNames[LN] + '"'; {Do not Localize} + DoSendReply(ASender.Context, LEntry); {Do not Localize} + end; + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'List failed'); {Do not Localize} + end; + finally + FreeAndNil(LMailBoxFlags); + end; + finally + FreeAndNil(LMailBoxNames); + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandLSUB(ASender: TIdCommand); +var + LParams: TStringList; + LMailBoxNames: TStringList; + LMailBoxFlags: TStringList; + LN: integer; + LEntry: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandLSUB) then begin + OnCommandLSUB(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechListMailBox) then begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //Treat this the same as LIST... + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 2 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if LParams[1] <> '*' then begin {Do not Localize} + SendBadReply(ASender, 'Parameter not supported, 2nd (last) parameter must be *'); {Do not Localize} + Exit; + end; + LMailBoxNames := TStringList.Create; + try + LMailBoxFlags := TStringList.Create; + try + if OnDefMechListMailBox(LContext.LoginName, LParams[0], LMailBoxNames, LMailBoxFlags) then begin + for LN := 0 to LMailBoxNames.Count-1 do begin + //Replies are of the form: + //* LIST (\HasNoChildren) "." "INBOX.CreatedFolder" + LEntry := '* LIST ('; {Do not Localize} + if LMailBoxFlags[LN] <> '' then begin + LEntry := LEntry + LMailBoxFlags[LN]; + end; + LEntry := LEntry + ') "' + MailBoxSeparator + '" "' + LMailBoxNames[LN] + '"'; {Do not Localize} + DoSendReply(ASender.Context, LEntry); {Do not Localize} + end; + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'List failed'); {Do not Localize} + end; + finally + FreeAndNil(LMailBoxFlags); + end; + finally + FreeAndNil(LMailBoxNames); + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandSTATUS(ASender: TIdCommand); +var + LMailBox: TIdMailBox; + LN: integer; + LParams: TStringList; + LTemp: string; + LAnswer: string; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if not (LContext.ConnectionState in [csAuthenticated, csSelected]) then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandSTATUS) then begin + OnCommandSTATUS(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechDoesImapMailBoxExist)) + or (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechSetupMailbox)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //This can be issued for ANY mailbox, not just the currently selected one. + //The format is: + //C5 STATUS "INBOX" (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN) + //* STATUS INBOX (MESSAGES 490 RECENT 132 UIDNEXT 6546 UIDVALIDITY 1065090323 UNSEEN 167) + //C5 OK Completed + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 1 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin + SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + if not OnDefMechDoesImapMailBoxExist(LContext.LoginName, LParams[0]) then begin + SendNoReply(ASender, 'Mailbox does not exist.'); {Do not Localize} + Exit; + end; + {Get everything you need for this mailbox...} + LMailBox := TIdMailBox.Create; + try + OnDefMechSetupMailbox(LContext.LoginName, LParams[0], LMailBox); + {Send the stats...} + LAnswer := '* STATUS ' + LParams[0] + ' ('; {Do not Localize} + for LN := 1 to LParams.Count-1 do begin + LTemp := LParams[LN]; + if LTemp <> '' then begin + //Strip brackets (will be on 1st & last param) + if LTemp[1] = '(' then begin {Do not Localize} + LTemp := Copy(LTemp, 2, MaxInt); + end; + if (LTemp <> '') and (LTemp[Length(LTemp)] = ')') then begin {Do not Localize} + LTemp := Copy(LTemp, 1, Length(LTemp)-1); + end; + case PosInStrArray(LTemp, ['MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN'], False) of + 0: // MESSAGES {Do not Localize} + begin + LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.TotalMsgs) + ' '; {Do not Localize} + end; + 1: // RECENT {Do not Localize} + begin + LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.RecentMsgs) + ' '; {Do not Localize} + end; + 2: // UIDNEXT {Do not Localize} + begin + LAnswer := LAnswer + LTemp + ' ' + LMailBox.UIDNext + ' '; {Do not Localize} + end; + 3: // UIDVALIDITY {Do not Localize} + begin + LAnswer := LAnswer + LTemp + ' ' + LMailBox.UIDValidity + ' '; {Do not Localize} + end; + 4: // UNSEEN {Do not Localize} + begin + LAnswer := LAnswer + LTemp + ' ' + IntToStr(LMailBox.UnseenMsgs) + ' '; {Do not Localize} + end; + else + begin + SendBadReply(ASender, 'Parameter not supported: ' + LTemp); {Do not Localize} + Exit; + end; + end; + end; + end; + if LAnswer[Length(LAnswer)] = ' ' then begin {Do not Localize} + LAnswer := Copy(LAnswer, 1, Length(LAnswer)-1); + end; + LAnswer := LAnswer + ')'; {Do not Localize} + DoSendReply(ASender.Context, LAnswer); + SendOkReply(ASender, 'Completed'); {Do not Localize} + finally + FreeAndNil(LMailBox); + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandAPPEND(ASender: TIdCommand); +var + LUID: string; + LStream: TStream; + LFile: string; + LTemp: string; + LParams: TStringList; + LParams2: TStringList; + LFlagsList: TStringList; + LSize: integer; + LFlags, LInternalDateTime: string; + LN: integer; + LMessage: TIdMessage; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + //You do NOT need to be in selected state for this. + if LContext.ConnectionState <> csAuthenticated then begin + SendWrongConnectionState(ASender); + Exit; + end; + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(FOnCommandAPPEND) then begin + OnCommandAPPEND(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechGetNextFreeUID)) + or (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechUpdateNextFreeUID)) + or (not Assigned(OnDefMechDeleteMessage)) //Needed to reverse out a save if setting flags fail + or (not Assigned(OnDefMechGetFileNameToWriteAppendMessage)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + //Format (the flags and date/time are optional): + //C323 APPEND "INBOX.Sent" (\Seen) "internal date/time" {1876} + //+ go ahead + //... + //C323 OK [APPENDUID 1065095982 105] Completed + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 2 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + if not OnDefMechReinterpretParamAsMailBox(LParams, 0) then begin + SendBadReply(ASender, 'Mailbox parameter is invalid.'); {Do not Localize} + Exit; + end; + LFlags := ''; + LInternalDateTime := ''; + LN := 1; + LTemp := LParams[Ln]; + if TextStartsWith(LTemp, '(') then begin {Do not Localize} + if not ReinterpretParamAsFlags(LParams, Ln) then begin + SendBadReply(ASender, 'Flags parameter is invalid.'); {Do not Localize} + Exit; + end; + LFlags := LParams[Ln]; + Inc(Ln); + end + else if TextIsSame(LTemp, 'NIL') then begin {Do not Localize} + Inc(Ln); + end; + LTemp := LParams[Ln]; + if TextStartsWith(LTemp, '"') then begin {Do not Localize} + if not ReinterpretParamAsQuotedStr(LParams, Ln) then begin + SendBadReply(ASender, 'InternalDateTime parameter is invalid.'); {Do not Localize} + Exit; + end; + LInternalDateTime := LParams[Ln]; + end; + LTemp := LParams[LParams.Count-1]; + if not TextStartsWith(LTemp, '{') then begin {Do not Localize} + SendBadReply(ASender, 'Size parameter is invalid.'); {Do not Localize} + Exit; + end; + LSize := IndyStrToInt(Copy(LTemp, 2, Length(LTemp)-2)); + //Grab the next UID... + LUID := OnDefMechGetNextFreeUID(LContext.LoginName, LParams[0]); + //Get the message... + LFile := OnDefMechGetFileNameToWriteAppendMessage(LContext.LoginName, LContext.MailBox.Name, LUID); + LStream := TIdFileCreateStream.Create(LFile); + try + ASender.Context.Connection.IOHandler.ReadStream(LStream, LSize); + if LFlags = '' then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + //Update the (optional) flags... + LParams2 := TStringList.Create; + try + LParams2.Add(LUID); + LParams2.Add('FLAGS.SILENT'); {Do not Localize} + { + for LN := 1 to LParams.Count-2 do begin + LParams2.Add(LParams[LN]); + end; + } + //The flags are in a string, need to reassemble... + LFlagsList := TStringList.Create; + try + BreakApart(LFlags, ' ', LFlagsList); {Do not Localize} + for LN := 0 to LFlagsList.Count-1 do begin + LTemp := LFlagsList[LN]; + if LN = 0 then begin + LTemp := '(' + LTemp; {Do not Localize} + end; + if LN = LFlagsList.Count-1 then begin + LTemp := LTemp + ')'; {Do not Localize} + end; + LParams2.Add(LTemp); + end; + if not ProcessStore(True, ASender, LParams2) then begin + //Have to reverse out our changes if ANYTHING fails.. + LMessage := TIdMessage.Create(Self); + try + LMessage.UID := LUID; //This is all we need for deletion + OnDefMechDeleteMessage(LContext.LoginName, LContext.MailBox.Name, LMessage); + finally + FreeAndNil(LMessage); + end; + Exit; + end; + finally + FreeAndNil(LFlagsList); + end; + finally + FreeAndNil(LParams2); + end; + end; + //Update the next free UID in the .uid file... + OnDefMechUpdateNextFreeUID(LContext.LoginName, LContext.MailBox.Name, IntToStr(IndyStrToInt(LUID)+1)); + // TODO: implement this + { + if LInternalDateTime <> '' then + begin + // what to do here? + end; + } + finally + FreeAndNil(LStream); + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandCHECK(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(fOnCommandCHECK) then begin + OnCommandCHECK(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + {On most servers, this does nothing, they always return OK...} + SendOkReply(ASender, 'Completed'); {Do not Localize} +end; + +procedure TIdIMAP4Server.DoCommandCLOSE(ASender: TIdCommand); +var + LResult: Boolean; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(fOnCommandCLOSE) then begin + OnCommandCLOSE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechDeleteMessage) then begin //Used by ExpungeRecords + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + {This is an implicit expunge...} + LResult := ExpungeRecords(ASender); + {Now close it...} + LContext.MailBox.Clear; + LContext.FConnectionState := csAuthenticated; + if LResult then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Implicit expunge failed for one or more messages'); {Do not Localize} + end; +end; + +procedure TIdIMAP4Server.DoCommandEXPUNGE(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(FOnCommandEXPUNGE) then begin + OnCommandEXPUNGE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechDeleteMessage) then begin //Used by ExpungeRecords + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + if ExpungeRecords(ASender) then begin + SendOkReply(ASender, 'Completed'); {Do not Localize} + end else begin + SendNoReply(ASender, 'Expunge failed for one or more messages'); {Do not Localize} + end; +end; + +procedure TIdIMAP4Server.DoCommandSEARCH(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(fOnCommandSEARCH) then begin + OnCommandSEARCH(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if not Assigned(OnDefMechGetMessageHeader) then begin //Used by ProcessSearch + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + ProcessSearch(False, ASender, LParams); + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandFETCH(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(FOnCommandFETCH) then begin + OnCommandFETCH(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + if (not Assigned(OnDefMechGetMessageHeader)) //Used by ProcessFetch + or (not Assigned(OnDefMechGetMessageSize)) + or (not Assigned(OnDefMechGetMessageRaw)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + ProcessFetch(False, ASender, LParams); + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandSTORE(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(fOnCommandSTORE) then begin + OnCommandSTORE(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + ProcessStore(False, ASender, LParams); + finally + FreeAndNil(LParams); + end; +end; + +function TIdIMAP4Server.MessageFlagSetToStr(const AFlags: TIdMessageFlagsSet): String; +begin + Result := ''; + if mfAnswered in AFlags then begin + Result := Result + MessageFlags[mfAnswered] + ' '; {Do not Localize} + end; + if mfFlagged in AFlags then begin + Result := Result + MessageFlags[mfFlagged] + ' '; {Do not Localize} + end; + if mfDeleted in AFlags then begin + Result := Result + MessageFlags[mfDeleted] + ' '; {Do not Localize} + end; + if mfDraft in AFlags then begin + Result := Result + MessageFlags[mfDraft] + ' '; {Do not Localize} + end; + if mfSeen in AFlags then begin + Result := Result + MessageFlags[mfSeen] + ' '; {Do not Localize} + end; + if Result <> '' then begin + Result := TrimRight(Result); + end; +end; + +procedure TIdIMAP4Server.DoCommandCOPY(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if LContext.MailBox.State = msReadOnly then begin + SendErrorOpenedReadOnly(ASender); + Exit; + end; + if Assigned(FOnCommandCOPY) then begin + OnCommandCOPY(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + //Format is COPY 2:4 DestinationMailBoxName + if (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechCopyMessage)) then //Needed for ProcessCopy + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + ProcessCopy(False, ASender, LParams); + finally + FreeAndNil(LParams); + end; +end; + +{UID before COPY, FETCH or STORE means the record numbers are UIDs. + UID before SEARCH means SEARCH is to _return_ UIDs rather than relative numbers.} +procedure TIdIMAP4Server.DoCommandUID(ASender: TIdCommand); +var + LParams: TStringList; + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if LContext.ConnectionState <> csSelected then begin + SendWrongConnectionState(ASender); + Exit; + end; + if Assigned(fOnCommandUID) then begin + OnCommandUID(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if not FUseDefaultMechanismsForUnassignedCommands then begin + Exit; + end; + LParams := TStringList.Create; + try + BreakApart(ASender.UnparsedParams, ' ', LParams); {Do not Localize} + if LParams.Count < 1 then begin + //Incorrect number of params... + SendIncorrectNumberOfParameters(ASender); + Exit; + end; + //Map the commands to the general handler but remove the FETCH or whatever... + case PosInStrArray(LParams[0], ['FETCH', 'COPY', 'STORE', 'SEARCH'], False) of + 0: // FETCH {Do not Localize} + begin + if (not Assigned(OnDefMechGetMessageHeader)) //Used by ProcessFetch + or (not Assigned(OnDefMechGetMessageSize)) + or (not Assigned(OnDefMechGetMessageRaw)) then + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams.Delete(0); + ProcessFetch(True, ASender, LParams); + end; + 1: // COPY {Do not Localize} + begin + if (not Assigned(OnDefMechReinterpretParamAsMailBox)) + or (not Assigned(OnDefMechCopyMessage)) then //Needed for ProcessCopy + begin + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams.Delete(0); + ProcessCopy(True, ASender, LParams); + end; + 2: // STORE {Do not Localize} + begin + LParams.Delete(0); + ProcessStore(True, ASender, LParams); + end; + 3: // SEARCH {Do not Localize} + begin + if not Assigned(OnDefMechGetMessageHeader) then begin //Used by ProcessSearch + SendUnassignedDefaultMechanism(ASender); + Exit; + end; + LParams.Delete(0); + ProcessSearch(True, ASender, LParams); + end; + else + begin + SendUnsupportedCommand(ASender); + end; + end; + finally + FreeAndNil(LParams); + end; +end; + +procedure TIdIMAP4Server.DoCommandX(ASender: TIdCommand); +begin + if not Assigned(fOnCommandX) then begin + OnCommandX(ASender.Context, TIdIMAP4PeerContext(ASender.Context).IMAP4Tag, ASender.UnparsedParams); + end else if FUseDefaultMechanismsForUnassignedCommands then begin + SendUnsupportedCommand(ASender); + end; +end; + +procedure TIdIMAP4Server.DoCommandSTARTTLS(ASender: TIdCommand); +var + LContext: TIdIMAP4PeerContext; +begin + LContext := TIdIMAP4PeerContext(ASender.Context); + if (not (IOHandler is TIdServerIOHandlerSSLBase)) or (not (FUseTLS in ExplicitTLSVals)) then begin + OnCommandError(ASender.Context, LContext.IMAP4Tag, ASender.UnparsedParams); + Exit; + end; + if LContext.UsingTLS then begin // we are already using TLS + DoSendReply(ASender.Context, 'BAD %s', [RSIMAP4SvrNotPermittedWithTLS]); {do not localize} + Exit; + end; + // TODO: STARTTLS may only be issued in auth-state + DoSendReply(ASender.Context, 'OK %s', [RSIMAP4SvrBeginTLSNegotiation]); {do not localize} + (ASender.Context.Connection.IOHandler as TIdSSLIOHandlerSocketBase).Passthrough := False; +end; + +end. diff --git a/indy/Protocols/IdIPAddrMon.pas b/indy/Protocols/IdIPAddrMon.pas new file mode 100644 index 0000000..561c22f --- /dev/null +++ b/indy/Protocols/IdIPAddrMon.pas @@ -0,0 +1,455 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.7 10/26/2004 10:20:04 PM JPMugaas + Updated refs. + + Rev 1.6 2004.02.03 5:45:14 PM czhower + Name changes + + Rev 1.5 1/31/2004 1:18:40 PM JPMugaas + Illiminated Todo; item so it should work in DotNET. + + Rev 1.4 1/21/2004 3:11:04 PM JPMugaas + InitComponent + + Rev 1.3 10/19/2003 4:51:34 PM DSiders + Added localization comments. + + Rev 1.2 2003.10.12 3:53:12 PM czhower + compile todos + + Rev 1.1 3/5/2003 11:41:14 PM BGooijen + Added IdCoreGlobal to the uses, this file was needed for the call to + Sleep(...) + + Rev 1.0 12/28/2002 3:04:52 PM DSiders + Initial revision. +} + +unit IdIPAddrMon; + +{ + TIdIPAddrMon + + Monitors adapters known to the IP protocol stack for changes in any + of the IP addresses. Similar to TIdIPWatch, but monitors all IP + addresses/adapters. + + Does not keep a permanent IP address history list. But does trigger + a TIdIPAddrMonEvent event to signal the adapter number, old IP, and + new IP for the change in status. + + OnStatusChanged is used to capture changed IP addresses, and/or + to sync with GUI display controls. If you do not assign a procedure + for the event handler, this component essentially does nothing except + eat small amounts of CPU time. + + The thread instance is created and freed when the value in Active is + changed. + + TIdIPAddrMonEvent + + An procedure use to handle notifications from the component. Includes + parameters that represent the adapter number, previous IP or '', + and the current IP or ''. + + TIdIPAddrMonThread + + Timer thread for the IP address monitor component. Based on + TIdIPWatchThread. + + Sleeps in increments of .5 seconds until the Interval has elapsed, and + fires the timer event. Sleep is called in increments to allow checking + for Terminated when a long Interval has been specified. + + Original Author: + + Don Siders, Integral Systems, Fri 27 Dec 2002 + + Donated to the Internet Direct (Indy) Project for use under the + terms of the Indy Dual License. +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdComponent, + IdThread; + +const + IdIPAddrMonInterval = 500; + +type + TIdIPAddrMonEvent = procedure(ASender: TObject; AAdapter: Integer; AOldIP, ANewIP: string) of object; + + TIdIPAddrMonThread = class(TIdThread) + protected + FInterval: UInt32; + FOnTimerEvent: TNotifyEvent; + + procedure Run; override; + procedure DoTimerEvent; + end; + + TIdIPAddrMon = class(TIdComponent) + private + FActive: Boolean; + FBusy: Boolean; + FInterval: UInt32; + FAdapterCount: Integer; + FThread: TIdIPAddrMonThread; + // TODO: replace these with TIdStackLocalAddressList + FIPAddresses: TStrings; + FPreviousIPAddresses: TStrings; + FOnStatusChanged: TIdIPAddrMonEvent; + + procedure SetActive(Value: Boolean); + procedure SetInterval(Value: UInt32); + procedure GetAdapterAddresses; + procedure DoStatusChanged; + + protected + procedure InitComponent; override; + procedure Loaded; override; + + public + destructor Destroy; override; + procedure CheckAdapters(Sender: TObject); + procedure ForceCheck; + + property AdapterCount: Integer read FAdapterCount; + property Busy: Boolean read FBusy; + property IPAddresses: TStrings read FIPAddresses; + property Thread: TIdIPAddrMonThread read FThread; + + published + property Active: Boolean read FActive write SetActive; + property Interval: UInt32 read FInterval write SetInterval default IdIPAddrMonInterval; + property OnStatusChanged: TIdIPAddrMonEvent read FOnStatusChanged write FOnStatusChanged; + end; + +implementation + +uses + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + IdStack, + SysUtils; + +procedure TIdIPAddrMon.InitComponent; +begin + inherited InitComponent; + + FInterval := IdIPAddrMonInterval; + FActive := False; + FBusy := False; + FAdapterCount := 0; + + // TODO: replace these with TIdStackLocalAddressList + FIPAddresses := TStringList.Create; + FPreviousIPAddresses := TStringList.Create; + + // FThread created when component becomes Active +end; + +destructor TIdIPAddrMon.Destroy; +begin + Active := False; + FBusy := False; + + FIPAddresses.Free; + FPreviousIPAddresses.Free; + + // FThread freed on Terminate + + inherited Destroy; +end; + +procedure TIdIPAddrMon.Loaded; +begin + inherited Loaded; + // Active = True must not be performed before all other props are loaded + if Active then begin + FActive := False; + Active := True; + end; +end; + +procedure TIdIPAddrMon.CheckAdapters(Sender: TObject); +begin + // previous check could still be running... + if FBusy then begin + Exit; + end; + + FBusy := True; + try + try + GetAdapterAddresses; + + if IsDesignTime then begin + Exit; + end; + + // TODO: replace with TIdStackLocalAddressList + { + LChanged := FPreviousIPAddresses.Count <> FIPAddresses.Count; + if not LChanged then + begin + for I := 0 to FIPAddresses.Count-1 do begin + LChanged := FPreviousIPAddresses[I].IPAddress.Count <> FIPAddresses[I].IPAddress; + if LChanged then begin + Break; + end; + end; + end; + + if LChanged then begin + // something changed at runtime + DoStatusChanged; + end; + } + + if (FPreviousIPAddresses.Count <> FIPAddresses.Count) or + (FPreviousIPAddresses.Text <> FIPAddresses.Text) then + begin + // something changed at runtime + DoStatusChanged; + end; + except + // eat any exception + end; + finally + FBusy := False; + end; +end; + +procedure TIdIPAddrMon.DoStatusChanged; +var + iOldCount: Integer; + iNewCount: Integer; + iAdapter: Integer; + sOldIP: string; + sNewIP: string; +begin + + if not Assigned(FOnStatusChanged) then + begin + Exit; + end; + + // figure out the change... new, removed, or altered IP for adapter(s) + iOldCount := FPreviousIPAddresses.Count; + iNewCount := FIPAddresses.Count; + + // find the new adapter IP address + if iOldCount < iNewCount then + begin + sOldIP := ''; {do not localize} + + for iAdapter := 0 to iNewCount - 1 do + begin + // TODO: replace with TIdStackLocalAddressList + { + sNewIP := FIPAddresses[iAdapter].IPAddress; + + if FPreviousIPAddresses.IndexOfIP(sNewIP, FIPAddresses[iAdapter].IPVersion) = -1 then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + } + + sNewIP := FIPAddresses[iAdapter]; + + if FPreviousIPAddresses.IndexOf(sNewIP) = -1 then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + end; + end + + // find the missing adapter IP address + else if iOldCount > iNewCount then + begin + sNewIP := ''; {do not localize} + + for iAdapter := 0 to iOldCount - 1 do + begin + // TODO: replace with TIdStackLocalAddressList + { + sOldIP := FPreviousIPAddresses[iAdapter].IPAddress; + + if FIPAddresses.IndexOfIP(sOldIP, FPreviousIPAddresses[iAdapter].IPVersion) = -1 then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + } + + sOldIP := FPreviousIPAddresses[iAdapter]; + + if FIPAddresses.IndexOf(sOldIP) = -1 then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + end; + end + + // find the altered adapter IP address + else + begin + for iAdapter := 0 to AdapterCount - 1 do + begin + // TODO: replace with TIdStackLocalAddressList + { + sOldIP := FPreviousIPAddresses[iAdapter].IPAddress; + sNewIP := FIPAddresses[iAdapter].IPAddress; + + if (FPreviousIPAddresses[iAdapter].IPVersion <> FIPAddresses[iAdapter].IPVersion) or + (sOldIP <> sNewIP) then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + } + + sOldIP := FPreviousIPAddresses[iAdapter]; + sNewIP := FIPAddresses[iAdapter]; + + if sOldIP <> sNewIP then + begin + FOnStatusChanged(Self, iAdapter, sOldIP, sNewIP); + end; + end; + end; + +end; + +procedure TIdIPAddrMon.ForceCheck; +begin + CheckAdapters(nil); +end; + +procedure TIdIPAddrMon.SetActive(Value: Boolean); +begin + if Value <> FActive then + begin + if Value then + begin + // get initial addresses at start-up and allow display in IDE + GetAdapterAddresses; + end; + if (not IsDesignTime) and (not IsLoading) then + begin + if Value then + begin + FThread := TIdIPAddrMonThread.Create(True); + FThread.FOnTimerEvent := CheckAdapters; + FThread.FInterval := Self.Interval; + FThread.Start; + end else + begin + if FThread <> nil then begin + FThread.TerminateAndWaitFor; + FreeAndNil(FThread); + end; + end; + end; + FActive := Value; + end; +end; + +procedure TIdIPAddrMon.SetInterval(Value: UInt32); +begin + FInterval := Value; + if Assigned(FThread) then begin + FThread.FInterval := FInterval; + end; +end; + +procedure TIdIPAddrMonThread.Run; +var + lInterval: Integer; +begin + lInterval := FInterval; + while lInterval > 0 do + begin + // force a check for terminated every .5 sec + if lInterval > 500 then + begin + IndySleep(500); + lInterval := lInterval - 500; + end else + begin + IndySleep(lInterval); + LInterval := 0; + end; + if Terminated then + begin + Exit; + end; + end; + + // interval has elapsed... fire the thread timer event + Synchronize(DoTimerEvent); +end; + +procedure TIdIPAddrMonThread.DoTimerEvent; +begin + if Assigned(FOnTimerEvent) then begin + FOnTimerEvent(Self); + end; +end; + +procedure TIdIPAddrMon.GetAdapterAddresses; +var + LAddresses: TIdStackLocalAddressList; + I: Integer; +begin + { + Doesn't keep a permanent history list like TIdIPWatch... + but does track previous IP addresses to detect changes. + } + + FPreviousIPAddresses.Assign(FIPAddresses); + FIPAddresses.Clear; + + LAddresses := TIdStackLocalAddressList.Create; + try + GStack.GetLocalAddressList(LAddresses); + for I := 0 to LAddresses.Count-1 do begin + FIPAddresses.Add(LAddresses[I].IPAddress); + end; + finally + LAddresses.Free; + end; + + FAdapterCount := FIPAddresses.Count; +end; + +end. diff --git a/indy/Protocols/IdIPWatch.pas b/indy/Protocols/IdIPWatch.pas new file mode 100644 index 0000000..2eeecda --- /dev/null +++ b/indy/Protocols/IdIPWatch.pas @@ -0,0 +1,388 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.5 10/26/2004 11:08:04 PM JPMugaas + Updated refs. + + Rev 1.4 2004.02.03 5:43:54 PM czhower + Name changes + + Rev 1.3 2/1/2004 3:33:46 AM JPMugaas + Reenabled. SHould work in DotNET. + + Rev 1.2 1/21/2004 3:11:12 PM JPMugaas + InitComponent + + Rev 1.1 2003.10.12 4:03:58 PM czhower + compile todos + + Rev 1.0 11/13/2002 07:55:32 AM JPMugaas + +2000-Dec-22 Kudzu + -Changed from a TTimer to a sleeping thread to eliminate the reference to ExtCtrls. This was the + only unit in all of Indy that used this unit and caused the pkg to rely on extra pkgs. + -Changed Enabled to Active to be more consistent + -Active now also defaults to false to be more consistent + +2000-MAY-10 Hadi Hariri + -Added new feature to Force Check of status + +2000-Apr-23 Hadi Hariri + -Converted to Indy + +2000-Mar-01 Johannes Berg + - new property HistoryFilename + - new property MaxHistoryEntries + - new property HistoryEnabled + +2000-Jan-13 MTL + -Moved to new Palette Scheme (Winshoes Misc) +} + +unit IdIPWatch; + +{ + Simple component determines Online status, + returns current IP address, and (optionally) keeps history on + IP's issued. + + Original Author: Dave Nosker - AfterWave Technologies (allbyte@jetlink.net) +} + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdGlobal, + IdComponent, IdThread; + +const + IP_WATCH_HIST_MAX = 25; + IP_WATCH_HIST_FILENAME = 'iphist.dat'; {Do not Localize} + IP_WATCH_INTERVAL = 1000; + +type + TIdIPWatchThread = class(TIdThread) + protected + FInterval: Integer; + FTimerEvent: TNotifyEvent; + // + procedure Run; override; + procedure TimerEvent; + end; + + TIdIPWatch = class(TIdComponent) + protected + FActive: Boolean; + FCurrentIP: string; + FHistoryEnabled: Boolean; + FHistoryFilename: string; + FIPHistoryList: TStringList; + FIsOnline: Boolean; + FLocalIPHuntBusy: Boolean; + FMaxHistoryEntries: Integer; + FOnLineCount: Integer; + FOnStatusChanged: TNotifyEvent; + FPreviousIP: string; + FThread: TIdIPWatchThread; + FWatchInterval: UInt32; + // + procedure AddToIPHistoryList(Value: string); + procedure CheckStatus(Sender: TObject); + procedure SetActive(Value: Boolean); + procedure SetMaxHistoryEntries(Value: Integer); + procedure SetWatchInterval(Value: UInt32); + procedure InitComponent; override; + public + {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} + constructor Create(AOwner: TComponent); reintroduce; overload; + {$ENDIF} + destructor Destroy; override; + function ForceCheck: Boolean; + procedure LoadHistory; + function LocalIP: string; + procedure SaveHistory; + // + property CurrentIP: string read FCurrentIP; + property IPHistoryList: TStringList read FIPHistoryList; + property IsOnline: Boolean read FIsOnline; + property PreviousIP: string read FPreviousIP; + published + property Active: Boolean read FActive write SetActive; + property HistoryEnabled: Boolean read FHistoryEnabled write FHistoryEnabled default True; + property HistoryFilename: string read FHistoryFilename write FHistoryFilename; + property MaxHistoryEntries: Integer read FMaxHistoryEntries write SetMaxHistoryEntries + default IP_WATCH_HIST_MAX; + property OnStatusChanged: TNotifyEvent read FOnStatusChanged write FOnStatusChanged; + property WatchInterval: UInt32 read FWatchInterval write SetWatchInterval + default IP_WATCH_INTERVAL; + end; + +implementation + +uses + {$IFDEF DOTNET} + {$IFDEF USE_INLINE} + System.Threading, + System.IO, + {$ENDIF} + {$ENDIF} + {$IFDEF USE_VCL_POSIX} + Posix.SysSelect, + Posix.SysTime, + {$ENDIF} + IdStack, SysUtils; + +{ TIdIPWatch } + +procedure TIdIPWatch.AddToIPHistoryList(Value: string); +begin + if (Value = '') or (Value = '127.0.0.1') or (Value = '::1') then {Do not Localize} + begin + Exit; + end; + + // Make sure the last entry does not allready contain the new one... + if FIPHistoryList.Count > 0 then + begin + if FIPHistoryList[FIPHistoryList.Count-1] = Value then + begin + Exit; + end; + end; + + FIPHistoryList.Add(Value); + if FIPHistoryList.Count > MaxHistoryEntries then + begin + FIPHistoryList.Delete(0); + end; +end; + +procedure TIdIPWatch.CheckStatus(Sender: TObject); +var + WasOnLine: Boolean; + OldIP: string; +begin + try + if FLocalIPHuntBusy then + begin + Exit; + end; + WasOnLine := FIsOnline; + OldIP := FCurrentIP; + FCurrentIP := LocalIP; + FIsOnline := (FCurrentIP <> '127.0.0.1') and (FCurrentIP <> '::1') and (FCurrentIP <> ''); {Do not Localize} + + if (WasOnline) and (not FIsOnline) then + begin + if (OldIP <> '127.0.0.1') and (OldIP <> '::1') and (OldIP <> '') then {Do not Localize} + begin + FPreviousIP := OldIP; + end; + AddToIPHistoryList(FPreviousIP); + end; + + if (not WasOnline) and (FIsOnline) then + begin + if FOnlineCount = 0 then + begin + FOnlineCount := 1; + end; + if FOnlineCount = 1 then + begin + if FPreviousIP = FCurrentIP then + begin + // Del last history item... + if FIPHistoryList.Count > 0 then + begin + FIPHistoryList.Delete(FIPHistoryList.Count-1); + end; + // Change the Previous IP# to the remaining last item on the list + // OR to blank if none on list. + if FIPHistoryList.Count > 0 then + begin + FPreviousIP := FIPHistoryList[FIPHistoryList.Count-1]; + end + else + begin + FPreviousIP := ''; {Do not Localize} + end; + end; + end; + FOnlineCount := 2; + end; + + if ((WasOnline) and (not FIsOnline)) or ((not WasOnline) and (FIsOnline)) then + begin + if (not IsDesignTime) and Assigned(FOnStatusChanged) then + begin + FOnStatusChanged(Self); + end; + end; + except + end; +end; + +{$IFDEF WORKAROUND_INLINE_CONSTRUCTORS} +constructor TIdIPWatch.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; +{$ENDIF} + +procedure TIdIPWatch.InitComponent; +begin + inherited; + FIPHistoryList := TStringList.Create; + FIsOnLine := False; + FOnLineCount := 0; + FWatchInterval := IP_WATCH_INTERVAL; + FActive := False; + FPreviousIP := ''; {Do not Localize} + FLocalIPHuntBusy := False; + FHistoryEnabled:= True; + FHistoryFilename:= IP_WATCH_HIST_FILENAME; + FMaxHistoryEntries:= IP_WATCH_HIST_MAX; +end; + +destructor TIdIPWatch.Destroy; +begin + if FIsOnLine then begin + AddToIPHistoryList(FCurrentIP); + end; + Active := False; + SaveHistory; + FIPHistoryList.Free; + inherited; +end; + +function TIdIPWatch.ForceCheck: Boolean; +begin + // Forces a check and doesn't wait for the timer to fire. {Do not Localize} + // It will return true if online. + CheckStatus(nil); + Result := FIsOnline; +end; + +procedure TIdIPWatch.LoadHistory; +begin + if not IsDesignTime then begin + FIPHistoryList.Clear; + if FileExists(FHistoryFilename) and FHistoryEnabled then + begin + FIPHistoryList.LoadFromFile(FHistoryFileName); + if FIPHistoryList.Count > 0 then + begin + FPreviousIP := FIPHistoryList[FIPHistoryList.Count-1]; + end; + end; + end; +end; + +function TIdIPWatch.LocalIP: string; +begin + FLocalIpHuntBusy := True; + try + // TODO: use GStack.GetLocalAddressList() instead, as + // GStack.LocalAddress only supports IPv4 addresses + // at this time... + Result := GStack.LocalAddress; + finally + FLocalIPHuntBusy := False; + end; +end; + +procedure TIdIPWatch.SaveHistory; +begin + if (not IsDesignTime) and FHistoryEnabled then begin + FIPHistoryList.SaveToFile(FHistoryFilename); + end; +end; + +procedure TIdIPWatch.SetActive(Value: Boolean); +begin + if Value <> FActive then begin + if not IsDesignTime then begin + if Value then begin + FThread := TIdIPWatchThread.Create(True); + FThread.FTimerEvent := CheckStatus; + FThread.FInterval := FWatchInterval; + FThread.Start; + end else begin + if FThread <> nil then begin + FThread.TerminateAndWaitFor; + FreeAndNil(FThread); + end; + end; + end; + FActive := Value; + end; +end; + +procedure TIdIPWatch.SetMaxHistoryEntries(Value: Integer); +begin + FMaxHistoryEntries:= Value; + while FIPHistoryList.Count > MaxHistoryEntries do // delete the oldest... + FIPHistoryList.Delete(0); +end; + +procedure TIdIPWatch.SetWatchInterval(Value: UInt32); +begin + if Value <> FWatchInterval then begin + FWatchInterval := Value; + end; + + // might be necessary even if its the same, for example + // when loading (not 100% sure though) + if Assigned(FThread) then begin + FThread.FInterval := FWatchInterval; + end; +end; + +{ TIdIPWatchThread } + +procedure TIdIPWatchThread.Run; +var + LInterval: Integer; +begin + LInterval := FInterval; + while LInterval > 0 do begin + if LInterval > 500 then begin + IndySleep(500); + LInterval := LInterval - 500; + end else begin + IndySleep(LInterval); + LInterval := 0; + end; + if Terminated then begin + Exit; + end; + Synchronize(TimerEvent); + end; +end; + +procedure TIdIPWatchThread.TimerEvent; +begin + if Assigned(FTimerEvent) then begin + FTimerEvent(Self); + end; +end; + +end. diff --git a/indy/Protocols/IdIRC.pas b/indy/Protocols/IdIRC.pas new file mode 100644 index 0000000..1bc6951 --- /dev/null +++ b/indy/Protocols/IdIRC.pas @@ -0,0 +1,2940 @@ +{ + $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$ +} +{ + 2003-11-Jul: + Original author: Sergio Perry + Matthew Elzer - bug fixes & modifications +} + +unit IdIRC; + +{ + Based on TIRCClient component by Steve Williams (stevewilliams@kromestudios.com) + ported to Indy by Daaron Dwyer (ddwyer@ncic.com) +} + +{ Based on RFC 2812 } + +interface + +{$i IdCompilerDefines.inc} + +uses + Classes, + IdAssignedNumbers, IdContext, IdCmdTCPClient, IdCommandHandlers, + IdIOHandler, IdGlobal, IdException; + +type + TIdIRC = class; + + TIdIRCUserMode = (amAway, amInvisible, amWallops, amRestricted, amOperator, amLocalOperator, amReceiveServerNotices); + TIdIRCUserModes = set of TIdIRCUserMode; + + TIdIRCStat = (stServerConnectionsList, stCommandUsageCount, stOperatorList, stUpTime); + + { -WELCOME- } + TIdIRCServerMsgEvent = procedure(ASender: TIdContext; const AMsg: String) of object; + TIdIRCMyInfoEvent = procedure(ASender: TIdContext; const AServer, AVersion, AUserModes, AChanModes, AExtra: String) of object; + TIdIRCBounceEvent = procedure(ASender: TIdContext; const AHost: String; APort: Integer; const AInfo: String) of object; + TIdIRCISupportEvent = procedure(ASender: TIdContext; AParameters: TStrings) of object; + { -PING- } + TIdIRCPingPongEvent = procedure(ASender: TIdContext) of object; + { -MESSAGE- } + TIdIRCPrivMessageEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATarget, AMessage: String) of object; + { -NOTICE- } + TIdIRCNoticeEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATarget, ANotice: String) of object; + { -REHASH- } + TIdIRCRehashEvent = procedure(ASender: TIdContext; const ANickname, AHost: String) of object; + { -SUMMON- } + TIdIRCSummonEvent = procedure(ASender: TIdContext; const ANickname, AHost: String) of object; + { -WALLOPS- } + TIdIRCWallopsEvent = procedure(ASender: TIdContext; const ANickname, AHost, AMessage: String) of object; + { -ISON- } + TIdIRCIsOnIRCEvent = procedure(ASender: TIdContext; const ANickname, AHost: String) of object; + { -AWAY- } + TIdIRCAwayEvent = procedure(ASender: TIdContext; const ANickname, AHost, AAwayMessage: String; UserAway: Boolean) of object; + { -JOIN- } + TIdIRCJoinEvent = procedure(ASender: TIdContext; const ANickname, AHost, AChannel: String) of object; + { -PART- } + TIdIRCPartEvent = procedure(ASender: TIdContext; const ANickname, AHost, AChannel, APartMessage: String) of object; + { -TOPIC- } + TIdIRCTopicEvent = procedure(ASender: TIdContext; const ANickname, AHost, AChannel, ATopic: String) of object; + { -KICK- } + TIdIRCKickEvent = procedure(ASender: TIdContext; const ANickname, AHost, AChannel, ATarget, AReason: String) of object; + { -MOTD- } + TIdIRCMOTDEvent = procedure(ASender: TIdContext; AMOTD: TStrings) of object; + { -TRACE- } + TIdIRCServerTraceEvent = procedure(ASender: TIdContext; ATraceInfo: TStrings) of object; + { -OPER- } + TIdIRCOpEvent = procedure(ASender: TIdContext; const ANickname, AChannel, AHost: String) of object; + { -INV- } + TIdIRCInvitingEvent = procedure(ASender: TIdContext; const ANickname, AHost: String) of object; + TIdIRCInviteEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATarget, AChannel: String) of object; + { -LIST- } + TIdIRCChanBANListEvent = procedure(ASender: TIdContext; const AChannel: String; ABanList: TStrings) of object; + TIdIRCChanEXCListEvent = procedure(ASender: TIdContext; const AChannel: String; AExceptList: TStrings) of object; + TIdIRCChanINVListEvent = procedure(ASender: TIdContext; const AChannel: String; AInviteList: TStrings) of object; + TIdIRCServerListEvent = procedure(ASender: TIdContext; AServerList: TStrings) of object; + TIdIRCNickListEvent = procedure(ASender: TIdContext; const AChannel: String; ANicknameList: TStrings) of object; + { -STATS- } + TIdIRCServerUsersEvent = procedure(ASender: TIdContext; AUsers: TStrings) of object; + TIdIRCServerStatsEvent = procedure(ASender: TIdContext; AStatus: TStrings) of object; + TIdIRCKnownServerNamesEvent = procedure(ASender: TIdContext; AKnownServers: TStrings) of object; + { -INFO- } + TIdIRCAdminInfoRecvEvent = procedure(ASender: TIdContext; AAdminInfo: TStrings) of object; + TIdIRCUserInfoRecvEvent = procedure(ASender: TIdContext; const AUserInfo: String) of object; + { -WHO- } + TIdIRCWhoEvent = procedure(ASender: TIdContext; AWhoResults: TStrings) of object; + TIdIRCWhoIsEvent = procedure(ASender: TIdContext; AWhoIsResults: TStrings) of object; + TIdIRCWhoWasEvent = procedure(ASender: TIdContext; AWhoWasResults: TStrings) of object; + { Mode } + TIdIRCChanModeEvent = procedure(ASender: TIdContext; const ANickname, AHost, AChannel, AMode, AParams: String) of object; + TIdIRCUserModeEvent = procedure(ASender: TIdContext; const ANickname, AHost, AMode: String) of object; + { -CTCP- } + TIdIRCCTCPQueryEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATarget, ACommand, AParams: String) of object; + TIdIRCCTCPReplyEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATarget, ACommand, AParams: String) of object; + { -DCC- } + TIdIRCDCCChatEvent = procedure(ASender: TIdContext; const ANickname, AHost: String; APort: Integer) of object; + TIdIRCDCCSendEvent = procedure(ASender: TIdContext; const ANickname, AHost, AFilename: String; APort: TIdPort; AFileSize: Int64) of object; + TIdIRCDCCResumeEvent = procedure(ASender: TIdContext; const ANickname, AHost, AFilename: String; APort: TIdPort; AFilePos: Int64) of object; + TIdIRCDCCAcceptEvent = procedure(ASender: TIdContext; const ANickname, AHost, AFilename: String; APort: TIdPort; AFilePos: Int64) of object; + { -Errors- } + TIdIRCServerErrorEvent = procedure(ASender: TIdContext; AErrorCode: Integer; const AErrorMessage: String) of object; + TIdIRCNickErrorEvent = procedure(ASender: TIdContext; AError: Integer) of object; + TIdIRCKillErrorEvent = procedure(ASender: TIdContext) of object; + { Other } + TIdIRCNicknameChangedEvent = procedure(ASender: TIdContext; const AOldNickname, AHost, ANewNickname: String) of object; + TIdIRCKillEvent = procedure(ASender: TIdContext; const ANickname, AHost, ATargetNickname, AReason: String) of object; + TIdIRCQuitEvent = procedure(ASender: TIdContext; const ANickname, AHost, AReason: String) of object; + TIdIRCSvrQuitEvent = procedure(ASender: TIdContext; const ANickname, AHost, AServer, AReason: String) of object; + TIdIRCSvrTimeEvent = procedure(ASender: TIdContext; const AHost, ATime: String) of object; + TIdIRCServiceEvent = procedure(ASender: TIdContext) of object; + TIdIRCSvrVersionEvent = procedure(ASender: TIdContext; const AVersion, AHost, AComments: String) of object; + TIdIRCRawEvent = procedure(ASender: TIdContext; AIn: Boolean; const AMessage: String) of object; + + EIdIRCError = class(EIdException); + + TIdIRCReplies = class(TPersistent) + protected + FFinger: String; + FVersion: String; + FUserInfo: String; + FClientInfo: String; + public + constructor Create; + procedure Assign(Source: TPersistent); override; + published + property Finger: String read FFinger write FFinger; + property Version: String read FVersion write FVersion; + property UserInfo: String read FUserInfo write FUserInfo; + property ClientInfo: String read FClientInfo write FClientInfo; + end; + + TIdIRC = class(TIdCmdTCPClient) + protected + FNickname: String; + FAltNickname: String; + FAltNickUsed: Boolean; + // + FUsername: String; + FRealName: String; + FPassword: String; + FUserMode: TIdIRCUserModes; + FUserAway: Boolean; + FReplies: TIdIRCReplies; + // + FSenderNick: String; + FSenderHost: String; + // + FBans: TStrings; + FExcepts: TStrings; + FInvites: TStrings; + FLinks: TStrings; + FMotd: TStrings; + FNames: TStrings; + FWho: TStrings; + FWhoIs: TStrings; + FWhoWas: TStrings; + FSvrList: TStrings; + FUsers: TStrings; + // + FOnSWelcome: TIdIRCServerMsgEvent; + FOnYourHost: TIdIRCServerMsgEvent; + FOnSCreated: TIdIRCServerMsgEvent; + FOnMyInfo: TIdIRCMyInfoEvent; + FOnBounce: TIdIRCBounceEvent; + FOnISupport: TIdIRCISupportEvent; + FOnSError: TIdIRCServerMsgEvent; + FOnPingPong: TIdIRCPingPongEvent; + FOnPrivMessage: TIdIRCPrivMessageEvent; + FOnNotice: TIdIRCNoticeEvent; + FOnRehash: TIdIRCRehashEvent; + FOnSummon: TIdIRCSummonEvent; + FOnWallops: TIdIRCWallopsEvent; + FOnIsOnIRC: TIdIRCIsOnIRCEvent; + FOnAway: TIdIRCAwayEvent; + FOnJoin: TIdIRCJoinEvent; + FOnPart: TIdIRCPartEvent; + FOnTopic: TIdIRCTopicEvent; + FOnKick: TIdIRCKickEvent; + FOnMOTD: TIdIRCMOTDEvent; + FOnTrace: TIdIRCServerTraceEvent; + FOnOp: TIdIRCOpEvent; + FOnInviting: TIdIRCInvitingEvent; + FOnInvite: TIdIRCInviteEvent; + FOnBANList: TIdIRCChanBANListEvent; + FOnEXCList: TIdIRCChanEXCListEvent; + FOnINVList: TIdIRCChanINVListEvent; + FOnSvrList: TIdIRCServerListEvent; + FOnNickList: TIdIRCNickListEvent; + FOnSvrUsers: TIdIRCServerUsersEvent; + FOnSvrStats: TIdIRCServerStatsEvent; + FOnKnownSvrs: TIdIRCKnownServerNamesEvent; + FOnAdminInfo: TIdIRCAdminInfoRecvEvent; + FOnUserInfo: TIdIRCUserInfoRecvEvent; + FOnWho: TIdIRCWhoEvent; + FOnWhoIs: TIdIRCWhoIsEvent; + FOnWhoWas: TIdIRCWhoWasEvent; + FOnChanMode: TIdIRCChanModeEvent; + FOnUserMode: TIdIRCUserModeEvent; + FOnCTCPQry: TIdIRCCTCPQueryEvent; + FOnCTCPRep: TIdIRCCTCPReplyEvent; + FOnDCCChat: TIdIRCDCCChatEvent; + FOnDCCSend: TIdIRCDCCSendEvent; + FOnDCCResume: TIdIRCDCCResumeEvent; + FOnDCCAccept: TIdIRCDCCAcceptEvent; + FOnServerError: TIdIRCServerErrorEvent; + FOnNickError: TIdIRCNickErrorEvent; + FOnKillError: TIdIRCKillErrorEvent; + FOnNickChange: TIdIRCNicknameChangedEvent; + FOnKill: TIdIRCKillEvent; + FOnQuit: TIdIRCQuitEvent; + FOnSvrQuit: TIdIRCSvrQuitEvent; + FOnSvrTime: TIdIRCSvrTimeEvent; + FOnService: TIdIRCServiceEvent; + FOnSvrVersion: TIdIRCSvrVersionEvent; + FOnRaw: TIdIRCRawEvent; + // + function GetUsedNickname: String; + procedure SetNickname(const AValue: String); + procedure SetUsername(const AValue: String); + procedure SetIdIRCUserMode(AValue: TIdIRCUserModes); + procedure SetIdIRCReplies(AValue: TIdIRCReplies); + function GetUserMode: String; + procedure ParseDCC(AContext: TIdContext; const ADCC: String); + //Command handlers + procedure DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext); + procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); override; + procedure DoBounce(ASender: TIdCommand; ALegacy: Boolean); + procedure CommandPRIVMSG(ASender: TIdCommand); + procedure CommandNOTICE(ASender: TIdCommand); + procedure CommandJOIN(ASender: TIdCommand); + procedure CommandPART(ASender: TIdCommand); + procedure CommandKICK(ASender: TIdCommand); + procedure CommandMODE(ASender: TIdCommand); + procedure CommandNICK(ASender: TIdCommand); + procedure CommandQUIT(ASender: TIdCommand); + procedure CommandSQUIT(ASender: TIdCommand); + procedure CommandINVITE(ASender: TIdCommand); + procedure CommandKILL(ASender: TIdCommand); + procedure CommandPING(ASender: TIdCommand); + procedure CommandERROR(ASender: TIdCommand); + procedure CommandWALLOPS(ASender: TIdCommand); + procedure CommandTOPIC(ASender: TIdCommand); + procedure CommandWELCOME(ASender: TIdCommand); + procedure CommandYOURHOST(ASender: TIdCommand); + procedure CommandCREATED(ASender: TIdCommand); + procedure CommandMYINFO(ASender: TIdCommand); + procedure CommandISUPPORT(ASender: TIdCommand); + procedure CommandBOUNCE(ASender: TIdCommand); + procedure CommandUSERHOST(ASender: TIdCommand); + procedure CommandISON(ASender: TIdCommand); + procedure CommandWHOIS(ASender: TIdCommand); + procedure CommandENDOFWHOIS(ASender: TIdCommand); + procedure CommandWHOWAS(ASender: TIdCommand); + procedure CommandENDOFWHOWAS(ASender: TIdCommand); + procedure CommandLISTSTART(ASender: TIdCommand); + procedure CommandLIST(ASender: TIdCommand); + procedure CommandLISTEND(ASender: TIdCommand); + procedure CommandAWAY(ASender: TIdCommand); + procedure CommandINVITING(ASender: TIdCommand); + procedure CommandSUMMONING(ASender: TIdCommand); + procedure CommandINVITELIST(ASender: TIdCommand); + procedure CommandENDOFINVITELIST(ASender: TIdCommand); + procedure CommandEXCEPTLIST(ASender: TIdCommand); + procedure CommandENDOFEXCEPTLIST(ASender: TIdCommand); + procedure CommandWHOREPLY(ASender: TIdCommand); + procedure CommandENDOFWHO(ASender: TIdCommand); + procedure CommandNAMEREPLY(ASender: TIdCommand); + procedure CommandENDOFNAMES(ASender: TIdCommand); + procedure CommandLINKS(ASender: TIdCommand); + procedure CommandENDOFLINKS(ASender: TIdCommand); + procedure CommandBANLIST(ASender: TIdCommand); + procedure CommandENDOFBANLIST(ASender: TIdCommand); + procedure CommandINFO(ASender: TIdCommand); + procedure CommandENDOFINFO(ASender: TIdCommand); + procedure CommandMOTD(ASender: TIdCommand); + procedure CommandENDOFMOTD(ASender: TIdCommand); + procedure CommandREHASHING(ASender: TIdCommand); + procedure CommandUSERSSTART(ASender: TIdCommand); + procedure CommandUSERS(ASender: TIdCommand); + procedure CommandENDOFUSERS(ASender: TIdCommand); + procedure CommandENDOFSTATS(ASender: TIdCommand); + procedure CommandSERVLIST(ASender: TIdCommand); + procedure CommandSERVLISTEND(ASender: TIdCommand); + procedure CommandTIME(ASender: TIdCommand); + procedure CommandSERVICE(ASender: TIdCommand); + procedure CommandVERSION(ASender: TIdCommand); + procedure CommandCHANMODE(ASender: TIdCommand); + procedure CommandOPER(ASender: TIdCommand); + procedure CommandNICKINUSE(ASender: TIdCommand); + // + procedure AssignIRCClientCommands; + function GetCmdHandlerClass: TIdCommandHandlerClass; override; + procedure SetIOHandler(AValue: TIdIOHandler); override; + procedure InitComponent; override; + public + destructor Destroy; override; + // + procedure Connect; override; + procedure Disconnect(const AReason: String = ''); reintroduce; + // + function IsChannel(const AChannel: String): Boolean; + function IsOp(const ANickname: String): Boolean; + function IsVoice(const ANickname: String): Boolean; + procedure Raw(const ALine: String); + procedure Say(const ATarget, AMsg: String); + procedure Notice(const ATarget, AMsg: String); + procedure Action(const ATarget, AMsg: String); + procedure CTCPQuery(const ATarget, ACommand, AParameters: String); + procedure CTCPReply(const ATarget, ACTCP, AReply: String); + procedure Join(const AChannel: String; const AKey: String =''); + procedure Part(const AChannel: String; const AReason: String = ''); + procedure Kick(const AChannel, ANickname: String; const AReason: String = ''); + procedure SetChannelMode(const AChannel, AMode: String; const AParams: String = ''); + procedure SetUserMode(const ANickname, AMode: String); + procedure GetChannelTopic(const AChannel: String); + procedure SetChannelTopic(const AChannel, ATopic: String); + procedure SetAway(const AMsg: String); + procedure Op(const AChannel, ANickname: String); + procedure Deop(const AChannel, ANickname: String); + procedure Voice(const AChannel, ANickname: String); + procedure Devoice(const AChannel, ANickname: String); + procedure Ban(const AChannel, AHostmask: String); + procedure Unban(const AChannel, AHostmask: String); + procedure RegisterService(const ANickname, ADistribution, AInfo: String; AType: Integer); + procedure ListChannelNicknames(const AChannel: String; const ATarget: String = ''); + procedure ListChannel(const AChannel: String; const ATarget: String = ''); + procedure Invite(const ANickname, AChannel: String); + procedure GetMessageOfTheDay(const ATarget: String = ''); + procedure GetNetworkStatus(const AHostMask: String = ''; const ATarget: String = ''); + procedure GetServerVersion(const ATarget: String = ''); + procedure GetServerStatus(AQuery: TIdIRCStat; const ATarget: String = ''); + procedure ListKnownServerNames(const ARemoteHost: String = ''; const AHostMask: String = ''); + procedure QueryServerTime(const ATarget: String = ''); + procedure RequestServerConnect(const ATargetHost: String; APort: Integer; const ARemoteHost: String = ''); + procedure TraceServer(const ATarget: String = ''); + procedure GetAdminInfo(const ATarget: String = ''); + procedure GetServerInfo(const ATarget: String = ''); + procedure ListNetworkServices(const AHostMask: String = ''; const AType: String = ''); + procedure QueryService(const AServiceName, AMessage: String); + procedure Who(const AMask: String; AOnlyAdmins: Boolean); + procedure WhoIs(const AMask: String; const ATarget: String = ''); + procedure WhoWas(const ANickname: String; ACount: Integer = -1; const ATarget: String = ''); + procedure Kill(const ANickname, AComment: String); + procedure Ping(const AServer1: String; const AServer2: String = ''); + procedure Pong(const AServer1: String; const AServer2: String = ''); + procedure Error(const AMessage: String); + procedure ReHash; + procedure Die; + procedure Restart; + procedure Summon(const ANickname: String; const ATarget: String = ''; const AChannel: String = ''); + procedure ListServerUsers(const ATarget: String = ''); + procedure SayWALLOPS(const AMessage: String); + procedure GetUserInfo(const ANickname: String); + procedure GetUsersInfo(const ANicknames: array of String); + procedure IsOnIRC(const ANickname: String); overload; + procedure IsOnIRC(const ANicknames: array of String); overload; + procedure BecomeOp(const ANickname, APassword: String); + procedure SQuit(const AHost, AComment: String); + procedure SetChannelLimit(const AChannel: String; ALimit: Integer); + procedure SetChannelKey(const AChannel, AKey: String); + // + property Away: Boolean read FUserAway; + published + property Nickname: String read FNickname write SetNickname; + property AltNickname: String read FAltNickname write FAltNickname; + property UsedNickname: String read GetUsedNickname; // returns Nickname or AltNickname + property Username: String read FUsername write SetUsername; + property RealName: String read FRealName write FRealName; + property Password: String read FPassword write FPassword; + property Port default IdPORT_IRC; + property Replies: TIdIRCReplies read FReplies write SetIdIRCReplies; + property UserMode: TIdIRCUserModes read FUserMode write SetIdIRCUserMode; + { Events } + property OnServerWelcome: TIdIRCServerMsgEvent read FOnSWelcome write FOnSWelcome; + property OnYourHost: TIdIRCServerMsgEvent read FOnYourHost write FOnYourHost; + property OnServerCreated: TIdIRCServerMsgEvent read FOnSCreated write FOnSCreated; + property OnMyInfo: TIdIRCMyInfoEvent read FOnMyInfo write FOnMyInfo; + property OnBounce: TIdIRCBounceEvent read FOnBounce write FOnBounce; + property OnISupport: TIdIRCISupportEvent read FOnISupport write FOnISupport; + property OnPingPong: TIdIRCPingPongEvent read FOnPingPong write FOnPingPong; + property OnPrivateMessage: TIdIRCPrivMessageEvent read FOnPrivMessage write FOnPrivMessage; + property OnNotice: TIdIRCNoticeEvent read FOnNotice write FOnNotice; + property OnRehash: TIdIRCRehashEvent read FOnRehash write FOnRehash; + property OnSummon: TIdIRCSummonEvent read FOnSummon write FOnSummon; + property OnWallops: TIdIRCWallopsEvent read FOnWallops write FOnWallops; + property OnIsOnIRC: TIdIRCIsOnIRCEvent read FOnIsOnIRC write FOnIsOnIRC; + property OnAway: TIdIRCAwayEvent read FOnAway write FOnAway; + property OnJoin: TIdIRCJoinEvent read FOnJoin write FOnJoin; + property OnPart: TIdIRCPartEvent read FOnPart write FOnPart; + property OnTopic: TIdIRCTopicEvent read FOnTopic write FOnTopic; + property OnKick: TIdIRCKickEvent read FOnKick write FOnKick; + property OnMOTD: TIdIRCMOTDEvent read FOnMOTD write FOnMOTD; + property OnTrace: TIdIRCServerTraceEvent read FOnTrace write FOnTrace; + property OnOp: TIdIRCOpEvent read FOnOp write FOnOp; + property OnInviting: TIdIRCInvitingEvent read FOnInviting write FOnInviting; + property OnInvite: TIdIRCInviteEvent read FOnInvite write FOnInvite; + property OnBanListReceived: TIdIRCChanBANListEvent read FOnBANList write FOnBANList; + property OnExceptionListReceived: TIdIRCChanEXCListEvent read FOnEXCList write FOnEXCList; + property OnInvitationListReceived: TIdIRCChanINVListEvent read FOnINVList write FOnINVList; + property OnServerListReceived: TIdIRCServerListEvent read FOnSvrList write FOnSvrList; + property OnNicknamesListReceived: TIdIRCNickListEvent read FOnNickList write FOnNickList; + property OnServerUsersListReceived: TIdIRCServerUsersEvent read FOnSvrUsers write FOnSvrUsers; + property OnServerStatsReceived: TIdIRCServerStatsEvent read FOnSvrStats write FOnSvrStats; + property OnKnownServersListReceived: TIdIRCKnownServerNamesEvent read FOnKnownSvrs write FOnKnownSvrs; + property OnAdminInfoReceived: TIdIRCAdminInfoRecvEvent read FOnAdminInfo write FOnAdminInfo; + property OnUserInfoReceived: TIdIRCUserInfoRecvEvent read FOnUserInfo write FOnUserInfo; + property OnWho: TIdIRCWhoEvent read FOnWho write FOnWho; + property OnWhoIs: TIdIRCWhoIsEvent read FOnWhoIs write FOnWhoIs; + property OnWhoWas: TIdIRCWhoWasEvent read FOnWhoWas write FOnWhoWas; + property OnChannelMode: TIdIRCChanModeEvent read FOnChanMode write FOnChanMode; + property OnUserMode: TIdIRCUserModeEvent read FOnUserMode write FOnUserMode; + property OnCTCPQuery: TIdIRCCTCPQueryEvent read FOnCTCPQry write FOnCTCPQry; + property OnCTCPReply: TIdIRCCTCPReplyEvent read FOnCTCPRep write FOnCTCPRep; + property OnDCCChat: TIdIRCDCCChatEvent read FOnDCCChat write FOnDCCChat; + property OnDCCSend: TIdIRCDCCSendEvent read FOnDCCSend write FOnDCCSend; + property OnDCCResume: TIdIRCDCCResumeEvent read FOnDCCResume write FOnDCCResume; + property OnDCCAccept: TIdIRCDCCAcceptEvent read FOnDCCAccept write FOnDCCAccept; + property OnServerError: TIdIRCServerErrorEvent read FOnServerError write FOnServerError; + property OnNicknameError: TIdIRCNickErrorEvent read FOnNickError write FOnNickError; + property OnKillError: TIdIRCKillErrorEvent read FOnKillError write FOnKillError; + property OnNicknameChange: TIdIRCNicknameChangedEvent read FOnNickChange write FOnNickChange; + property OnKill: TIdIRCKillEvent read FOnKill write FOnKill; + property OnQuit: TIdIRCQuitEvent read FOnQuit write FOnQuit; + property OnServerQuit: TIdIRCSvrQuitEvent read FOnSvrQuit write FOnSvrQuit; + property OnServerTime: TIdIRCSvrTimeEvent read FOnSvrTime write FOnSvrTime; + property OnService: TIdIRCServiceEvent read FOnService write FOnService; + property OnServerVersion: TIdIRCSvrVersionEvent read FOnSvrVersion write FOnSvrVersion; + property OnRaw: TIdIRCRawEvent read FOnRaw write FOnRaw; + end; + +implementation + +uses + IdGlobalProtocols, IdResourceStringsProtocols, IdSSL, + IdStack, IdBaseComponent, SysUtils; + +const + IdIRCCTCP: array[0..11] of String = ('ACTION', 'SOUND', 'PING', 'FINGER', {do not localize} + 'USERINFO', 'VERSION', 'CLIENTINFO', 'TIME', 'ERROR', 'DCC', 'SED', 'ERRMSG'); {do not localize} + + MQuote = #16; + XDelim = #1; + XQuote = #92; + +{ TIdIRCReplies } + +constructor TIdIRCReplies.Create; +begin + inherited Create; + // +end; + +procedure TIdIRCReplies.Assign(Source: TPersistent); +var + LSource: TIdIRCReplies; +begin + if Source is TIdIRCReplies then + begin + LSource := TIdIRCReplies(Source); + FFinger := LSource.Finger; + FFinger := LSource.Finger; + FVersion := LSource.Version; + FUserInfo := LSource.UserInfo; + FClientInfo := LSource.ClientInfo; + end else begin + inherited Assign(Source); + end; +end; + +{ TIdIRC } + +// RLebeau 1/7/2010: SysUtils.TrimLeft() removes all characters < #32, but +// CTC requires character #1, so don't remove that character when parsing +// IRC parameters in FetchIRCParam()... +// +function IRCTrimLeft(const S: string): string; +var + I, L: Integer; +begin + L := Length(S); + I := 1; + while (I <= L) and (S[I] <= ' ') and (S[I] <> XDelim) do begin + Inc(I); + end; + Result := Copy(S, I, Maxint); +end; + +function FetchIRCParam(var S: String): String; +var + LTmp: String; +begin + LTmp := IRCTrimLeft(S); + if TextStartsWith(LTmp, ':') then + begin + Result := Copy(LTmp, 2, MaxInt); + S := ''; + end else + begin + Result := Fetch(LTmp, ' '); + S := IRCTrimLeft(LTmp); + end; +end; + +function IRCQuote(const S: String): String; +begin + // IMPORTANT! MQuote needs to be the first character in the replacement + // list, otherwise it will end up being double-escaped if the other + // character get replaced, which will produce the wrong output!! + Result := StringsReplace(S, [MQuote, #0, LF, CR], [MQuote+MQuote, MQuote+'0', MQuote+'n', MQuote+'r']); +end; + +{$IFDEF STRING_IS_IMMUTABLE} +function FindCharInSB(const ASB: TIdStringBuilder; AChar: Char; AStart: Integer): Integer; +begin + for Result := AStart to ASB.Length-1 do begin + if ASB[Result] = AChar then begin + Exit; + end; + end; + Result := -1; +end; +{$ENDIF} + +function IRCUnquote(const S: String): String; +var + I, L: Integer; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} + +begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(S); + L := LSB.Length; + I := 0; + while I < L do begin + I := FindCharInSB(LSB, MQuote, I); + if I = -1 then begin + Break; + end; + LSB.Remove(I, 1); + Dec(L); + if I >= L then begin + Break; + end; + case LSB[I] of + '0': LSB[I] := #0; + 'n': LSB[I] := LF; + 'r': LSB[I] := CR; + end; + Inc(I); + end; + Result := LSB.ToString; + {$ELSE} + Result := S; + L := Length(Result); + I := 1; + while I <= L do begin + I := PosIdx(MQuote, Result, I); + if I = 0 then begin + Break; + end; + IdDelete(Result, I, 1); + Dec(L); + if I > L then begin + Break; + end; + case Result[I] of + '0': Result[I] := #0; + 'n': Result[I] := LF; + 'r': Result[I] := CR; + end; + Inc(I); + end; + {$ENDIF} +end; + +function CTCPQuote(const S: String): String; +begin + Result := StringsReplace(S, [XDelim, XQuote], [XQuote+'a', XQuote+XQuote]); +end; + +function CTCPUnquote(const S: String): String; +var + I, L: Integer; + {$IFDEF STRING_IS_IMMUTABLE} + LSB: TIdStringBuilder; + {$ENDIF} +begin + {$IFDEF STRING_IS_IMMUTABLE} + LSB := TIdStringBuilder.Create(S); + L := LSB.Length; + I := 0; + while I < L do begin + I := FindCharInSB(LSB, XQuote, I); + if I = -1 then begin + Break; + end; + LSB.Remove(I, 1); + Dec(L); + if I >= L then begin + Break; + end; + if LSB[I] = 'a' then begin + LSB[I] := XDelim; + end; + Inc(I); + end; + Result := LSB.ToString; + {$ELSE} + Result := S; + L := Length(Result); + I := 1; + while I <= L do begin + I := PosIdx(XQuote, Result, I); + if I = 0 then begin + Break; + end; + IdDelete(Result, I, 1); + Dec(L); + if I > L then begin + Break; + end; + if Result[I] = 'a' then begin + Result[I] := XDelim; + end; + Inc(I); + end; + {$ENDIF} +end; + +procedure ExtractCTCPs(var AText: String; CTCPs: TStrings); +var + LTmp: String; + I, J, K: Integer; +begin + I := 1; + repeat + J := PosIdx(XDelim, AText, I); + if J = 0 then begin + Break; + end; + K := PosIdx(XDelim, AText, J+1); + if K = 0 then begin + Break; + end; + LTmp := Copy(AText, J+1, K-J-1); + LTmp := CTCPUnquote(LTmp); + CTCPs.Add(LTmp); + IdDelete(AText, J, (K-J)+1); + I := J; + until False; +end; + +type + TIdIRCCommandHandler = class(TIdCommandHandler) + public + procedure DoParseParams(AUnparsedParams: string; AParams: TStrings); override; + end; + +procedure TIdIRCCommandHandler.DoParseParams(AUnparsedParams: string; AParams: TStrings); +begin + AParams.Clear; + while AUnparsedParams <> '' do begin + AParams.Add(FetchIRCParam(AUnparsedParams)); + end; +end; + +function TIdIRC.GetCmdHandlerClass: TIdCommandHandlerClass; +begin + Result := TIdIRCCommandHandler; +end; + +procedure TIdIRC.InitComponent; +begin + inherited InitComponent; + // + FReplies := TIdIRCReplies.Create; + Port := IdPORT_IRC; + FUserMode := []; + + // RLebeau 2/21/08: for the IRC protocol, RFC 2812 section 2.4 says that + // clients are not allowed to issue numeric replies for server-issued + // commands. Added the PerformReplies property so TIdIRC can specify + // that behavior. + CommandHandlers.PerformReplies := False; + + // RLebeau 3/11/08: most of the command handlers should parse parameters by default + CommandHandlers.ParseParamsDefault := True; + + if not IsDesignTime then begin + AssignIRCClientCommands; + end; +end; + +destructor TIdIRC.Destroy; +begin + FreeAndNil(FReplies); + FreeAndNil(FBans); + FreeAndNil(FExcepts); + FreeAndNil(FInvites); + FreeAndNil(FLinks); + FreeAndNil(FMotd); + FreeAndNil(FNames); + FreeAndNil(FWho); + FreeAndNil(FWhoIs); + FreeAndNil(FWhoWas); + FreeAndNil(FSvrList); + FreeAndNil(FUsers); + inherited Destroy; +end; + +function TIdIRC.GetUserMode: String; +const + IdIRCUserModeChars: array[TIdIRCUserMode] of Char = ('a', 'i', 'w', 'r', 'o', 'O', 's'); {do not localize} +var + i: TIdIRCUserMode; +begin + if FUserMode <> [] then + begin + Result := '+'; + for i := amAway to amReceiveServerNotices do begin + if i in FUserMode then begin + Result := Result + IdIRCUserModeChars[i]; + end; + end; + end else begin + Result := '0'; + end; +end; + +procedure TIdIRC.Connect; +begin + // I doubt that there is explicit SSL support in the IRC protocol + if (IOHandler is TIdSSLIOHandlerSocketBase) then begin + (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False; + end; + inherited Connect; + // + try + FAltNickUsed := False; + if FPassword <> '' then begin + Raw(IndyFormat('PASS %s', [FPassword])); {do not localize} + end; + SetNickname(FNickname); + SetUsername(FUsername); + except + on E: EIdSocketError do begin + inherited Disconnect; + IndyRaiseOuterException(EIdIRCError.Create(RSIRCCannotConnect)); + end; + end; +end; + +procedure TIdIRC.Disconnect(const AReason: String = ''); +begin + try + Raw(IndyFormat('QUIT :%s', [AReason])); {do not localize} + finally + inherited Disconnect; + end; +end; + +procedure TIdIRC.Raw(const ALine: String); +begin + if Connected then begin + if Assigned(FOnRaw) then begin + FOnRaw(nil, False, ALine); + end; + IOHandler.WriteLn(IRCQuote(ALine)); + end; +end; + +procedure TIdIRC.AssignIRCClientCommands; +var + LCommandHandler: TIdCommandHandler; +begin + { Text commands } + //PRIVMSG Nickname/#channel :message + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'PRIVMSG'; {do not localize} + LCommandHandler.OnCommand := CommandPRIVMSG; + LCommandHandler.ParseParams := False; + + //NOTICE Nickname/#channel :message + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'NOTICE'; {do not localize} + LCommandHandler.OnCommand := CommandNOTICE; + LCommandHandler.ParseParams := False; + + //JOIN #channel + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'JOIN'; {do not localize} + LCommandHandler.OnCommand := CommandJOIN; + + //PART #channel + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'PART'; {do not localize} + LCommandHandler.OnCommand := CommandPART; + + //KICK #channel target :reason + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'KICK'; {do not localize} + LCommandHandler.OnCommand := CommandKICK; + + //MODE Nickname/#channel +/-modes parameters... + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'MODE'; {do not localize} + LCommandHandler.OnCommand := CommandMODE; + LCommandHandler.ParseParams := False; + + //NICK newNickname + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'NICK'; {do not localize} + LCommandHandler.OnCommand := CommandNICK; + + //QUIT :reason + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'QUIT'; {do not localize} + LCommandHandler.OnCommand := CommandQUIT; + + //SQUIT server :reason + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'SQUIT'; {do not localize} + LCommandHandler.OnCommand := CommandSQUIT; + + //INVITE Nickname :#channel + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'INVITE'; {do not localize} + LCommandHandler.OnCommand := CommandINVITE; + + //KILL Nickname :reason + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'KILL'; {do not localize} + LCommandHandler.OnCommand := CommandKILL; + + //PING server + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'PING'; {do not localize} + LCommandHandler.OnCommand := CommandPING; + + //WALLOPS :message + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'WALLOPS'; {do not localize} + LCommandHandler.OnCommand := CommandWALLOPS; + LCommandHandler.ParseParams := False; + + //TOPIC + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'TOPIC'; {do not localize} + LCommandHandler.OnCommand := CommandTOPIC; + + //ERROR message + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := 'ERROR'; {do not localize} + LCommandHandler.OnCommand := CommandERROR; + LCommandHandler.ParseParams := False; + + { Numeric commands, refer to http://www.alien.net.au/irc/irc2numerics.html } + //RPL_WELCOME + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '001'; {do not localize} + LCommandHandler.OnCommand := CommandWELCOME; + LCommandHandler.ParseParams := False; + + //RPL_YOURHOST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '002'; {do not localize} + LCommandHandler.OnCommand := CommandYOURHOST; + + //RPL_CREATED + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '003'; {do not localize} + LCommandHandler.OnCommand := CommandCREATED; + + //RPL_MYINFO + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '004'; {do not localize} + LCommandHandler.OnCommand := CommandMYINFO; + LCommandHandler.ParseParams := False; + + //RPL_BOUNCE (deprecated), RPL_ISUPPORT (new) + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '005'; {do not localize} + //LCommandHandler.OnCommand := CommandBOUNCE; // deprecated + LCommandHandler.OnCommand := CommandISUPPORT; + + { TODO: + 008 RPL_SNOMASK ircu Server notice mask (hex) + 009 RPL_STATMEMTOT ircu + 014 RPL_YOURCOOKIE Hybrid? + 042 RPL_YOURID IRCnet + 043 RPL_SAVENICK IRCnet : Sent to the client when their nickname was forced to change due to a collision + 050 RPL_ATTEMPTINGJUNC aircd + 051 RPL_ATTEMPTINGREROUTE aircd + 200 RPL_TRACELINK RFC1459 Link [.] [V ] See RFC + 201 RPL_TRACECONNECTING RFC1459 Try. See RFC + 202 RPL_TRACEHANDSHAKE RFC1459 H.S. See RFC + 203 RPL_TRACEUNKNOWN RFC1459 ???? [] See RFC + 204 RPL_TRACEOPERATOR RFC1459 Oper See RFC + 205 RPL_TRACEUSER RFC1459 User See RFC + 206 RPL_TRACESERVER RFC1459 Serv S C @ [V] See RFC + 207 RPL_TRACESERVICE RFC2812 Service See RFC + 208 RPL_TRACENEWTYPE RFC1459 0 See RFC + 209 RPL_TRACECLASS RFC2812 Class See RFC + 210 RPL_TRACERECONNECT RFC2812 + 210 RPL_STATS aircd Used instead of having multiple stats numerics + 211 RPL_STATSLINKINFO RFC1459 Reply to STATS (See RFC) + 212 RPL_STATSCOMMANDS RFC1459 [ ] Reply to STATS (See RFC) + 213 RPL_STATSCLINE RFC1459 C * Reply to STATS (See RFC) + 214 RPL_STATSNLINE RFC1459 N * Reply to STATS (See RFC), Also known as RPL_STATSOLDNLINE (ircu, Unreal) + 215 RPL_STATSILINE RFC1459 I * Reply to STATS (See RFC) + 216 RPL_STATSKLINE RFC1459 K * Reply to STATS (See RFC) + 217 RPL_STATSQLINE RFC1459 + 217 RPL_STATSPLINE ircu + 218 RPL_STATSYLINE + } + // RPL_BOUNCE (new) + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '010'; {do not localize} + LCommandHandler.OnCommand := CommandBOUNCE; + + //RPL_ENDOFSTATS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '219'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFSTATS; + + {TODO: + 221 RPL_UMODEIS RFC1459 [] Information about a user's own modes. Some daemons have extended the mode command and certain modes take parameters (like channel modes). + } + //RPL_SERVLIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '234'; {do not localize} + LCommandHandler.OnCommand := CommandSERVLIST; + + //RPL_SERVLISTEND + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '235'; {do not localize} + LCommandHandler.OnCommand := CommandSERVLISTEND; + + {TODO: + 236 RPL_STATSVERBOSE ircu Verbose server list? + 237 RPL_STATSENGINE ircu Engine name? + 239 RPL_STATSIAUTH IRCnet + 241 RPL_STATSLLINE RFC1459 L * Reply to STATS (See RFC) + 242 RPL_STATSUPTIME RFC1459 :Server Up days :: Reply to STATS (See RFC) + 243 RPL_STATSOLINE RFC1459 O * [:] Reply to STATS (See RFC); The info field is an extension found in some IRC daemons, which returns info such as an e-mail address or the name/job of an operator + 244 RPL_STATSHLINE RFC1459 H * Reply to STATS (See RFC) + 245 RPL_STATSSLINE Bahamut, IRCnet, Hybrid + 250 RPL_STATSCONN ircu, Unreal + 251 RPL_LUSERCLIENT RFC1459 :There are users and invisible on servers Reply to LUSERS command, other versions exist (eg. RFC2812); Text may vary. + 252 RPL_LUSEROP RFC1459 : Reply to LUSERS command - Number of IRC operators online + 253 RPL_LUSERUNKNOWN RFC1459 : Reply to LUSERS command - Number of unknown/unregistered connections + 254 RPL_LUSERCHANNELS RFC1459 : Reply to LUSERS command - Number of channels formed + 255 RPL_LUSERME RFC1459 :I have clients and servers Reply to LUSERS command - Information about local connections; Text may vary. + 256 RPL_ADMINME RFC1459 : Start of an RPL_ADMIN* reply. In practise, the server parameter is often never given, and instead the info field contains the text 'Administrative info about '. Newer daemons seem to follow the RFC and output the server's hostname in the 'server' parameter, but also output the server name in the text as per traditional daemons. + 257 RPL_ADMINLOC1 RFC1459 : Reply to ADMIN command (Location, first line) + 258 RPL_ADMINLOC2 RFC1459 : Reply to ADMIN command (Location, second line) + 259 RPL_ADMINEMAIL RFC1459 : Reply to ADMIN command (E-mail address of administrator) + 261 RPL_TRACELOG RFC1459 File See RFC + 263 RPL_TRYAGAIN RFC2812 : When a server drops a command without processing it, it MUST use this reply. Also known as RPL_LOAD_THROTTLED and RPL_LOAD2HI, I'm presuming they do the same thing. + 265 RPL_LOCALUSERS aircd, Hybrid, Hybrid, Bahamut Also known as RPL_CURRENT_LOCAL + 266 RPL_GLOBALUSERS aircd, Hybrid, Hybrid, Bahamut Also known as RPL_CURRENT_GLOBAL + 267 RPL_START_NETSTAT aircd + 268 RPL_NETSTAT aircd + 269 RPL_END_NETSTAT aircd + 270 RPL_PRIVS ircu + 271 RPL_SILELIST ircu + 272 RPL_ENDOFSILELIST ircu + 273 RPL_NOTIFY aircd + 276 RPL_VCHANEXIST + 277 RPL_VCHANLIST + 278 RPL_VCHANHELP + 280 RPL_GLIST ircu + 296 RPL_CHANINFO_KICKS aircd + 299 RPL_END_CHANINFO aircd + 300 RPL_NONE RFC1459 Dummy reply, supposedly only used for debugging/testing new features, however has appeared in production daemons. + } + //RPL_AWAY + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '301'; {do not localize} + LCommandHandler.OnCommand := CommandAWAY; + + //RPL_USERHOST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '302'; {do not localize} + LCommandHandler.OnCommand := CommandUSERHOST; + LCommandHandler.ParseParams := False; + + //RPL_ISON + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '303'; {do not localize} + LCommandHandler.OnCommand := CommandISON; + + //RPL_UNAWAY + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '305'; {do not localize} + LCommandHandler.OnCommand := CommandAWAY; + + //RPL_NOWAWAY + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '306'; {do not localize} + LCommandHandler.OnCommand := CommandAWAY; + + //RPL_WHOISUSER + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '311'; {do not localize} + LCommandHandler.OnCommand := CommandWHOIS; + + //RPL_WHOISSERVER + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '312'; {do not localize} + LCommandHandler.OnCommand := CommandWHOIS; + + //RPL_WHOISOPERATOR + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '313'; {do not localize} + LCommandHandler.OnCommand := CommandWHOIS; + + //RPL_WHOWASUSER + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '314'; + LCommandHandler.OnCommand := CommandWHOWAS; + + //RPL_ENDOFWHO + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '315'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFWHO; + + //RPL_WHOISIDLE + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '317'; {do not localize} + LCommandHandler.OnCommand := CommandWHOIS; + + //RPL_ENDOFWHOIS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '318'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFWHOIS; + + //RPL_WHOISCHANNELS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '319'; {do not localize} + LCommandHandler.OnCommand := CommandWHOIS; + + {TODO: + 320 RPL_WHOISVIRT AustHex + 320 RPL_WHOIS_HIDDEN Anothernet + 320 RPL_WHOISSPECIAL Unreal + } + //RPL_LISTSTART + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '321'; {do not localize} + LCommandHandler.OnCommand := CommandLISTSTART; + + //RPL_LIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '322'; {do not localize} + LCommandHandler.OnCommand := CommandLIST; + + //RPL_LISTEND + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '323'; {do not localize} + LCommandHandler.OnCommand := CommandLISTEND; + + //RPL_CHANMODEIS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '324'; {do not localize} + LCommandHandler.OnCommand := CommandCHANMODE; + + //RPL_UNIQOPIS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '325'; {do not localize} + //LCommandHandler.OnCommand := CommandUNIQOP; + + {TODO: + 326 RPL_NOCHANPASS + 327 RPL_CHPASSUNKNOWN + 328 RPL_CHANNEL_URL Bahamut, AustHex + 329 RPL_CREATIONTIME Bahamut + } + //RPL_NOTOPIC + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '331'; {do not localize} + LCommandHandler.OnCommand := CommandTOPIC; + + //RPL_TOPIC + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '332'; + LCommandHandler.OnCommand := CommandTOPIC; + + {TODO: + 333 RPL_TOPICWHOTIME ircu + 339 RPL_BADCHANPASS + 340 RPL_USERIP ircu + } + //RPL_INVITING + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '341'; {do not localize} + LCommandHandler.OnCommand := CommandINVITING; + + //RPL_SUMMONING + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '342'; {do not localize} + LCommandHandler.OnCommand := CommandSUMMONING; + + {TODO: + 345 RPL_INVITED GameSurge : has been invited by Sent to users on a channel when an INVITE command has been issued + } + //RPL_INVITELIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '346'; {do not localize} + LCommandHandler.OnCommand := CommandINVITELIST; + + //RPL_ENDOFINVITELIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '347'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFINVITELIST; + + //RPL_EXCEPTLIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '348'; {do not localize} + LCommandHandler.OnCommand := CommandEXCEPTLIST; + + //RPL_ENDOFEXCEPTLIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '349'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFEXCEPTLIST; + + //RPL_VERSION + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '351'; {do not localize} + LCommandHandler.OnCommand := CommandVERSION; + + //RPL_WHOREPLY + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '352'; {do not localize} + LCommandHandler.OnCommand := CommandWHOREPLY; + + //RPL_NAMEREPLY + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '353'; {do not localize} + LCommandHandler.OnCommand := CommandNAMEREPLY; + + { TODO: + 354 RPL_WHOSPCRPL ircu Reply to WHO, however it is a 'special' reply because it is returned using a non-standard (non-RFC1459) format. The format is dictated by the command given by the user, and can vary widely. When this is used, the WHO command was invoked in its 'extended' form, as announced by the 'WHOX' ISUPPORT tag. + 355 RPL_NAMREPLY_ QuakeNet ( '=' / '*' / '@' ) ' ' : [ '@' / '+' ] *( ' ' [ '@' / '+' ] ) Reply to the "NAMES -d" command - used to show invisible users (when the channel is set +D, QuakeNet relative). The proper define name for this numeric is unknown at this time Also see #353. + } + //RPL_LINKS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '364'; {do not localize} + LCommandHandler.OnCommand := CommandLINKS; + + //RPL_ENDOFLINKS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '365'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFLINKS; + + //RPL_ENDOFNAMES + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '366'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFNAMES; + + // RPL_BANLIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '367'; {do not localize} + LCommandHandler.OnCommand := CommandBANLIST; + + //RPL_ENDOFBANLIST + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '368'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFBANLIST; + + //RPL_ENDOFWHOWAS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '369'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFWHOWAS; + + //RPL_INFO + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '371'; {do not localize} + LCommandHandler.OnCommand := CommandINFO; + + //RPL_MOTD + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '372'; {do not localize} + LCommandHandler.OnCommand := CommandMOTD; + + //RPL_ENDOFINFO + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '374'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFINFO; + LCommandHandler.ParseParams := False; + + //RPL_MOTDSTART + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '375'; {do not localize} + LCommandHandler.OnCommand := CommandMOTD; + + //RPL_ENDOFMOTD + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '376'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFMOTD; + + //RPL_YOUREOPER + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '381'; {do not localize} + //LCommandHandler.OnCommand := CommandYOUAREOPER; + + //RPL_REHASHING + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '382'; {do not localize} + LCommandHandler.OnCommand := CommandREHASHING; + + //RPL_YOUARESERVICE + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '383'; {do not localize} + LCommandHandler.OnCommand := CommandSERVICE; + + {TODO: + 385 RPL_NOTOPERANYMORE AustHex, Hybrid, Unreal + 388 RPL_ALIST Unreal + 389 RPL_ENDOFALIST Unreal + } + //RPL_TIME + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '391'; {do not localize} + LCommandHandler.OnCommand := CommandTIME; + + //RPL_USERSSTART + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '392'; {do not localize} + LCommandHandler.OnCommand := CommandUSERSSTART; + + //RPL_USERS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '393'; {do not localize} + LCommandHandler.OnCommand := CommandUSERS; + + //RPL_ENDOFUSERS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '394'; {do not localize} + LCommandHandler.OnCommand := CommandENDOFUSERS; + + //RPL_NOUSERS + LCommandHandler := CommandHandlers.Add; + LCommandHandler.Command := '395'; {do not localize} + LCommandHandler.OnCommand := CommandUSERS; + + //ERR_NICKNAMEINUSE + LCommandHandler := CommandHandlers.Add; + // 433 ERR_NICKNAMEINUSE RFC1459 : + // Returned by the NICK command when the given nickname is already in use + LCommandHandler.Command := '433'; {do not localize} + LCommandHandler.OnCommand := CommandNICKINUSE; + + {TODO: + 396 RPL_HOSTHIDDEN Undernet Reply to a user when user mode +x (host masking) was set successfully + 400 ERR_UNKNOWNERROR [] : Sent when an error occured executing a command, but it is not specifically known why the command could not be executed. + 401 ERR_NOSUCHNICK RFC1459 : Used to indicate the nickname parameter supplied to a command is currently unused + 402 ERR_NOSUCHSERVER RFC1459 : Used to indicate the server name given currently doesn't exist + 403 ERR_NOSUCHCHANNEL RFC1459 : Used to indicate the given channel name is invalid, or does not exist + 404 ERR_CANNOTSENDTOCHAN RFC1459 : Sent to a user who does not have the rights to send a message to a channel + 405 ERR_TOOMANYCHANNELS RFC1459 : Sent to a user when they have joined the maximum number of allowed channels and they tried to join another channel + 406 ERR_WASNOSUCHNICK RFC1459 : Returned by WHOWAS to indicate there was no history information for a given nickname + 407 ERR_TOOMANYTARGETS RFC1459 : The given target(s) for a command are ambiguous in that they relate to too many targets + 408 ERR_NOSUCHSERVICE RFC2812 : Returned to a client which is attempting to send an SQUERY (or other message) to a service which does not exist + 409 ERR_NOORIGIN RFC1459 : PING or PONG message missing the originator parameter which is required since these commands must work without valid prefixes + 411 ERR_NORECIPIENT RFC1459 : Returned when no recipient is given with a command + 412 ERR_NOTEXTTOSEND RFC1459 : Returned when NOTICE/PRIVMSG is used with no message given + 413 ERR_NOTOPLEVEL RFC1459 : Used when a message is being sent to a mask without being limited to a top-level domain (i.e. * instead of *.au) + 414 ERR_WILDTOPLEVEL RFC1459 : Used when a message is being sent to a mask with a wild-card for a top level domain (i.e. *.*) + 415 ERR_BADMASK RFC2812 : Used when a message is being sent to a mask with an invalid syntax + 416 ERR_TOOMANYMATCHES IRCnet [] : Returned when too many matches have been found for a command and the output has been truncated. An example would be the WHO command, where by the mask '*' would match everyone on the network! Ouch! + 416 ERR_QUERYTOOLONG ircu Same as ERR_TOOMANYMATCHES + 419 ERR_LENGTHTRUNCATED aircd + 421 ERR_UNKNOWNCOMMAND RFC1459 : Returned when the given command is unknown to the server (or hidden because of lack of access rights) + 422 ERR_NOMOTD RFC1459 : Sent when there is no MOTD to send the client + 423 ERR_NOADMININFO RFC1459 : Returned by a server in response to an ADMIN request when no information is available. RFC1459 mentions this in the list of numerics. While it's not listed as a valid reply in section 4.3.7 ('Admin command'), it's confirmed to exist in the real world. + 424 ERR_FILEERROR RFC1459 : Generic error message used to report a failed file operation during the processing of a command + 425 ERR_NOOPERMOTD Unreal + 429 ERR_TOOMANYAWAY Bahamut + 430 ERR_EVENTNICKCHANGE AustHex Returned by NICK when the user is not allowed to change their nickname due to a channel event (channel mode +E) + 431 ERR_NONICKNAMEGIVEN RFC1459 : Returned when a nickname parameter expected for a command isn't found + 432 ERR_ERRONEUSNICKNAME RFC1459 : Returned after receiving a NICK message which contains a nickname which is considered invalid, such as it's reserved ('anonymous') or contains characters considered invalid for nicknames. This numeric is misspelt, but remains with this name for historical reasons :) + 436 ERR_NICKCOLLISION RFC1459 : Returned by a server to a client when it detects a nickname collision + 439 ERR_TARGETTOOFAST ircu Also known as many other things, RPL_INVTOOFAST, RPL_MSGTOOFAST etc + 440 ERR_SERVICESDOWN Bahamut, Unreal + 441 ERR_USERNOTINCHANNEL RFC1459 : Returned by the server to indicate that the target user of the command is not on the given channel + 442 ERR_NOTONCHANNEL RFC1459 : Returned by the server whenever a client tries to perform a channel effecting command for which the client is not a member + 443 ERR_USERONCHANNEL RFC1459 [:] Returned when a client tries to invite a user to a channel they're already on + 444 ERR_NOLOGIN RFC1459 : Returned by the SUMMON command if a given user was not logged in and could not be summoned + 445 ERR_SUMMONDISABLED RFC1459 : Returned by SUMMON when it has been disabled or not implemented + 446 ERR_USERSDISABLED RFC1459 : Returned by USERS when it has been disabled or not implemented + 447 ERR_NONICKCHANGE Unreal + 449 ERR_NOTIMPLEMENTED Undernet Unspecified Returned when a requested feature is not implemented (and cannot be completed) + 451 ERR_NOTREGISTERED RFC1459 : Returned by the server to indicate that the client must be registered before the server will allow it to be parsed in detail + 452 ERR_IDCOLLISION + 453 ERR_NICKLOST + 455 ERR_HOSTILENAME Unreal + 456 ERR_ACCEPTFULL + 457 ERR_ACCEPTEXIST + 458 ERR_ACCEPTNOT + 459 ERR_NOHIDING Unreal Not allowed to become an invisible operator? + 460 ERR_NOTFORHALFOPS Unreal + 461 ERR_NEEDMOREPARAMS RFC1459 : Returned by the server by any command which requires more parameters than the number of parameters given + 462 ERR_ALREADYREGISTERED RFC1459 : Returned by the server to any link which attempts to register again + 463 ERR_NOPERMFORHOST RFC1459 : Returned to a client which attempts to register with a server which has been configured to refuse connections from the client's host + 464 ERR_PASSWDMISMATCH RFC1459 : Returned by the PASS command to indicate the given password was required and was either not given or was incorrect + 465 ERR_YOUREBANNEDCREEP RFC1459 : Returned to a client after an attempt to register on a server configured to ban connections from that client + 466 ERR_YOUWILLBEBANNED RFC1459 Sent by a server to a user to inform that access to the server will soon be denied + 467 ERR_KEYSET RFC1459 : Returned when the channel key for a channel has already been set + 468 ERR_INVALIDUSERNAME ircu + 468 ERR_ONLYSERVERSCANCHANGE Bahamut, Unreal + 469 ERR_LINKSET Unreal + 470 ERR_LINKCHANNEL Unreal + 470 ERR_KICKEDFROMCHAN aircd + 471 ERR_CHANNELISFULL RFC1459 : Returned when attempting to join a channel which is set +l and is already full + 472 ERR_UNKNOWNMODE RFC1459 : Returned when a given mode is unknown + 473 ERR_INVITEONLYCHAN RFC1459 : Returned when attempting to join a channel which is invite only without an invitation + 474 ERR_BANNEDFROMCHAN RFC1459 : Returned when attempting to join a channel a user is banned from + 475 ERR_BADCHANNELKEY RFC1459 : Returned when attempting to join a key-locked channel either without a key or with the wrong key + 476 ERR_BADCHANMASK RFC2812 : The given channel mask was invalid + 478 ERR_BANLISTFULL RFC2812 : Returned when a channel access list (i.e. ban list etc) is full and cannot be added to + 479 ERR_BADCHANNAME Hybrid + 479 ERR_LINKFAIL Unreal + 481 ERR_NOPRIVILEGES RFC1459 : Returned by any command requiring special privileges (eg. IRC operator) to indicate the operation was unsuccessful + 482 ERR_CHANOPRIVSNEEDED RFC1459 : Returned by any command requiring special channel privileges (eg. channel operator) to indicate the operation was unsuccessful + 483 ERR_CANTKILLSERVER RFC1459 : Returned by KILL to anyone who tries to kill a server + 485 ERR_UNIQOPRIVSNEEDED RFC2812 : Any mode requiring 'channel creator' privileges returns this error if the client is attempting to use it while not a channel creator on the given channel + 488 ERR_TSLESSCHAN IRCnet + 491 ERR_NOOPERHOST RFC1459 : Returned by OPER to a client who cannot become an IRC operator because the server has been configured to disallow the client's host + 493 ERR_NOFEATURE ircu + 494 ERR_BADFEATURE ircu + 495 ERR_BADLOGTYPE ircu + 496 ERR_BADLOGSYS ircu + 497 ERR_BADLOGVALUE ircu + 498 ERR_ISOPERLCHAN ircu + 499 ERR_CHANOWNPRIVNEEDED Unreal Works just like ERR_CHANOPRIVSNEEDED except it indicates that owner status (+q) is needed. Also see #482. + 501 ERR_UMODEUNKNOWNFLAG RFC1459 : Returned by the server to indicate that a MODE message was sent with a nickname parameter and that the mode flag sent was not recognised + 502 ERR_USERSDONTMATCH RFC1459 : Error sent to any user trying to view or change the user mode for a user other than themselves + 503 ERR_GHOSTEDCLIENT Hybrid + 504 ERR_USERNOTONSERV + 511 ERR_SILELISTFULL ircu + 512 ERR_TOOMANYWATCH Bahamut Also known as ERR_NOTIFYFULL (aircd), I presume they are the same + 513 ERR_BADPING ircu Also known as ERR_NEEDPONG (Unreal/Ultimate) for use during registration, however it's not used in Unreal (and might not be used in Ultimate either). + 515 ERR_BADEXPIRE ircu + 516 ERR_DONTCHEAT ircu + 517 ERR_DISABLED ircu : + 522 ERR_WHOSYNTAX Bahamut + 523 ERR_WHOLIMEXCEED Bahamut + 525 ERR_REMOTEPFX CAPAB USERCMDPFX : Proposed. + 526 ERR_PFXUNROUTABLE CAPAB USERCMDPFX : Proposed. + 550 ERR_BADHOSTMASK QuakeNet + 551 ERR_HOSTUNAVAIL QuakeNet + 552 ERR_USINGSLINE QuakeNet + 600 RPL_LOGON Bahamut, Unreal + 601 RPL_LOGOFF Bahamut, Unreal + 602 RPL_WATCHOFF Bahamut, Unreal + 603 RPL_WATCHSTAT Bahamut, Unreal + 604 RPL_NOWON Bahamut, Unreal + 605 RPL_NOWOFF Bahamut, Unreal + 606 RPL_WATCHLIST Bahamut, Unreal + 607 RPL_ENDOFWATCHLIST Bahamut, Unreal + 608 RPL_WATCHCLEAR Ultimate + 611 RPL_ISLOCOP Ultimate + 612 RPL_ISNOTOPER Ultimate + 613 RPL_ENDOFISOPER Ultimate + 618 RPL_DCCLIST + 624 RPL_OMOTDSTART Ultimate + 625 RPL_OMOTD Ultimate + 626 RPL_ENDOFO Ultimate + 630 RPL_SETTINGS Ultimate + 631 RPL_ENDOFSETTINGS Ultimate + 660 RPL_TRACEROUTE_HOP KineIRCd [

[ | '*'] ] Returned from the TRACEROUTE IRC-Op command when tracerouting a host + 661 RPL_TRACEROUTE_START KineIRCd Start of an RPL_TRACEROUTE_HOP list + 662 RPL_MODECHANGEWARN KineIRCd ['+' | '-'] : Plain text warning to the user about turning on or off a user mode. If no '+' or '-' prefix is used for the mode char, '+' is presumed. + 663 RPL_CHANREDIR KineIRCd : Used to notify the client upon JOIN that they are joining a different channel than expected because the IRC Daemon has been set up to map the channel they attempted to join to the channel they eventually will join. + 664 RPL_SERVMODEIS KineIRCd .. Reply to MODE . KineIRCd supports server modes to simplify configuration of servers; Similar to RPL_CHANNELMODEIS + 665 RPL_OTHERUMODEIS KineIRCd Reply to MODE to return the user-modes of another user to help troubleshoot connections, etc. Similar to RPL_UMODEIS, however including the target + 666 RPL_ENDOF_GENERIC KineIRCd [ ...] : Generic response for new lists to save numerics. + 670 RPL_WHOWASDETAILS KineIRCd : Returned by WHOWAS to return extended information (if available). The type field is a number indication what kind of information. + 671 RPL_WHOISSECURE KineIRCd [:] Reply to WHOIS command - Returned if the target is connected securely, eg. type may be TLSv1, or SSLv2 etc. If the type is unknown, a '*' may be used. + 672 RPL_UNKNOWNMODES Ithildin : Returns a full list of modes that are unknown when a client issues a MODE command (rather than one numeric per mode) + 673 RPL_CANNOTSETMODES Ithildin : Returns a full list of modes that cannot be set when a client issues a MODE command + 678 RPL_LUSERSTAFF KineIRCd : Reply to LUSERS command - Number of network staff (or 'helpers') online (differs from Local/Global operators). Similar format to RPL_LUSEROP + 679 RPL_TIMEONSERVERIS KineIRCd [ | '0'] : Optionally sent upon connection, and/or sent as a reply to the TIME command. This returns the time on the server in a uniform manner. The seconds (and optionally nanoseconds) is the time since the UNIX Epoch, and is used since many existing timestamps in the IRC-2 protocol are done this way (i.e. ban lists). The timezone is hours and minutes each of Greenwich ('[+/-]HHMM'). Since all timestamps sent from the server are in a similar format, this numeric is designed to give clients the ability to provide accurate timestamps to their users. + 682 RPL_NETWORKS KineIRCd : A reply to the NETWORKS command when requesting a list of known networks (within the IIRC domain). + 687 RPL_YOURLANGUAGEIS KineIRCd : Reply to the LANGUAGE command, informing the client of the language(s) it has set + 688 RPL_LANGUAGE KineIRCd * : A language reply to LANGUAGE when requesting a list of known languages + 689 RPL_WHOISSTAFF KineIRCd : The user is a staff member. The information may explain the user's job role, or simply state that they are a part of the network staff. Staff members are not IRC operators, but rather people who have special access in association with network services. KineIRCd uses this numeric instead of the existing numerics due to the overwhelming number of conflicts. + 690 RPL_WHOISLANGUAGE KineIRCd Reply to WHOIS command - A list of languages someone can speak. The language codes are comma delimitered. + 702 RPL_MODLIST RatBox 0x Output from the MODLIST command + 703 RPL_ENDOFMODLIST RatBox : Terminates MODLIST output + 704 RPL_HELPSTART RatBox : Start of HELP command output + 705 RPL_HELPTXT RatBox : Output from HELP command + 706 RPL_ENDOFHELP RatBox : End of HELP command output + 708 RPL_ETRACEFULL RatBox : Output from 'extended' trace + 709 RPL_ETRACE RatBox : Output from 'extended' trace + 710 RPL_KNOCK RatBox !@ : Message delivered using KNOCK command + 711 RPL_KNOCKDLVR RatBox : Message returned from using KNOCK command + 712 ERR_TOOMANYKNOCK RatBox : Message returned when too many KNOCKs for a channel have been sent by a user + 713 ERR_CHANOPEN RatBox : Message returned from KNOCK when the channel can be freely joined by the user + 714 ERR_KNOCKONCHAN RatBox : Message returned from KNOCK when the user has used KNOCK on a channel they have already joined + 715 ERR_KNOCKDISABLED RatBox : Returned from KNOCK when the command has been disabled + 716 RPL_TARGUMODEG RatBox : Sent to indicate the given target is set +g (server-side ignore) + 717 RPL_TARGNOTIFY RatBox : Sent following a PRIVMSG/NOTICE to indicate the target has been notified of an attempt to talk to them while they are set +g + 718 RPL_UMODEGMSG RatBox @ : Sent to a user who is +g to inform them that someone has attempted to talk to them (via PRIVMSG/NOTICE), and that they will need to be accepted (via the ACCEPT command) before being able to talk to them + 720 RPL_OMOTDSTART RatBox : IRC Operator MOTD header, sent upon OPER command + 721 RPL_OMOTD RatBox : IRC Operator MOTD text (repeated, usually) + 722 RPL_ENDOFOMOTD RatBox : IRC operator MOTD footer + 723 ERR_NOPRIVS RatBox : Returned from an oper command when the IRC operator does not have the relevant operator privileges. + 724 RPL_TESTMARK RatBox !@ : Reply from an oper command reporting how many users match a given user@host mask + 725 RPL_TESTLINE RatBox : Reply from an oper command reporting relevant I/K lines that will match a given user@host + 726 RPL_NOTESTLINE RatBox : Reply from oper command reporting no I/K lines match the given user@host + 771 RPL_XINFO Ithildin Used to send 'eXtended info' to the client, a replacement for the STATS command to send a large variety of data and minimise numeric pollution. + 773 RPL_XINFOSTART Ithildin Start of an RPL_XINFO list + 774 RPL_XINFOEND Ithildin Termination of an RPL_XINFO list + 972 ERR_CANNOTDOCOMMAND Unreal Works similarly to all of KineIRCd's CANNOT* numerics. This one indicates that a command could not be performed for an arbitrary reason. For example, a halfop trying to kick an op. + 973 ERR_CANNOTCHANGEUMODE KineIRCd : Reply to MODE when a user cannot change a user mode + 974 ERR_CANNOTCHANGECHANMODE KineIRCd : Reply to MODE when a user cannot change a channel mode + 975 ERR_CANNOTCHANGESERVERMODE KineIRCd : Reply to MODE when a user cannot change a server mode + 976 ERR_CANNOTSENDTONICK KineIRCd : Returned from NOTICE, PRIVMSG or other commands to notify the user that they cannot send a message to a particular client. Similar to ERR_CANNOTSENDTOCHAN. KineIRCd uses this in conjunction with user-mode +R to allow users to block people who are not identified to services (spam avoidance) + 977 ERR_UNKNOWNSERVERMODE KineIRCd : Returned by MODE to inform the client they used an unknown server mode character. + 979 ERR_SERVERMODELOCK KineIRCd : Returned by MODE to inform the client the server has been set mode +L by an administrator to stop server modes being changed + 980 ERR_BADCHARENCODING KineIRCd : Returned by any command which may have had the given data modified because one or more glyphs were incorrectly encoded in the current charset (given). Such a use would be where an invalid UTF-8 sequence was given which may be considered insecure, or defines a character which is invalid within that context. For safety reasons, the invalid character is not returned to the client. + 981 ERR_TOOMANYLANGUAGES KineIRCd : Returned by the LANGUAGE command to tell the client they cannot set as many languages as they have requested. To assist the client, the maximum languages which can be set at one time is given, and the language settings are not changed. + 982 ERR_NOLANGUAGE KineIRCd : Returned by the LANGUAGE command to tell the client it has specified an unknown language code. + 983 ERR_TEXTTOOSHORT KineIRCd : Returned by any command requiring text (such as a message or a reason), which was not long enough to be considered valid. This was created initially to combat '/wallops foo' abuse, but is also used by DIE and RESTART commands to attempt to encourage meaningful reasons. + 999 ERR_NUMERIC_ERR Bahamut + } + + FCommandHandlers.OnBeforeCommandHandler := DoBeforeCmd; +end; + +{ Command handlers } + +procedure TIdIRC.DoBeforeCmd(ASender: TIdCommandHandlers; var AData: string; AContext: TIdContext); +var + LTmp: String; +begin + AData := IRCUnquote(AData); + // ":nickname!user@host" + if TextStartsWith(AData, ':') then begin + LTmp := Fetch(AData, ' '); + Delete(LTmp, 1, 1); // remove ':' + FSenderNick := Fetch(LTmp, '!'); + FSenderHost := LTmp; + end else begin + FSenderNick := ''; + FSenderHost := ''; + end; + if Assigned(FOnRaw) then begin + FOnRaw(AContext, True, AData); + end; +end; + +procedure TIdIRC.DoReplyUnknownCommand(AContext: TIdContext; ALine: string); +var + ACmdCode: Integer; +begin + ACmdCode := IndyStrToInt(Fetch(ALine, ' '), -1); + // + case ACmdCode of + 6, + 7: + begin + //MAP + end; + 5, + 400..424, + 437..502: + begin + if Assigned(FOnServerError) then begin + OnServerError(AContext, ACmdCode, ALine); + end; + end; + 431..432, + 436: + begin + if Assigned(FOnNickError) then begin + OnNicknameError(AContext, ACmdCode); + end; + end; + end; +end; + +procedure TIdIRC.CommandPRIVMSG(ASender: TIdCommand); +var + LTmp, LTarget, LData, LCTCP: String; + I: Integer; + CTCPList: TStringList; +begin + LTmp := ASender.UnparsedParams; + LTarget := FetchIRCParam(LTmp); + LData := FetchIRCParam(LTmp); + + CTCPList := TStringList.Create; + try + ExtractCTCPs(LData, CTCPList); + if CTCPList.Count = 0 then begin + if Assigned(FOnPrivMessage) then begin + OnPrivateMessage(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + end else + begin + if (LData <> '') and Assigned(FOnPrivMessage) then begin + OnPrivateMessage(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + for I := 0 to CTCPList.Count - 1 do begin + LData := CTCPList[I]; + LCTCP := Fetch(LData, ' '); + case PosInStrArray(LCTCP, IdIRCCTCP) of + 0: { ACTION } + begin + { + if Assigned(FOnAction) then begin + FOnAction(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + } + if Assigned(FOnCTCPQry) then begin + FOnCTCPQry(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + // RLebeau: CTCP ACTION does not send a reply back + //CTCPReply(FSenderNick, 'ERRMSG', LCTCP +' ' + LData + ' unknown query'); {do not localize} + end; + 1: { SOUND } + begin + { + if Assigned(FOnSound) then begin + FOnSound(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + } + if Assigned(FOnCTCPQry) then begin + FOnCTCPQry(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + CTCPReply(FSenderNick, 'ERRMSG', LCTCP +' ' + LData + ' unknown query'); {do not localize} + end; + 2: { PING } + begin + { + LTmp := ''; + if Assigned(FOnPing) then begin + FOnPing(ASender.Context, LTmp); + end; + if LTmp = '' then begin + LTmp := DateTimeToStr(Now); + end; + CTCPReply(FSenderNick, LCTCP, ':' + LTmp); + } + if Assigned(FOnCTCPQry) then begin + FOnCTCPQry(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + // AWinkelsdorf 3/10/2010 ToDo: CTCP Ping might need a CTIME result but + // many clients do not send the required CTIME with the Ping Query... + CTCPReply(FSenderNick, LCTCP, DateTimeToStr(Now)); {do not localize} + end; + 3: { FINGER } + begin + CTCPReply(FSenderNick, LCTCP, Replies.Finger); {do not localize} + end; + 4: { USERINFO } + begin + CTCPReply(FSenderNick, LCTCP, Replies.UserInfo); {do not localize} + end; + 5: { VERSION } + begin + CTCPReply(FSenderNick, LCTCP, Replies.Version); {do not localize} + end; + 6: { CLIENTINFO } + begin + // TODO: add OnClientInfoQuery event to handle per-command queries + CTCPReply(FSenderNick, LCTCP, Replies.ClientInfo); {do not localize} + end; + 7: { TIME } + begin + CTCPReply(FSenderNick, LCTCP, DateTimeToStr(Now)); + end; + 8: { ERROR } + begin + CTCPReply(FSenderNick, LCTCP, LData + ' No Error'); {do not localize} + end; + 9: { DCC } + begin + ParseDCC(ASender.Context, LData); + end; + 10: { SED } + begin + //ParseSED(AContext, LData); + if Assigned(FOnCTCPQry) then begin + FOnCTCPQry(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + CTCPReply(FSenderNick, LCTCP, LData + ' unknown query'); {do not localize} + end; + 11: { ERRMSG } + begin + CTCPReply(FSenderNick, LCTCP, LData + ' No Error'); {do not localize} + end; + else + begin + if Assigned(FOnCTCPQry) then begin + FOnCTCPQry(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + CTCPReply(FSenderNick, LCTCP, LData + ' unknown query'); {do not localize} + end; + end; + end; + end; + finally + CTCPList.Free; + end; +end; + +procedure TIdIRC.CommandNOTICE(ASender: TIdCommand); +var + LTmp, LTarget, LData, LCTCP: String; + I: Integer; + CTCPList: TStringList; +begin + LTmp := ASender.UnparsedParams; + LTarget := FetchIRCParam(LTmp); + LData := FetchIRCParam(LTmp); + + CTCPList := TStringList.Create; + try + ExtractCTCPs(LData, CTCPList); + if CTCPList.Count = 0 then begin + if Assigned(FOnNotice) then begin + OnNotice(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + end else + begin + if (LData <> '') and Assigned(FOnNotice) then begin + OnNotice(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + for I := 0 to CTCPList.Count - 1 do begin + LData := CTCPList[I]; + LCTCP := Fetch(LData, ' '); + case PosInStrArray(LCTCP, IdIRCCTCP) of + 0: { ACTION } + begin + { + if Assigned(FOnAction) then begin + FOnAction(ASender.Context, FSenderNick, FSenderHost, LTarget, LData); + end; + } + if Assigned(FOnCTCPRep) then begin + FOnCTCPRep(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + end; + 9: { DCC } + begin + ParseDCC(ASender.Context, LData); + end; + 10: { SED } + begin + //ParseSED(AContext, LData); + if Assigned(FOnCTCPRep) then begin + FOnCTCPRep(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + end; + else + if Assigned(FOnCTCPRep) then begin + FOnCTCPRep(ASender.Context, FSenderNick, FSenderHost, LTarget, LCTCP, LData); + end; + end; + end; + end; + finally + CTCPList.Free; + end; +end; + +procedure TIdIRC.CommandJOIN(ASender: TIdCommand); +begin + if Assigned(FOnJoin) then begin + OnJoin(ASender.Context, FSenderNick, FSenderHost, ASender.Params[0]); + end; +end; + +procedure TIdIRC.CommandPART(ASender: TIdCommand); +var + LChannel, LMsg: string; +begin + if Assigned(FOnPart) then begin + if ASender.Params.Count > 0 then begin + LChannel := ASender.Params[0]; + end; + if ASender.Params.Count > 1 then begin + LMsg := ASender.Params[1]; + end; + OnPart(ASender.Context, FSenderNick, FSenderHost, LChannel, LMsg); + end; +end; + +procedure TIdIRC.CommandKICK(ASender: TIdCommand); +var + LChannel, LTarget, LReason: string; +begin + if Assigned(FOnKick) then begin + if ASender.Params.Count > 0 then begin + LChannel := ASender.Params[0]; + end; + if ASender.Params.Count > 1 then begin + LTarget := ASender.Params[1]; + end; + if ASender.Params.Count > 2 then begin + LReason := ASender.Params[2]; + end; + OnKick(ASender.Context, FSenderNick, FSenderHost, LChannel, LTarget, LReason); + end; +end; + +procedure TIdIRC.CommandMODE(ASender: TIdCommand); +var + LTmp, LParam: String; +begin + LTmp := ASender.UnparsedParams; + LParam := FetchIRCParam(LTmp); + if IsChannel(LParam) then begin + if Assigned(FOnChanMode) then begin + OnChannelMode(ASender.Context, FSenderNick, FSenderHost, LParam, LTmp, ''); + end; + end + else if Assigned(FOnUserMode) then begin + OnUserMode(ASender.Context, FSenderNick, FSenderHost, LTmp); + end; +end; + +procedure TIdIRC.CommandNICK(ASender: TIdCommand); +begin + if Assigned(FOnNickChange) then begin + OnNicknameChange(ASender.Context, FSenderNick, FSenderHost, ASender.Params[0]); + end; +end; + +procedure TIdIRC.CommandQUIT(ASender: TIdCommand); +var + LReason: string; +begin + if Assigned(FOnQuit) then begin + if ASender.Params.Count > 0 then begin + LReason := ASender.Params[0]; + end; + OnQuit(ASender.Context, FSenderNick, FSenderHost, LReason); + end; +end; + +procedure TIdIRC.CommandSQUIT(ASender: TIdCommand); +var + LServer, LComment: string; +begin + if Assigned(FOnSvrQuit) then begin + if ASender.Params.Count > 0 then begin + LServer := ASender.Params[0]; + end; + if ASender.Params.Count > 1 then begin + LComment := ASender.Params[1]; + end; + OnServerQuit(ASender.Context, FSenderNick, FSenderHost, LServer, LComment); + end; +end; + +procedure TIdIRC.CommandINVITE(ASender: TIdCommand); +begin + if Assigned(FOnInvite) then begin + OnInvite(ASender.Context, FSenderNick, FSenderHost, ASender.Params[0], ASender.Params[1]); + end; +end; + +procedure TIdIRC.CommandKILL(ASender: TIdCommand); +var + LTarget, LReason: string; +begin + if Assigned(FOnKill) then begin + if ASender.Params.Count > 0 then begin + LTarget := ASender.Params[0]; + end; + if ASender.Params.Count > 1 then begin + LReason := ASender.Params[1]; + end; + OnKill(ASender.Context, FSenderNick, FSenderHost, LTarget, LReason); + end; +end; + +procedure TIdIRC.CommandPING(ASender: TIdCommand); +var + LServer1: String; +begin + if ASender.Params.Count > 0 then begin + LServer1 := ASender.Params[0]; + end; + Pong(LServer1); + if Assigned(FOnPingPong) then begin + OnPingPong(ASender.Context); + end; +end; + +procedure TIdIRC.CommandWALLOPS(ASender: TIdCommand); +var + LTmp: string; +begin + if Assigned(FOnWallops) then begin + LTmp := ASender.UnparsedParams; + OnWallops(ASender.Context, FSenderNick, FSenderHost, FetchIRCParam(LTmp)); + end; +end; + +procedure TIdIRC.CommandTOPIC(ASender: TIdCommand); +var + LChannel, LTopic: String; +begin + if Assigned(FOnTopic) then + begin + if ASender.Params.Count > 0 then begin + LChannel := ASender.Params[0]; + end; + if (ASender.CommandHandler.Command <> '331') and (ASender.Params.Count > 1) then begin {do not localize} + LTopic := ASender.Params[1]; + end else begin + LTopic := ''; + end; + OnTopic(ASender.Context, FSenderNick, FSenderHost, LChannel, LTopic); + end; +end; + +procedure TIdIRC.CommandWELCOME(ASender: TIdCommand); +var + LTmp: string; +begin + if Assigned(FOnSWelcome) then begin + LTmp := ASender.UnparsedParams; + OnServerWelcome(ASender.Context, FetchIRCParam(LTmp)); + end; +end; + +procedure TIdIRC.CommandERROR(ASender: TIdCommand); +var + LTmp: String; +begin + if Assigned(FOnServerError) then begin + LTmp := ASender.UnparsedParams; + OnServerError(ASender.Context, 0, FetchIRCParam(LTmp)); + end; +end; + +procedure TIdIRC.CommandYOURHOST(ASender: TIdCommand); +var + LTmp: String; +begin + if Assigned(FOnYourHost) then begin + LTmp := ASender.UnparsedParams; + OnYourHost(ASender.Context, FetchIRCParam(LTmp)); + end; +end; + +procedure TIdIRC.CommandCREATED(ASender: TIdCommand); +var + LTmp: string; +begin + if Assigned(FOnSCreated) then begin + LTmp := ASender.UnparsedParams; + OnServerCreated(ASender.Context, FetchIRCParam(LTmp)); + end; +end; + +procedure TIdIRC.CommandMYINFO(ASender: TIdCommand); +var + LTmp, LServer, LVersion, LUserModes, LChanModes: String; +begin + if Assigned(FOnMyInfo) then begin + LTmp := ASender.UnparsedParams; + LServer := FetchIRCParam(LTmp); + LVersion := FetchIRCParam(LTmp); + LUserModes := FetchIRCParam(LTmp); + LChanModes := FetchIRCParam(LTmp); + // TODO: + OnMyInfo(ASender.Context, LServer, LVersion, LUserModes, LChanModes, LTmp); + end; +end; + +procedure TIdIRC.DoBounce(ASender: TIdCommand; ALegacy: Boolean); +var + LHost, LPort, LInfo: string; +begin + if Assigned(FOnBounce) then begin + if ALegacy then begin + LInfo := ASender.Params[0]; + LHost := FetchIRCParam(LInfo); + LPort := FetchIRCParam(LInfo); + end else + begin + LHost := ASender.Params[0]; + LPort := ASender.Params[1]; + if ASender.Params.Count > 2 then begin + LInfo := ASender.Params[2]; + end; + end; + // TODO: reconnect automatically + OnBounce(ASender.Context, LHost, IndyStrToInt(LPort, 0), LInfo); + end; +end; + +procedure TIdIRC.CommandISUPPORT(ASender: TIdCommand); +var + LParams: TStringList; + I: Integer; +begin + if ASender.Params.Count = 1 then begin + DoBounce(ASender, True); // legacy, deprecated + Exit; + end; + if Assigned(FOnISupport) then + begin + LParams := TStringList.Create; + try + for I := 1 to ASender.Params.Count-1 do // skip nickname + begin + LParams.Add(ASender.Params[I]); + end; + OnISupport(ASender.Context, LParams); + finally + LParams.Free; + end; + end; +end; + +procedure TIdIRC.CommandBOUNCE(ASender: TIdCommand); +begin + DoBounce(ASender, False); +end; + +procedure TIdIRC.CommandAWAY(ASender: TIdCommand); +var + LCmd: Integer; +begin + LCmd := IndyStrToInt(ASender.CommandHandler.Command, 0); + case LCmd of + 301: + begin + if Assigned(FOnAway) then begin + OnAway(ASender.Context, FSenderNick, FSenderHost, ASender.Params[0], True); + end; + end; + 305, 306: + begin + FUserAway := (LCmd = 306); + if Assigned(FOnAway) then begin + OnAway(ASender.Context, GetUsedNickname, '', ASender.Params[0], FUserAway); + end; + end; + end; +end; + +procedure TIdIRC.CommandUSERHOST(ASender: TIdCommand); +begin + if Assigned(FOnUserInfo) then begin + OnUserInfoReceived(ASender.Context, ASender.UnparsedParams); + end; +end; + +procedure TIdIRC.CommandISON(ASender: TIdCommand); +begin + if Assigned(FOnIsOnIRC) then begin + OnIsOnIRC(ASender.Context, FSenderNick, FSenderHost); + end; +end; + +procedure TIdIRC.CommandWHOIS(ASender: TIdCommand); +begin + if not Assigned(FWhoIs) then begin + FWhoIs := TStringList.Create; + end; + FWhoIs.Add(ASender.Params[0]); +end; + +procedure TIdIRC.CommandENDOFWHOIS(ASender: TIdCommand); +begin + CommandWHOIS(ASender); + if Assigned(FOnWhoIs) then begin + OnWhoIs(ASender.Context, FWhoIs); + end; + FWhoIs.Clear; +end; + +procedure TIdIRC.CommandWHOWAS(ASender: TIdCommand); +begin + if not Assigned(FWhoWas) then begin + FWhoWas := TStringList.Create; + end; + FWhoWas.Add(ASender.Params[0]); +end; + +procedure TIdIRC.CommandENDOFWHOWAS(ASender: TIdCommand); +begin + CommandWHOWAS(ASender); + if Assigned(FOnWhoWas) then begin + OnWhoWas(ASender.Context, FWhoWas); + end; + FWhoWas.Clear; +end; + +procedure TIdIRC.CommandLISTSTART(ASender: TIdCommand); +begin + if not Assigned(FSvrList) then begin + FSvrList := TStringList.Create; + end else begin + FSvrList.Clear; + end; +end; + +procedure TIdIRC.CommandLIST(ASender: TIdCommand); +begin + if not Assigned(FSvrList) then begin + FSvrList := TStringList.Create; + end; + FSvrList.Add(ASender.Params[0] + ' ' + ASender.Params[1] + ' ' + ASender.Params[2]); {do not localize} +end; + +procedure TIdIRC.CommandLISTEND(ASender: TIdCommand); +begin + CommandLIST(ASender); + if Assigned(FOnSvrList) then begin + OnServerListReceived(ASender.Context, FSvrList); + end; + FSvrList.Clear; +end; + +procedure TIdIRC.CommandINVITING(ASender: TIdCommand); +begin + if Assigned(FOnInviting) then begin + OnInviting(ASender.Context, FSenderNick, FSenderHost); + end; +end; + +procedure TIdIRC.CommandSUMMONING(ASender: TIdCommand); +begin + if Assigned(FOnSummon) then begin + OnSummon(ASender.Context, FSenderNick, FSenderHost); + end; +end; + +procedure TIdIRC.CommandINVITELIST(ASender: TIdCommand); +begin + if not Assigned(FInvites) then begin + FInvites := TStringList.Create; + end; + // TODO: use a collection instead + FInvites.Add(ASender.Params[0] + ' ' + ASender.Params[1]); {do not localize} +end; + +procedure TIdIRC.CommandENDOFINVITELIST(ASender: TIdCommand); +begin + if not Assigned(FInvites) then begin + FInvites := TStringList.Create; + end; + FInvites.Add(ASender.Params[0]); + if Assigned(FOnINVList) then begin + OnInvitationListReceived(ASender.Context, FSenderNick, FInvites); + end; + FInvites.Clear; +end; + +procedure TIdIRC.CommandEXCEPTLIST(ASender: TIdCommand); +begin + if not Assigned(FExcepts) then begin + FExcepts := TStringList.Create; + end; + // TODO: use a collection instead + FExcepts.Add(ASender.Params[0] + ' ' + ASender.Params[1]); {do not localize} +end; + +procedure TIdIRC.CommandENDOFEXCEPTLIST(ASender: TIdCommand); +begin + if not Assigned(FExcepts) then begin + FExcepts := TStringList.Create; + end; + FExcepts.Add(ASender.Params[0]); + if Assigned(FOnEXCList) then begin + OnExceptionListReceived(ASender.Context, FSenderNick, FExcepts); + end; + FExcepts.Clear; +end; + +procedure TIdIRC.CommandWHOREPLY(ASender: TIdCommand); +begin + if not Assigned(FWho) then begin + FWho := TStringList.Create; + end; + FWho.Add(''); // TODO +end; + +procedure TIdIRC.CommandENDOFWHO(ASender: TIdCommand); +begin + if not Assigned(FWho) then begin + FWho := TStringList.Create; + end; + FWho.Add(ASender.Params[0]); + if Assigned(FOnWho) then begin + OnWho(ASender.Context, FWho); + end; + FWho.Clear; +end; + +procedure TIdIRC.CommandNAMEREPLY(ASender: TIdCommand); +var + i: Integer; + LNames: string; + LNameList: TStringList; +begin + if not Assigned(FNames) then begin + FNames := TStringList.Create; + end; + // AWinkelsdorf 3/10/2010 Rewrote logic to split Names into single Lines of FNames + if ASender.Params.Count >= 4 then begin // Names are in [3] + LNames := StringsReplace(ASender.Params[3], [' '], [',']); {do not localize} + LNameList := TStringList.Create; + try + LNameList.CommaText := LNames; + for i := 0 to LNameList.Count - 1 do + begin + if LNameList[i] <> '' then + FNames.Add(LNameList[i]); + end; + finally + LNameList.Free; + end; + end else begin + FNames.Add(ASender.Params[0]); + end; +end; + +procedure TIdIRC.CommandENDOFNAMES(ASender: TIdCommand); +var + LChannel: string; +begin + if not Assigned(FNames) then begin + FNames := TStringList.Create; + end; + LChannel := ''; + if ASender.Params.Count > 0 then begin + LChannel := ASender.Params[1]; + end; + if Assigned(FOnNickList) then begin + OnNicknamesListReceived(ASender.Context, LChannel, FNames); + end; + FNames.Clear; +end; + +procedure TIdIRC.CommandLINKS(ASender: TIdCommand); +var + LHopCnt, LInfo: String; +begin + if not Assigned(FLinks) then begin + FLinks := TStringList.Create; + end; + LInfo := ASender.Params[2]; + LHopCnt := Fetch(LInfo); + // TODO: use a collection instead + FLinks.Add(ASender.Params[0] + ' ' + ASender.Params[1] + ' ' + LHopCnt + ' ' + LInfo); {do not localize} +end; + +procedure TIdIRC.CommandENDOFLINKS(ASender: TIdCommand); +begin + if not Assigned(FLinks) then begin + FLinks := TStringList.Create; + end; + FLinks.Add(ASender.Params[0]); + if Assigned(FOnKnownSvrs) then begin + OnKnownServersListReceived(ASender.Context, FLinks); + end; + FLinks.Clear; +end; + +procedure TIdIRC.CommandBANLIST(ASender: TIdCommand); +begin + if not Assigned(FBans) then begin + FBans := TStringList.Create; + end; + // TODO: use a collection instead + FBans.Add(ASender.Params[0] + ' ' + ASender.Params[1]); {do not localize} +end; + +procedure TIdIRC.CommandENDOFBANLIST(ASender: TIdCommand); +begin + if not Assigned(FBans) then begin + FBans := TStringList.Create; + end; + FBans.Add(ASender.Params[0]); + if Assigned(FOnBanList) then begin + OnBanListReceived(ASender.Context, FSenderNick, FBans); + end; + FBans.Clear; +end; + +procedure TIdIRC.CommandINFO(ASender: TIdCommand); +begin + // TODO +end; + +procedure TIdIRC.CommandENDOFINFO(ASender: TIdCommand); +begin + if Assigned(FOnUserInfo) then begin + OnUserInfoReceived(ASender.Context, ASender.UnparsedParams); + end; +end; + +procedure TIdIRC.CommandMOTD(ASender: TIdCommand); +begin + if not Assigned(FMotd) then begin + FMotd := TStringList.Create; + end; + FMotd.Add(ASender.Params[0]); +end; + +procedure TIdIRC.CommandENDOFMOTD(ASender: TIdCommand); +begin + if not Assigned(FMotd) then begin + FMotd := TStringList.Create; + end; + if Assigned(FOnMOTD) then begin + OnMOTD(ASender.Context, FMotd); + end; + FMotd.Clear; +end; + +procedure TIdIRC.CommandREHASHING(ASender: TIdCommand); +begin + if Assigned(FOnRehash) then begin + OnRehash(ASender.Context, FSenderNick, FSenderHost); + end; +end; + +procedure TIdIRC.CommandUSERSSTART(ASender: TIdCommand); +begin + if not Assigned(FUsers) then begin + FUsers := TStringList.Create; + end else begin + FUsers.Clear; + end; +end; + +procedure TIdIRC.CommandUSERS(ASender: TIdCommand); +begin + if ASender.CommandHandler.Command = '393' then {do not localize} + begin + if not Assigned(FUsers) then begin + FUsers := TStringList.Create; + end; + // TODO: use a collection instead + FUsers.Add(ASender.Params[0] + ' ' + ASender.Params[1] + ' ' + ASender.Params[2]); {do not localize} + end; +end; + +procedure TIdIRC.CommandENDOFUSERS(ASender: TIdCommand); +begin + if not Assigned(FUsers) then begin + FUsers := TStringList.Create; + end; + if Assigned(FOnSvrUsers) then begin + OnServerUsersListReceived(ASender.Context, FUsers); + end; + FUsers.Clear; +end; + +procedure TIdIRC.CommandENDOFSTATS(ASender: TIdCommand); +begin + if Assigned(FOnSvrStats) then begin + OnServerStatsReceived(ASender.Context, nil); // TODO + end; +end; + +procedure TIdIRC.CommandSERVLIST(ASender: TIdCommand); +begin + // +end; + +procedure TIdIRC.CommandSERVLISTEND(ASender: TIdCommand); +begin + // : +end; + +procedure TIdIRC.CommandTIME(ASender: TIdCommand); +var + LServer, LTimeString: String; +begin + if Assigned(FOnSvrTime) then begin + LServer := ASender.Params[0]; + case ASender.Params.Count of + 2: begin // " : - + @@ -49,6 +51,9 @@ + + + @@ -88,7 +93,7 @@ - + diff --git a/restemplate.pas b/restemplate.pas index 2d648bd..d5c3b15 100644 --- a/restemplate.pas +++ b/restemplate.pas @@ -22,6 +22,9 @@ program restemplate; {$mode objfpc}{$H+} uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} URestemplateApp, UFilter, UCRTHelper, vinfo; {$R *.res}