Jump to content

baka0815

Members
  • Content Count

    42
  • Joined

  • Last visited

Posts posted by baka0815


  1. I'm using a TIdHTTPWebBrokerBridge and therefore assigning OnCommandGet (via TIdHTTPWebBrokerBridgeAccess) or OnCommandOther didn't do anything, those events never happen.

     

    What works for me is assigning OnConnect as in the link posted and to check if it's a TLS handshake and therefore setting PassThrough.

    In the OnAction-event of the WebModule-Actions I then check if I wanted to have a TLS connection and return a 400 statuscode with a message that encryption is required.

     

    Is that a feasible way or am I holding it the wrong way up?


  2. In my case it's configurable what the port should be and if SSL/TLS is active for that port.

    Because of that I want to redirect incoming non-TLS requests to the TLS variant or at least send an error message that TLS is required.

     

    I'll take a look at the post later and come back if I have additional questions. Thank you!


  3. That's exactly what I'm doing with the implementation of OnQuerySSLPort().

      if AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase then begin
        TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough :=
          not DoQuerySSLPort(AContext.Connection.Socket.Binding.Port);
      end;
      inherited DoConnect(AContext);
    
    

    This is the code where DoQuerySSLPort calls the event and sets it's Result to True, therefor setting PassThrough.

     

    When I then try to connect via http://... (as said earlier https:// works flawlessly!) the exception raised is

    error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number

    The log from Postman is just

    Error: socket hang up

     

    The reply from curl is

    *   Trying [fe80::64b3:3bf5:dfb6:3b7c]:8080...
    * Connected to server (fe80::64b3:3bf5:dfb6:3b7c) port 8080
    > GET / HTTP/1.1
    > Host: server:8080
    > User-Agent: curl/8.4.0
    > Accept: */*
    >
    * Empty reply from server
    * Closing connection
    curl: (52) Empty reply from server

    But what I would like to get is either a redirect to https or at least deliver some error message to the client to know what's wrong.


  4. At first: I searched the forum but couldn't find a topic matching my case, however my search-fu might be lacking, so please send me to the right place, if this was asked before. Thanks!

     

    I'm using a TIdHTTPWebBrokerBridge to serve incoming connections and that works flawlessly, with and without TLS (<= 1.2). However, if TLS is activated, I want to force the usage of TLS.

    What I'm currently doing is to implement OnQuerySSLPort(APort: TIdPort; var VUseSSL: Boolean) ans set VUseSLL to True, so SSL/TLS is forced and connections without TLS are not possible.

    But when I try to connect to the server without TLS (http://localhost:8080) I'm getting (via Bruno)

    Error invoking remote method 'send-http-request': TypeError: Cannot read properties of undefined (reading 'data')

    or "connection reset" via Firefox.

     

    When debugging I see that an exception is raised in procedure TIdSSLSocket.Accept(const pHandle: TIdStackSocketHandle);

    EIdOSSLAcceptError.RaiseException(fSSL, error, RSSSLAcceptError);

    How am I supposed to implement a redirect to the correct HTTPS-URL (or raise an error that TLS is required or something along those lines=?


  5. Quote

     I might consider doing that for a future version of the .cmd script.  That would probably require making a separate .txt file of all the known Indy units and then have the .cmd script loop through that file for every platform.

    Wouldn't that be something that could be automated in the CI of the Indy project (the creation of the cmd, without the txt file I mean)?


  6. 2 hours ago, dummzeuch said:

    Hm, yes that could be done. On the other hand, I could move the line number to the left column too, so it occupies the space under the file name which you left empty. 

    So, something like this?

    Filename.pas | if (True) then
    Line 1234    |   DoSomething()
                 | else
    -------------+---------------------------
    Filename.pas | // Comment
    Line 4567    | DoSomething();
                 |

     


  7. Thanks for the clarification, I'll check that!

     

    I opened a new ticket on sourceforge (wasn't sure if bug or feature, so I went for feature) to remove the global "Root: TGXFolder" variable: https://sourceforge.net/p/gexperts/feature-requests/170/

    That's the reason for the access violation in my previous tests as the tree of the first created menu (the main menu "configure" form) still holds references to now freed objects (thanks to the new created form for the plugin).

     

    And as you mentioned, there are 2 more places creating the form, which would also recreate the root-tree and therefore clearing previous references.


  8. Quote

    Someting else just occurred to me: What, if the welcome page plugin is not available? Some people might not want that page and remove the plugin. 

    So I tried that and it turned out that WelcomePagePluginService will always be nil and the timer will run until the IDE exits. 

    Which means that there should probably a counter or something similar that limits the number of calls to the timer event before giving up. 

    I don't understand your example, I'm afraid.

    The timer stops as soon as the plugin can be registered - not added to the welcome page. Or is the "WelcomePagePluginService" only available if the welcome page itself is active? So without WelcomePage there won't be the service?

     

    Quote

    Creating forms that will never be shown nonetheless take time an resources to create, it also opens the door for unintended side effects. That's why I don't like the way the expert (and now also the welcome page plugin) handle the creation of the form.

    The form is only created when the CreateView method is called. And that's only the case if the plugin is not only registered, but also added to the welcome page.

     

    To your list of bugs I would also add that ".OnSettingsChanged := HandleOnSettingsChanged;" is not always encapsulated in {$IFDEF GX_VER150_up} if I see that correctly:

    • Inside procedure TFavoriteFilesExpert.AfterIDEInitialized;
    • Inside function TFavoriteFilesExpert.TryGetRootFolder(out _Folder: TGXFolder): Boolean;

  9. Thanks for the feedback!

    Quote

    1. I don't like it that TFavoriteFilesExpert.RegisterWelcomePage is used as the OnTimer event handler as well as directly called from TFavoriteFilesExpert.AfterIDEInitialized. These should be two different methods and maybe a third that does the actual registration. Each of these methods would be much easier to understand than this one method.

    The logic is

    1. Check if we can register the welcome page plugin
      1. if not, then try again later (return to 1 in some seconds)
      2. if yes, register the plugin

    If I would create different methods, that would duplicate some code:

    procedure RegisterWelcomePage; // <-- entry point, check if WelcomePagePluginService is assigned, if yes call DoRegisterPlugin, if not, create timer
    
    procedure RegisterWelcomePageTimer(Sender: TObject); // Timer method which checks if WelcomePagePluginService is assigned if yes destroy timer and call DoRegisterPlugin, if not, do nothing (wait for next timer event)
    
    procedure DoRegisterPlugin; // perform the registration of the plugin

    Or RegisterWelcpomePageTimer(Sender: TObject) calls RegisterWelcomePage(), but then that method would need to check if the timer is already created, which is also a bit confusing.

    Or "RegisterWelcomePage" would always only create the timer and the timer would do the rest, as it's almost always the case that the plugin is not ready at that point. Then we wouldn't need the DoRegisterPlugin method.

     

    Quote

    2. I wasn't aware that there are 3 copies of the code that creates the TfmFavFiles form. You have now added a 4th one and then changed the parent of the treeview and listview on it. If you add some comments like this, I'm fine with that, even though it feels more than a bit hacky.

    What's the problem of another instance of the form? The comment is correct, I changed it to

        // This is a bit hacky!
        // We're creating the form of the favorite files here and change the parents
        // of the elements, that we need in our frame.
        // That way the logic of the form (events etc.) can be used and does not
        // need to be duplicated here.
    Quote

    3. Where does the source preview on the welcome page come from? Even if that gets created by the form, it does not get it's parent changed so it should not be visible on the frame. I don't think it makes much sense there either.

    I change the parent of the Panel pnlFiles, that hosts both elements, the listview and the fileview.

     

    Quote

    4. I'm wondering whether this could create a problem:

    This shouldn't create a problem, as changing the parent of one control removes this control from the current owner. From Vcl.Controls, procedure TControl.SetParent(AParent: TWinControl):

        if FParent <> nil then
          FParent.RemoveControl(Self);

     

    Quote

    5. If there is only one node in the tree view, that tree view does not make sense in the welcome page and should not be shown at all. 

    6. On the other hand, the tree view still has its popup menu, so it would be possible to add and remove nodes from it. (why doesn't the list view have it?) But what happens, if the user changes the configuration while the welcome page plugin exists and has its own copy of the form? I think those changes will get lost becaise the get overwritten by the form instance created by the welcome page plugin. But I am not sure, I haven't traced that code yet. 

    I tried that and it is working, however only for the current session.

    If you add a new file using the plugin, it's also directly visible in the main menu. But it's currently not saved to the configuration, so it's lost after a restart of the IDE. The plugin itself should be read-only and not allow any modifications to the favorites, I think. What's your opinion on this?

    Changing the view to only display while there are more than one entry would (in the current implementation) need the favorites form to have a separate mode of operation, or I would need to check if there are more than one entry in the treeview before changing the parent.

    Maybe I should reimplement the loading of the options logic instead of recreating the favorites form?

     

    Also using the configure-option currently raises an exception when the plugin is active:

    [01F027FC]{rtl280.bpl  } System.@UStrAsg (Line 26918, "System.pas" + 3) + $0
    [1FF1895B]{CnWizards_D110A.DLL} Unbekannte Funktion bei __dbk_fcall_wrapper + $66C43
    [785AC793]{vcl280.bpl  } Vcl.ComCtrls.TListItem.SetCaption (Line 17509, "Vcl.ComCtrls.pas" + 3) + $5
    [239FEE87]{GExpertsRS110.dll} GX_FavFiles.TfmFavFiles.FileToListItem (Line 2045, "GX_FavFiles.pas" + 1) + $9
    [239FA16C]{GExpertsRS110.dll} GX_FavFiles.TfmFavFiles.tvFoldersChange (Line 635, "GX_FavFiles.pas" + 15) + $9
    [7859E35A]{vcl280.bpl  } Vcl.ComCtrls.TTreeNode.GetSelected (Line 9297, "Vcl.ComCtrls.pas" + 0) + $2

    Edit: the reason for the exception seems to be the global root-node, which gets cleared and repopulated in TfmFavFiles.LoadEntries. After the clearing the references to the objects in the tree are no longer valid, the folders freed and therefore the exception.

    So there mustn't be multiple instances of the TfmFavFiles form! That should be noted somewhere!

    It looks like I need to replicate the loading logic of the elements in a different way.

     

    Quote

    7. You will also have to put an ifdef around the finalization code.

    Did that.

     

    However, when I change the code to only need the options (I don't need the expert in my current implementation) I could move the plugin logic to it's own unit. That would make it easier do en-/disable it for older Delphi versions.

    But I await your comments on 5 and 6 on that.


  10. @dummzeuchwhat I would also like to have input in is, if the way I change the parent is okay, or if I should rebuild the panels for the component (I would then just create a tree on the left and the files-panel on the right - or should I include the files in the tree?).

     

    I changed the above code you mentioned to the following, because I would like to have the clarity of disabling the timer as soon as possible (and not just implicitly by destroying it).

        if (Assigned(FPluginTimer)) then
        begin
          FPluginTimer.Enabled := False;
          FreeAndNil(FPluginTimer);
        end;

     


  11. Yes, I'm aware of the necessary IFDEFs, I should've mentioned them. The ToolsAPI.WelcomePage is only available in Delphi 11+.

     

    I would also love to remove the timer, however I don't know of a way to register the plugin at the correct time. Creating the plugin as a BPL seems to work, but I would like to have it included in the GExperts DLL and not include the same source in a separate BPL file which then needs to be installed separately.


  12. I'm currently experimenting with the ToolsAPI.WelcomePage API a bit and created a plugin for the GExperts favorites.

     

    image.thumb.png.282af78ecac6ea9ccf32cd5ef3c7f5c2.png

     

    My current work is a bit hacky, therefore there is currently no patch or something.

     

    I include the ToolsAPI.WelcomePage unit in the GX_FavFiles and have a separate type for the plugin there:

      TGXWelcomePagePlugin = class(TInterfacedObject, INTAWelcomePagePlugin, INTAWelcomePageContentPluginCreator)
      private
        FIconIndex: Integer;
        FView: TFrame;
        FFavoriteFilesExpert: TFavoriteFilesExpert;
      public
        constructor Create(const AFavoriteFilesExpert: TFavoriteFilesExpert);
        destructor Destroy; override;
    
        { INTAWelcomePagePlugin }
        function GetPluginID: string;
        function GetPluginName: string;
        function GetPluginVisible: Boolean;
    
        { INTAWelcomePageContentPluginCreator }
        function GetView: TFrame;
        function CreateView: TFrame;
        procedure DestroyView;
        function GetIcon: TGraphicArray;
        function GetIconIndex: Integer;
        procedure SetIconIndex(const Value: Integer);
      end;

    The AfterIDEInitialized procedure calls the creation of the plugin via the RegisterWelcomePage method. This does not work on first try, so there is a timer to catch the moment the WelcomePagePluginService is finally availabe.

    If anyone knows of a different way to register the plugin, please let me know! A dedicated, global "Register" procedure as used for BPL-Plugins didn't work.

    procedure TFavoriteFilesExpert.RegisterWelcomePage(Sender: TObject);
    begin
      // The WelcomePagePluginService might not be initialized the first time we get here,
      // so try again at a later time.
      if not Assigned(WelcomePagePluginService) then
      begin
        if (not Assigned(FPluginTimer)) then
        begin
          FPluginTimer := TTimer.Create(nil);
          FPluginTimer.OnTimer := RegisterWelcomePage;
          FPluginTimer.Interval := 2000;
        end;
    
        FPluginTimer.Enabled := True;
      end
      else
      begin
        FPluginTimer.Enabled := False;
    
        if (Assigned(FPluginTimer)) then
          FreeAndNil(FPluginTimer);
    
        WelcomePagePluginService.RegisterPluginCreator(TGXWelcomePagePlugin.Create(Self));
      end;
    end;

    The welcome page gets the favorites expert as a parameter to be able to access the options (it may be sufficient to just pass the options).

    Then in the CreateView method I create a TfmFavFiles form and "steal" the tvFolders and pnlFiles objects and change the parent to the plugin view.

    function TGXWelcomePagePlugin.CreateView: TFrame;
    var
      FavFiles: TfmFavFiles;
    begin
      if not Assigned(FView) then
      begin
        FView := WelcomePagePluginService.CreateCaptionFrame(sPluginID, sPluginName, nil);
    
        FavFiles := TfmFavFiles.Create(nil, FFavoriteFilesExpert.FOptions);
        FavFiles.tvFolders.Parent := FView;
        FavFiles.pnlFiles.Parent := FView;
      end;
      
      Result := FView;
    end;

    This works form me, but is not in a state that I would like to upstream the work, so any input is greatly appreciated!

    • Like 2

  13. If I click on either download, the filename corresponds to the version in the link description.

    I also downloaded some of the files and they are clearly different:

    image.thumb.png.ba0423b0b786bf2d35283a7efc983ced.png

     

    I don't know what is (or was?) your problem downloading the files. As it installed correctly merely the name of the download file was somehow changed, but the contents seems to be correct.


  14. Regarding

    IsoStringToDateTime

    the optimization/checks that ChatGPT introduced are not sufficient:

      if (d < 1) or (d > DaysInAMonth(y, m)) then
        raise EConvertError.Create('Invalid day of the month: ' + IntToStr(d));
      if (m < 1) or (m > 12) then
        raise EConvertError.Create('Invalid month of the year: ' + IntToStr(m));

    This checks the day using the month (m) which could have an invalid value (the check for valid months comes later). So it didn't take the ordering for the checks into account.

     
    • Like 2

  15. 1 hour ago, Lars Fosdal said:

    /off-topic Preferences of code.  I like begin/end for clarity.
    However, I also like unusual breaks before then and strange indentations of single statements.  If only I could convince the formatter to do my bidding.

    
    procedure MyProcedure(a, b: Boolean);
    begin
      if a
      then begin
        if b 
         then Beep
          else Beep
      end  
      else begin
        if b // Kommentar
         then Beep
          else Beep;
      end;
    end;

     

    I also like begin/end for readability, but that code style gives me the creeps.

     

    I would format it the following way (or even use additional begins and ends for the "Beeps").

    procedure MyProcedure(a, b: Boolean);
    begin
      if a then
      begin
        if b then 
          Beep
        else
          Beep;
      end  
      else
      begin
        // Kommentar
        if b then
          Beep
        else
          Beep;
      end;
    end;

     

×