Browse Source

--- Merging r33481 into '.':
U packages/rtl-console/fpmake.pp
U packages/rtl-extra/fpmake.pp
U packages/fcl-net/fpmake.pp
U packages/fcl-process/fpmake.pp
U packages/fcl-web/fpmake.pp
U packages/fppkg/fpmake.pp
U packages/ami-extra/fpmake.pp
U packages/googleapi/fpmake.pp
U packages/fv/fpmake.pp
--- Recording mergeinfo for merge of r33481 into '.':
U .
--- Merging r33735 into '.':
U packages/fcl-web/src/base/custhttpapp.pp
U packages/fcl-web/src/base/fphttpserver.pp
--- Recording mergeinfo for merge of r33735 into '.':
G .
--- Merging r33791 into '.':
U packages/fcl-web/src/base/fpoauth2.pp
--- Recording mergeinfo for merge of r33791 into '.':
G .
--- Merging r34061 into '.':
U packages/fcl-web/src/base/restbase.pp
--- Recording mergeinfo for merge of r34061 into '.':
G .
--- Merging r34063 into '.':
U packages/fcl-web/src/base/fphttpwebclient.pp
--- Recording mergeinfo for merge of r34063 into '.':
G .
--- Merging r34064 into '.':
U packages/fcl-web/src/base/fpwebclient.pp
--- Recording mergeinfo for merge of r34064 into '.':
G .

# revisions: 33481,33735,33791,34061,34063,34064

git-svn-id: branches/fixes_3_0@35974 -

marco 8 years ago
parent
commit
339cfc75fc

+ 2 - 0
packages/ami-extra/fpmake.pp

@@ -30,6 +30,8 @@ begin
     P.SourcePath.Add('src');
 
     P.OSes:=AllAmigaLikeOSes;
+    if Defaults.CPU=powerpc then
+      P.OSes:=P.OSes-[amiga];
 
     T:=P.Targets.AddUnit('cliputils.pas');
 

+ 4 - 0
packages/fcl-net/fpmake.pp

@@ -43,6 +43,8 @@ begin
     // IP and Sockets
     T:=P.Targets.AddUnit('netdb.pp',AllUnixOSes);
     T:=P.Targets.AddUnit('resolve.pp',AllUnixOSes+AllWindowsOSes+AllAmigaLikeOSes+[OS2,EMX]);
+      if Defaults.CPU=powerpc then
+        T.OSes:=T.OSes-[amiga];
       with T.Dependencies do
         begin
           AddInclude('resolve.inc');
@@ -50,6 +52,8 @@ begin
         end;
     T.ResourceStrings := True;
     T:=P.Targets.AddUnit('ssockets.pp',AllUnixOSes+AllWindowsOSes+AllAmigaLikeOSes+[OS2,EMX]);
+      if Defaults.CPU=powerpc then
+        T.OSes:=T.OSes-[amiga];
       with T.Dependencies do
         begin
           AddUnit('resolve');

+ 2 - 0
packages/fcl-process/fpmake.pp

@@ -26,6 +26,8 @@ begin
     P.Options.Add('-S2h');
     P.NeedLibC:= false;
     P.OSes:=AllOSes-[embedded,msdos];
+    if Defaults.CPU=powerpc then
+      P.OSes:=P.OSes-[amiga];
 
     P.SourcePath.Add('src');
     P.IncludePath.Add('src/unix',AllUnixOSes);

+ 2 - 0
packages/fcl-web/fpmake.pp

@@ -19,6 +19,8 @@ begin
 {$endif ALLPACKAGES}
     P.Version:='3.0.3';
     P.OSes := [beos,haiku,freebsd,darwin,iphonesim,solaris,netbsd,openbsd,linux,win32,win64,wince,aix,amiga,aros,morphos,dragonfly];
+    if Defaults.CPU=powerpc then
+      P.OSes:=P.OSes-[amiga];
     P.Dependencies.Add('fcl-base');
     P.Dependencies.Add('fcl-db');
     P.Dependencies.Add('fcl-xml');

+ 81 - 2
packages/fcl-web/src/base/custhttpapp.pp

@@ -37,6 +37,8 @@ Type
     Procedure InitResponse(AResponse : TFPHTTPConnectionResponse); override;
     Property WebHandler : TFPHTTPServerHandler Read FWebHandler;
     Property Active;
+    Property OnAcceptIdle;
+    Property AcceptIdleTimeout;
   end;
 
   { TFCgiHandler }
@@ -49,9 +51,13 @@ Type
     FServer: TEmbeddedHTTPServer;
     function GetAllowConnect: TConnectQuery;
     function GetAddress: string;
+    function GetIdle: TNotifyEvent;
+    function GetIDleTimeOut: Cardinal;
     function GetPort: Word;
     function GetQueueSize: Word;
     function GetThreaded: Boolean;
+    procedure SetIdle(AValue: TNotifyEvent);
+    procedure SetIDleTimeOut(AValue: Cardinal);
     procedure SetOnAllowConnect(const AValue: TConnectQuery);
     procedure SetAddress(const AValue: string);
     procedure SetPort(const AValue: Word);
@@ -86,13 +92,22 @@ Type
     Property OnRequestError : TRequestErrorHandler Read FOnRequestError Write FOnRequestError;
     // Should addresses be matched to hostnames ? (expensive)
     Property LookupHostNames : Boolean Read GetLookupHostNames Write SetLookupHostNames;
+    // Event handler called when going Idle while waiting for a connection
+    Property OnAcceptIdle : TNotifyEvent Read GetIdle Write SetIdle;
+    // If >0, when no new connection appeared after timeout, OnAcceptIdle is called.
+    Property AcceptIdleTimeout : Cardinal Read GetIDleTimeOut Write SetIDleTimeOut;
   end;
 
   { TCustomHTTPApplication }
 
   TCustomHTTPApplication = Class(TCustomWebApplication)
   private
+    procedure FakeConnect;
+    function GetIdle: TNotifyEvent;
+    function GetIDleTimeOut: Cardinal;
     function GetLookupHostNames : Boolean;
+    procedure SetIdle(AValue: TNotifyEvent);
+    procedure SetIDleTimeOut(AValue: Cardinal);
     Procedure SetLookupHostnames(Avalue : Boolean);
     function GetAllowConnect: TConnectQuery;
     function GetAddress: String;
@@ -108,6 +123,7 @@ Type
     function InitializeWebHandler: TWebHandler; override;
     Function HTTPHandler : TFPHTTPServerHandler;
   Public
+    procedure Terminate; override;
     Property Address : string Read GetAddress Write SetAddress;
     Property Port : Word Read GetPort Write SetPort Default 80;
     // Max connections on queue (for Listen call)
@@ -118,6 +134,10 @@ Type
     property Threaded : Boolean read GetThreaded Write SetThreaded;
     // Should addresses be matched to hostnames ? (expensive)
     Property LookupHostNames : Boolean Read GetLookupHostNames Write SetLookupHostNames;
+    // Event handler called when going Idle while waiting for a connection
+    Property OnAcceptIdle : TNotifyEvent Read GetIdle Write SetIdle;
+    // If >0, when no new connection appeared after timeout, OnAcceptIdle is called.
+    Property AcceptIdleTimeout : Cardinal Read GetIDleTimeOut Write SetIDleTimeOut;
   end;
 
 
@@ -143,13 +163,33 @@ uses
 
 { TCustomHTTPApplication }
 
+function TCustomHTTPApplication.GetIdle: TNotifyEvent;
+begin
+  Result:=HTTPHandler.OnAcceptIdle;
+end;
+
+function TCustomHTTPApplication.GetIDleTimeOut: Cardinal;
+begin
+  Result:=HTTPHandler.AcceptIdleTimeout;
+end;
+
 function TCustomHTTPApplication.GetLookupHostNames : Boolean;
 
 begin
   Result:=HTTPHandler.LookupHostNames;
 end;
 
-Procedure TCustomHTTPApplication.SetLookupHostnames(Avalue : Boolean);
+procedure TCustomHTTPApplication.SetIdle(AValue: TNotifyEvent);
+begin
+  HTTPHandler.OnAcceptIdle:=AValue;
+end;
+
+procedure TCustomHTTPApplication.SetIDleTimeOut(AValue: Cardinal);
+begin
+  HTTPHandler.AcceptIdleTimeOut:=AValue;
+end;
+
+procedure TCustomHTTPApplication.SetLookupHostnames(Avalue: Boolean);
 
 begin
   HTTPHandler.LookupHostNames:=AValue;
@@ -215,6 +255,25 @@ begin
   Result:=Webhandler as TFPHTTPServerHandler;
 end;
 
+procedure TCustomHTTPApplication.FakeConnect;
+
+begin
+  try
+    TInetSocket.Create('localhost',Self.Port).Free;
+  except
+    // Ignore errors this may raise.
+  end
+end;
+
+procedure TCustomHTTPApplication.Terminate;
+
+begin
+  inherited Terminate;
+  // We need to break the accept loop. Do a fake connect.
+  if Threaded And (AcceptIdleTimeout=0) then
+    FakeConnect;
+end;
+
 { TFPHTTPServerHandler }
 
 procedure TFPHTTPServerHandler.HandleRequestError(Sender: TObject; E: Exception
@@ -251,7 +310,7 @@ begin
   Result:=FServer.LookupHostNames;
 end;
 
-Procedure TFPHTTPServerHandler.SetLookupHostnames(Avalue : Boolean);
+procedure TFPHTTPServerHandler.SetLookupHostnames(Avalue: Boolean);
 
 begin
   FServer.LookupHostNames:=AValue;
@@ -267,6 +326,16 @@ begin
   Result:=FServer.Address;
 end;
 
+function TFPHTTPServerHandler.GetIdle: TNotifyEvent;
+begin
+  Result:=FServer.OnAcceptIdle;
+end;
+
+function TFPHTTPServerHandler.GetIDleTimeOut: Cardinal;
+begin
+  Result:=FServer.AcceptIdleTimeout;
+end;
+
 function TFPHTTPServerHandler.GetPort: Word;
 begin
   Result:=FServer.Port;
@@ -282,6 +351,16 @@ begin
   Result:=FServer.Threaded;
 end;
 
+procedure TFPHTTPServerHandler.SetIdle(AValue: TNotifyEvent);
+begin
+  FServer.OnAcceptIdle:=AValue;
+end;
+
+procedure TFPHTTPServerHandler.SetIDleTimeOut(AValue: Cardinal);
+begin
+  FServer.AcceptIdleTimeOut:=AValue;
+end;
+
 procedure TFPHTTPServerHandler.SetOnAllowConnect(const AValue: TConnectQuery);
 begin
   FServer.OnAllowConnect:=Avalue

+ 1 - 1
packages/fcl-web/src/base/fphttpserver.pp

@@ -658,7 +658,7 @@ end;
 
 procedure TFPCustomHttpServer.StopServerSocket;
 begin
-  FServer.StopAccepting(True);
+  FServer.StopAccepting(False);
 end;
 
 procedure TFPCustomHttpServer.SetActive(const AValue: Boolean);

+ 11 - 4
packages/fcl-web/src/base/fphttpwebclient.pp

@@ -113,9 +113,16 @@ end;
 { TFPHTTPWebClient }
 
 Function TFPHTTPWebClient.DoCreateRequest: TWebClientRequest;
+
+Var
+  C : TFPHTTPClient;
+
 begin
-  Result:=TFPHTTPRequest.Create(TFPHTTPClient.Create(Self));
-  Result.Headers.NameValueSeparator:=':';
+  C:=TFPHTTPClient.Create(Self);
+  C.RequestHeaders.NameValueSeparator:=':';
+  C.ResponseHeaders.NameValueSeparator:=':';
+//  C.HTTPversion:='1.0';
+  Result:=TFPHTTPRequest.Create(C);
 end;
 
 Function TFPHTTPWebClient.DoHTTPMethod(Const AMethod, AURL: String;
@@ -124,7 +131,7 @@ Function TFPHTTPWebClient.DoHTTPMethod(Const AMethod, AURL: String;
 Var
   U,S : String;
   h : TFPHTTPClient;
-  Res : Boolean;
+
 
 begin
   U:=AURL;
@@ -145,7 +152,7 @@ begin
       H.RequestBody:=ARequest.Content;
       H.RequestBody.Position:=0;
       end;
-    H.HTTPMethod(AMethod,U,Result.Content,[]); // Will rais an exception
+    H.HTTPMethod(AMethod,U,Result.Content,[]); // Will raise an exception
   except
     FreeAndNil(Result);
     Raise;

+ 89 - 38
packages/fcl-web/src/base/fpoauth2.pp

@@ -162,31 +162,35 @@ Type
 
   TOAuth2Handler = Class(TAbstractRequestSigner)
   private
-    FAutoStore: Boolean;
+    FAutoConfig: Boolean;
+    FAutoSession: Boolean;
+    FConfigLoaded: Boolean;
+    FSessionLoaded: Boolean;
     FClaimsClass: TClaimsClass;
     FConfig: TOAuth2Config;
-    FConfigLoaded: Boolean;
+    FSession: TOAuth2Session;
     FIDToken: TJWTIDToken;
+    FWebClient: TAbstractWebClient;
+    FStore : TAbstracTOAuth2ConfigStore;
     FOnAuthSessionChange: TOnAuthSessionChangeHandler;
     FOnIDTokenChange: TOnIDTokenChangeHandler;
-    FSession: TOAuth2Session;
+    FOnSignRequest: TOnAuthConfigChangeHandler;
     FOnAuthConfigChange: TOnAuthConfigChangeHandler;
-    FOnSignRequest: TOnAuthSessionChangeHandler;
     FOnUserConsent: TUserConsentHandler;
-    FSessionLoaded: Boolean;
-    FWebClient: TAbstractWebClient;
-    FStore : TAbstracTOAuth2ConfigStore;
+    Function GetAutoStore : Boolean;
+    Procedure SetAutoStore(AValue : Boolean); 
     procedure SetConfig(AValue: TOAuth2Config);
     procedure SetSession(AValue: TOAuth2Session);
     procedure SetStore(AValue: TAbstracTOAuth2ConfigStore);
   Protected
+    function CheckHostedDomain(URL: String): String; virtual;
     Function RefreshToken: Boolean; virtual;
     Function CreateOauth2Config : TOAuth2Config; virtual;
     Function CreateOauth2Session : TOAuth2Session; virtual;
     Function CreateIDToken : TJWTIDToken; virtual;
     Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure DoAuthConfigChange; virtual;
-    Procedure DoAuthSessionChange; virtual;
+    Procedure DoAuthSessionChange(Const AUser : String = ''); virtual;
     Procedure DoSignRequest(ARequest: TWebClientRequest); override;
     Property ConfigLoaded : Boolean Read FConfigLoaded;
     Property SessionLoaded : Boolean Read FSessionLoaded;
@@ -199,6 +203,8 @@ Type
     // Variable name for AuthScope in authentication URL.
     // Default = scope. Descendents can override this to provide correct behaviour.
     Class Function AuthScopeVariableName : String; virtual;
+    // Default for hosted domain, if any
+    Class function DefaultHostedDomain: String; virtual;
     // Check if config is authenticated.
     Function IsAuthenticated : Boolean; virtual;
     // Generate an authentication URL
@@ -207,11 +213,11 @@ Type
     // Do whatever is necessary to mark the request as 'authenticated'.
     Function Authenticate: TAuthenticateAction; virtual;
     // Load config from store
-    procedure LoadConfig;
+    procedure LoadConfig(Force : Boolean = false);
     // Save config to store
     procedure SaveConfig;
-    // Load Session from store.If AUser is empty, then ID Token.GetUniqueUser is used.
-    procedure LoadSession(Const AUser : String = '');
+    // Load Session from store.If AUser is empty, then ID Token.GetUniqueUser is used. 
+    procedure LoadSession(Const AUser : String = ''; AForce : Boolean = False);
     // Save session in store. If AUser is empty, then ID Token.GetUniqueUser is used. Will call OnAuthSessionChange
     procedure SaveSession(Const AUser : String = '');
     // Refresh ID token from Session.IDToken. Called after token is refreshed or session is loaded.
@@ -237,11 +243,15 @@ Type
     // Called when the IDToken information changes
     Property OnIDTokenChange : TOnIDTokenChangeHandler Read FOnIDTokenChange Write FOnIDTokenChange;
     // Called when a request is signed
-    Property OnSignRequest : TOnAuthSessionChangeHandler Read FOnSignRequest Write FOnSignRequest;
+    Property OnSignRequest : TOnAuthConfigChangeHandler Read FOnSignRequest Write FOnSignRequest;
     // User to load/store parts of the config store.
     Property Store : TAbstracTOAuth2ConfigStore Read FStore Write SetStore;
-    // Call storing automatically when needed.
-    Property AutoStore : Boolean Read FAutoStore Write FAutoStore;
+    // Call storing session/config automatically when needed.
+    Property AutoStore : Boolean Read GetAutoStore Write SetAutoStore;
+    // AutoSession = True makes sure the load/save of the session as needed.
+    Property AutoSession : Boolean Read FAutoSession Write FAutoSession default True;
+    // AutoConfig = True will enable the load of config as needed.
+    Property AutoConfig : Boolean Read FAutoConfig Write FAutoConfig default True;
   end;
   TOAuth2HandlerClass = Class of TOAuth2Handler;
 
@@ -347,13 +357,33 @@ begin
     end;
 end;
 
+function TOAuth2Handler.CheckHostedDomain(URL : String): String;
+
+Var
+  HD : String;
+
+begin
+  HD:=Config.HostedDomain;
+  if (HD='') then
+    Result:=DefaultHostedDomain;
+  Result:=StringReplace(URL,'%HostedDomain%',Config.HostedDomain,[rfIgnoreCase]);
+end;
+
+Class function TOAuth2Handler.DefaultHostedDomain : String;
+
+begin
+  Result:='';
+end;
+
 function TOAuth2Handler.AuthenticateURL: String;
+
 begin
   Result:=Config.AuthURL
         + '?'+ AuthScopeVariableName+'='+HTTPEncode(Config.AuthScope)
         +'&redirect_uri='+HTTPEncode(Config.RedirectUri)
         +'&client_id='+HTTPEncode(Config.ClientID)
         +'&response_type=code'; // Request refresh token.
+  Result:=CheckHostedDomain(Result);
   if Assigned(Session) then
     begin
     if (Session.LoginHint<>'') then
@@ -376,14 +406,15 @@ begin
   FSession.Assign(AValue);
 end;
 
-procedure TOAuth2Handler.LoadConfig;
+procedure TOAuth2Handler.LoadConfig(Force : Boolean = False);
 
 begin
-  if Assigned(Store) and not ConfigLoaded then
-    begin
-    Store.LoadConfig(Config);
-    FConfigLoaded:=True;
-    end;
+  if Assigned(Store) then
+    if Force or not ConfigLoaded then
+      begin
+      Store.LoadConfig(Config);
+      FConfigLoaded:=True;
+      end;
 end;
 
 procedure TOAuth2Handler.SaveConfig;
@@ -395,22 +426,23 @@ begin
     end;
 end;
 
-procedure TOAuth2Handler.LoadSession(const AUser: String);
+procedure TOAuth2Handler.LoadSession(const AUser: String; AForce : Boolean = False);
 
 Var
   U : String;
 
 begin
   if Assigned(Store) then
-    begin
-    U:=AUser;
-    If (U='') and Assigned(FIDToken) then
-      U:=FIDToken.GetUniqueUserID;
-    Store.LoadSession(Session,AUser);
-    FSessionLoaded:=True;
-    if (Session.IDToken<>'') then
-      RefreshIDToken;
-    end;
+    if AForce or Not SessionLoaded then
+      begin
+      U:=AUser;
+      If (U='') and Assigned(FIDToken) then
+        U:=FIDToken.GetUniqueUserID;
+      Store.LoadSession(Session,AUser);
+      FSessionLoaded:=True;
+      if (Session.IDToken<>'') then
+        RefreshIDToken;
+      end;
 end;
 
 procedure TOAuth2Handler.SaveSession(const AUser: String);
@@ -428,6 +460,19 @@ begin
     end;
 end;
 
+Function TOAuth2Handler.GetAutoStore : Boolean;
+
+begin
+  Result:=AutoSession and AutoConfig;
+end;
+
+Procedure TOAuth2Handler.SetAutoStore(AValue : Boolean); 
+
+begin
+  AutoSession:=True;
+  AutoConfig:=True;
+end;
+
 procedure TOAuth2Handler.RefreshIDToken;
 begin
   FreeAndNil(FIDToken);
@@ -449,14 +494,15 @@ Var
   Resp: TWebClientResponse;
 
 begin
-  LoadConfig;
+  if AutoConfig and not ConfigLoaded then
+    LoadConfig;
   Req:=Nil;
   Resp:=Nil;
   D:=Nil;
   try
     Req:=WebClient.CreateRequest;
     Req.Headers.Values['Content-Type']:='application/x-www-form-urlencoded';
-    url:=Config.TOKENURL;
+    url:=CheckHostedDomain(Config.TOKENURL);
     Body:='client_id='+HTTPEncode(Config.ClientID)+
           '&client_secret='+ HTTPEncode(Config.ClientSecret);
     if (Session.RefreshToken<>'') then
@@ -475,10 +521,11 @@ begin
     if Result then
       begin
       Session.LoadTokensFromJSONResponse(Resp.GetContentAsString);
-      If (Session.IDToken)<>'' then
+      If (Session.IDToken<>'') then
         begin
         RefreshIDToken;
-        DoAuthSessionChange;
+        if AutoSession then
+          DoAuthSessionChange(IDToken.GetUniqueUserName);
         end;
       end
     else
@@ -518,9 +565,10 @@ end;
 function TOAuth2Handler.IsAuthenticated: Boolean;
 
 begin
-  LoadConfig;
+  If AutoConfig then
+    LoadConfig;
   // See if we need to load the session
-  if (Session.RefreshToken='') then
+  if (Session.RefreshToken='') and AutoSession then
     LoadSession;
   Result:=(Session.AccessToken<>'');
   If Result then
@@ -553,11 +601,12 @@ begin
   SaveConfig;
 end;
 
-procedure TOAuth2Handler.DoAuthSessionChange;
+procedure TOAuth2Handler.DoAuthSessionChange(Const AUser : String = ''); 
+    
 begin
   If Assigned(FOnAuthSessionChange) then
     FOnAuthSessionChange(Self,Session);
-  SaveSession;
+  SaveSession(AUser);
 end;
 
 procedure TOAuth2Handler.DoSignRequest(ARequest: TWebClientRequest);
@@ -580,6 +629,8 @@ begin
   inherited Create(AOwner);
   FConfig:=CreateOauth2Config;
   FSession:=CreateOauth2Session;
+  FAutoSession:=True;
+  FAutoConfig:=True;
 end;
 
 destructor TOAuth2Handler.Destroy;

+ 82 - 8
packages/fcl-web/src/base/fpwebclient.pp

@@ -21,13 +21,21 @@ uses
   Classes, SysUtils;
 
 Type
+
   { TRequestResponse }
+  
+  // Some IIS servers react badly to svAny. So we set up a system where you can set a min/max SSL version.
+  
+  TSSLVersion = (svNone,svAny,svSSLv2,svSSLv3,svTLSv1,svTLSv11,svTLSv12,svTLSv13);
+  TSSLVersions = Set of TSSLVersion;
+  TSSLVersionArray = Array of TSSLVersion;
 
   TRequestResponse = Class(TObject)
   private
     FHeaders : TStrings;
     FStream : TStream;
     FOwnsStream : Boolean;
+    FSSLVersion : TSSLVersion;
   Protected
     function GetHeaders: TStrings;virtual;
     function GetStream: TStream;virtual;
@@ -39,6 +47,8 @@ Type
     Property Headers : TStrings Read GetHeaders;
     // Request content or response content
     Property Content: TStream Read GetStream;
+    // SSLVersion : Which version to use
+    Property SSLVersion : TSSLVersion Read FSSLVersion Write FSSLVersion;
   end;
 
   { TWebClientRequest }
@@ -95,9 +105,6 @@ Type
 
   { TAbstractWebClient }
 
-  TSSLVersion = (svAny,svSSLv2,svSSLv3,svTLSv1,svTLSv11,svTLSv12,svTLSv13);
-  TSSLVersions = Set of TSSLVersion;
-  TSSLVersionArray = Array of TSSLVersion;
 
   TAbstractWebClient = Class(TComponent)
   private
@@ -105,14 +112,19 @@ Type
     FSigner: TAbstractRequestSigner;
     FLogFile : String;
     FLogStream : TStream;
-    FTrySSLVersion: TSSLVersion;
+    FMinSSLVersion: TSSLVersion;
+    FMaxSSLVersion: TSSLVersion;
     Procedure LogRequest(AMethod, AURL: String; ARequest: TWebClientRequest);
     Procedure LogResponse(AResponse: TWebClientResponse);
     procedure SetLogFile(AValue: String);
+    procedure SetSSLVersion(AValue : TSSLVersion);
+    Function GetSSLVersion : TSSLVersion;
   protected
+    // Determine min/max version to try
+    procedure GetVersionLimits(out PMin, PMax: TSSLVersion);
     // Write a string to the log file
     procedure StringToStream(str: string);
-    // Must execute the requested method using request/response. Must take ResponseCOntent stream into account
+    // Must execute the requested method using request/response. Must take ResponseContent stream into account
     Function DoHTTPMethod(Const AMethod,AURL : String; ARequest : TWebClientRequest) : TWebClientResponse; virtual; abstract;
     // Must create a request.
     Function DoCreateRequest : TWebClientRequest; virtual; abstract;
@@ -130,7 +142,12 @@ Type
     Property RequestSigner : TAbstractRequestSigner Read FSigner Write FSigner;
     Property ResponseExaminer : TAbstractResponseExaminer Read FExaminer Write FExaminer;
     Property LogFile : String Read FLogFile Write SetLogFile;
-    property SSLVersion : TSSLVersion Read FTrySSLVersion Write FTrySSLVersion;
+    // This will set MinSSLversion and MaxSSLversion
+    property SSLVersion : TSSLVersion Read GetSSLVersion Write SetSSLVersion;
+    // Minimum Version to try. If spNone is set, all should be tried in succession from high to MinSSLVersion.
+    Property MinSSLVersion : TSSLVersion Read FMinSSLVersion Write FMinSSLVersion default svAny;
+    // Maximum Version to try. If spNone is set, all should be tried in succession from MaxSSLVersion to low.
+    Property MaxSSLVersion : TSSLVersion Read FMaxSSLVersion Write FMaxSSLVersion default svAny;
   end;
   TAbstractWebClientClass = Class of TAbstractWebClient;
 
@@ -211,6 +228,18 @@ end;
 
 { TAbstractWebClient }
 
+procedure TAbstractWebClient.SetSSLVersion(AValue : TSSLVersion);
+
+begin
+  MinSSLVersion:=AValue;
+  MaxSSLVersion:=AValue;
+end;
+
+Function TAbstractWebClient.GetSSLVersion : TSSLVersion;
+
+begin
+  Result:=MinSSLVersion;
+end;
 
 procedure TAbstractWebClient.SetLogFile(AValue: String);
 begin
@@ -220,7 +249,10 @@ begin
   FLogFile:=AValue;
   if (FLogFile<>'') then
     if FileExists(FLogFile) then
-      FLogStream:=TFileStream.Create(FLogFile,fmOpenWrite or fmShareDenyWrite)
+      begin
+      FLogStream:=TFileStream.Create(FLogFile,fmOpenWrite or fmShareDenyWrite);
+      FLogStream.Seek(0,soFromEnd);
+      end
     else
       FLogStream:=TFileStream.Create(FLogFile,fmCreate or fmShareDenyWrite);
 end;
@@ -277,19 +309,61 @@ begin
   StringToStream('');
 end;
 
+procedure TAbstractWebClient.GetVersionLimits(out PMin, PMax: TSSLVersion);
+
+begin
+  if MinSSLVersion=svNone then
+    PMin:=Succ(Low(TSSLVersion))
+  else
+    PMin:=MinSSLVersion;
+  if MaxSSLVersion=svNone then
+    PMax:=High(TSSLVersion)
+  else
+    PMax:=MaxSSLVersion;
+  if PMax<PMin then
+    PMax:=PMin;
+end;
+
 function TAbstractWebClient.ExecuteRequest(const AMethod, AURL: String;
   ARequest: TWebClientRequest): TWebClientResponse;
+  
+Var
+  P,PMax,PMin : TSSLVersion;
+  S: String;
+
 begin
   if Assigned(FLogStream) then
     LogRequest(AMethod,AURL,ARequest);
   Result:=DoHTTPMethod(AMethod,AURL,ARequest);
+  GetVersionLimits(PMin,PMax);
+  if PMin<>PMax then
+    StringToStream('Trying multiple protocols.');
+  P:=PMax;
+  While (Not Assigned(Result)) and (P>=PMin) do
+    begin
+    Str(P,S);
+    StringToStream('Trying protocol: '+S);
+    Result:=Nil;
+    ARequest.SSLVersion:=P;
+    if Assigned(FLogStream) then
+      LogRequest(AMethod,AURL,ARequest);
+    try
+      Result:=DoHTTPMethod(AMethod,AURL,ARequest);
+    except
+      if (P=PMin) then
+        Raise;
+    end;
+    P:=Pred(P);
+    end;
   if Assigned(Result) then
     begin
     if Assigned(FLogStream) then
       LogResponse(Result);
     If Assigned(FExaminer) then
       FExaminer.ExamineResponse(Result);
-    end;
+    end
+  else
+    StringToStream('Request generated no response');
 end;
 
 function TAbstractWebClient.ExecuteSignedRequest(const AMethod, AURL: String;

+ 8 - 5
packages/fcl-web/src/base/restbase.pp

@@ -113,7 +113,7 @@ Type
     Class Function GetParentPropCount : Integer; virtual;
     Class Function ExportPropertyName(Const AName : String) : string; virtual;
     Class Function CleanPropertyName(Const AName : String) : string;
-    Class Function CreateObject(Const AKind : String) : TBaseObject;
+    Class Function CreateObject(Const AKind : String; AClass: TClass = Nil) : TBaseObject;
     Class Procedure RegisterObject;
     Class Function ObjectRestKind : String; virtual;
     Procedure LoadPropertyFromJSON(Const AName : String; JSON : TJSONData); virtual;
@@ -689,15 +689,16 @@ begin
     Case ET^.Kind of
       tkClass :
         begin
-        // Writeln(ClassName,' Adding instance of type: ',AN);
-        TObjectArray(AP)[I]:=CreateObject(AN);
+        TObjectArray(AP)[I]:=CreateObject(AN,GetTypeData(ET)^.ClassType);
         TObjectArray(AP)[I].LoadFromJSON(AValue.Objects[i]);
         end;
       tkFloat :
         if IsDateTimeProp(ET) then
           TDateTimeArray(AP)[I]:=RFC3339ToDateTime(AValue.Strings[i])
         else
+          begin
           TFloatArray(AP)[I]:=AValue.Floats[i];
+          end;
       tkInt64 :
         TInt64Array(AP)[I]:=AValue.Int64s[i];
       tkBool :
@@ -713,7 +714,6 @@ begin
       tkAstring,
       tkLString :
         begin
-        // Writeln('Setting String ',i,': ',AValue.Strings[i]);
         TStringArray(AP)[I]:=AValue.Strings[i];
         end;
     else
@@ -792,6 +792,7 @@ begin
 {$else}
     DynArraySetLength(AP,P^.PropType,1,@i);
     I:=Length(TObjectArray(AP));
+//    Writeln('Array length : ',I);
     SetDynArrayProp(P,AP);
 {$endif}
     try
@@ -1222,13 +1223,15 @@ begin
    Result:='_'+Result
 end;
 
-class function TBaseObject.CreateObject(const AKind: String): TBaseObject;
+class function TBaseObject.CreateObject(const AKind: String; AClass: TClass = Nil): TBaseObject;
 
 Var
   C : TBaseObjectClass;
 
 begin
   C:=RESTFactory.GetObjectClass(AKind);
+  if (C=Nil) and Assigned(AClass) and AClass.InheritsFrom(TBaseObject) then
+   C:=TBaseObjectClass(AClass);
   if C<>Nil then
     Result:=C.Create
   else

+ 2 - 0
packages/fppkg/fpmake.pp

@@ -45,6 +45,8 @@ begin
     P.Description := 'Libraries to create fppkg package managers.';
     P.NeedLibC:= false;
     P.OSes := P.OSes - [embedded,nativent,msdos];
+    if Defaults.CPU = powerpc then
+      P.OSes := P.OSes - [amiga];
 
     P.SourcePath.Add('src');
     P.IncludePath.Add('src');

+ 2 - 0
packages/fv/fpmake.pp

@@ -21,6 +21,8 @@ begin
     P.License := 'LGPL with modification, ';
     P.HomepageURL := 'www.freepascal.org';
     P.OSes := [beos,haiku,freebsd,darwin,iphonesim,solaris,netbsd,openbsd,linux,win32,win64,os2,emx,netware,netwlibc,go32v2,aix,dragonfly]+AllAmigaLikeOSes;
+    if Defaults.CPU = powerpc then
+      P.OSes := P.OSes - [amiga];
     P.Email := '';
     P.Description := 'Free Vision, a portable Turbo Vision clone.';
     P.NeedLibC:= false;

+ 2 - 0
packages/googleapi/fpmake.pp

@@ -31,6 +31,8 @@ begin
     P.Description := 'Google API client libraries.';
     P.NeedLibC:= false;
     P.OSes := [beos,haiku,freebsd,darwin,iphonesim,solaris,netbsd,openbsd,linux,win32,win64,wince,aix,amiga,aros,dragonfly];
+    if Defaults.CPU = powerpc then
+      P.OSes := P.OSes - [amiga];
     P.Directory:=ADirectory;
     P.Version:='3.0.3';
     P.Dependencies.Add('fcl-base');

+ 2 - 0
packages/rtl-console/fpmake.pp

@@ -41,6 +41,8 @@ begin
     P.License := 'LGPL with modification, ';
     P.HomepageURL := 'www.freepascal.org';
     P.OSes:=Rtl_ConsoleOSes;
+    if Defaults.CPU=powerpc then
+      P.OSes:=P.OSes-[amiga];
     P.Email := '';
     P.Description := 'Rtl-console, console abstraction';
     P.NeedLibC:= false;

+ 2 - 0
packages/rtl-extra/fpmake.pp

@@ -106,6 +106,8 @@ begin
 
     T:=P.Targets.AddUnit('serial.pp',SerialOSes);
     T:=P.Targets.AddUnit('sockets.pp',SocketsOSes);
+    if Defaults.CPU=powerpc then
+      T.OSes:=T.OSes-[amiga];
     with T.Dependencies do
      begin
        addinclude('osdefs.inc',AllUnixOSes);