Jump to content

Remy Lebeau

Members
  • Content Count

    2337
  • Joined

  • Last visited

  • Days Won

    95

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Range check error.

    Make sure that Count is not 0, otherwise Str[1] will report a range error if Range Checking is enabled. I would use PAnsiChar(Str)^ instead to avoid that check. Also, you are not checking ReadFile() for failure. Try this instead: function Tcomportserver.ReadString(var Str : AnsiString; Count : Integer) : Integer; var Overlapped : TOverlapped; BytesRead : DWORD; begin Result := 0; if Count <= 0 then Exit; SetLength(Str, Count); FillChar(Overlapped, SizeOf(Overlapped), 0); Overlapped.hEvent := CreateEvent(nil, True, True, nil); if Overlapped.hEvent = 0 then raise EWriteError.Create('Unable to create overlapped event object: ' + LastErr); try if not ReadFile(ComHandle, PAnsiChar(Str)^, Count, BytesRead, @Overlapped) then begin if GetLastError() <> ERROR_IO_PENDING then raise EWriteError.Create('Unable to read from port: ' + LastErr); end; if not GetOverlappedResult(ComHandle, Overlapped, BytesRead, True) then raise EWriteError.Create('Unable to read from port: ' + LastErr); SetLength(Str, BytesRead); Result := BytesRead; finally CloseHandle(Overlapped.hEvent); end; end;
  2. Remy Lebeau

    Convert C# function to delphi

    When I base64-decode that example digest, the SHA256 hash bytes are: 6b f8 28 22 8d 57 50 2a fc d2 b9 ca 14 25 a9 ef 24 69 c1 52 f9 d0 4f 51 6a e9 ee 10 78 75 d5 73 When I SHA256-encode the example JSON, I get varying results depending on how line breaks, whitespace, etc are handled: 61 4d f9 fe dd 28 e7 02 82 04 3b d1 62 fd 52 e8 7d 7c 55 16 87 a9 92 c7 65 08 ed b0 23 82 81 15 75 ca 3d a4 9c d7 c5 d1 c0 85 a4 b9 70 f5 9d 27 0c 7d 34 bd ac a1 92 ef b2 02 2f 6d a1 00 57 15 e0 71 09 2d 7c e3 49 be d3 5e 88 f8 ca 1b 94 c7 1a 49 f8 17 47 a4 a6 16 3e 0f 0f fd e7 f9 34 30 ce ea 16 24 4c 32 66 d3 30 4d 9b 23 a0 b4 0d cc 4a 9f 13 35 13 52 a3 c8 50 9e d1 52 ca a2 dd 8a d6 ef 0e 0a f6 5e af fd 1c 87 4a b9 09 2a 0c 6b 2e cf df 08 6c 5a 79 a7 ad 3a bd 1b c2 73 c2 c9 89 dd 42 34 69 e6 ec 47 a7 bb 9d ae d8 8a 9b 68 ce f1 06 7a bc 50 27 fd ae df f6 89 f7 56 ad f8 No matter what I do to the JSON, I can't get the same result as the decoded digest. So, I think maybe the website's example is wrong to begin with?
  3. Remy Lebeau

    base64 encode/decode  TStringDynArray

    Simply assign the (Unicode)String directly to the AnsiString, the RTL will handle the conversion for you. But like David said, you really shouldn't be using `AnsiString`, at least outside of direct interop with ANSI-based APIs. Stick with (Unicode)String natively for all of your processing, converting to/from bytes only when the string data needs to leave/enter your code (socket I/O, streaming, etc).
  4. Remy Lebeau

    Image attachment in a html email??

    Yes. TIdAttachment is an abstract base class in Indy 10, it has no such constructor. You are looking for TIdAttachmentFile instead. I suspect the code you are trying to compile is actually meant for Indy 9, not 10. In Indy 9, TIdAttachment was a concrete class that only supported files. TIdAttachment was redesigned in Indy 10 to be a base class with new descendants introduced (TIdAttachmentFile, TIdAttachmentMemory, etc). Also, the structure of your email is not setup correctly for HTML, with images. The article Lajos linked to explains in detail how it should be setup. And Indy has a TIdMessageBuilderHtml class to help with this task, see https://www.indyproject.org/2008/01/16/new-html-message-builder-class/
  5. Remy Lebeau

    TIdFTP read time out in FinalizeDataOperation

    Hard to say for sure without seeing the complete FTP sessions, but if I had to guess, the command connection in TIdFTP is likely being dropped part-way through the transfer, which would explain why the final response is not being received, and also why sending a QUIT request after the transfer timeout is causing a RST packet. Are you performing these transfers through a router/proxy/firewall, by chance? If so, they tend to drop idle connections after a few minutes, and thus are likely dropping the command connection during a large transfer since the command connection is sitting idle while the transfer is in progress. TIdFTP has a NATKeepAlive property to help avoid that issue, try enabling and configuring it as needed. You should filter the logs to isolate just the FTP command connections before saving them. You don't need 500Mb to show a handful of packets.
  6. Remy Lebeau

    TIdFTP read time out in FinalizeDataOperation

    I don't know what to tell you, then. TIdFTP is not receiving a final STOR response from the server. Since your FTP connection is not encrypted, have you tried using a packet sniffer like Wireshark to verify the response is actually being sent on the wire?
  7. That depends on what the thread actually does. Worker threads can have UIs and process user input, too. But, being that this code is running in a service, that is not likely to happen. So, if the thread has no UI, no keyboard input processing, then certainly TranslateMessage() can be omitted. But it doesn't really hurt to leave it in, either. It would essentially just be a no-op.
  8. Remy Lebeau

    TIdFTP read time out in FinalizeDataOperation

    In your log, I don't see a final response being received from the server for the STOR command, or the QUIT command. That would explain the timeout. Are you able to perform a similar transfer using another FTP client, like Filezilla, and get a final response after the transfer is finished?
  9. Remy Lebeau

    Playstore Android 64 bit APK, is it possible

    Have you read the documentation yet? Deploying Your Final Android Application Submitting Your Android App to Google Play
  10. Your message loop is not dispatching any system messages it receives. For example, asynchronous socket messages (since ICS uses non-blocking sockets). You need to call DispatchMessage() for any messages that you do not process on your own, eg: while GetMessage(lMsg,0,0,0) do begin if lMsg.message = _UM_Discover+fBaseMessage then begin if lMsg.lParam = _UP_DISCOVER_PARAM_ServerSettings then begin fdhsDiscover.FindServer; end; end else begin TranslateMessage(lMsg); DispatchMessage(lMsg); end; end;
  11. Remy Lebeau

    UDP sending and receiving

    You don't need to Listen() on a UDP socket. Only Bind() it, and then ReceiveFrom() on it. Listen() is only used for TCP. You can't Bind() to a broadcast IP, only to a local IP.
  12. Remy Lebeau

    UDP sending and receiving

    Connect()'ing a UDP socket assigns the specified peer IP/port as a static association, so that subsequent sends always go only to that peer, and only packets from that peer can be received. It also enabled better error reporting. If a send fails with an ICMP error, such as host/port unreachable, a subsequent send/read can fail with an appropriate error code. On a Connect'ed socket, you can use Send() and Receive(), you are not restricted to SendTo() and ReceiveFrom().
  13. Remy Lebeau

    Using VMWare: what files remain on host?

    Yes, each IDE would use its own default output folders within the VM. Source code is relatively small, so size was not an issue. Besides, I typically used fixed-sized VMs anyway. But no, storing the sources on the host did not affect the VM size. No. For me, being able to compile the same sources in multiple IDEs was important. If I changed something, I didn't want to have to copy the changes to multiple VMs to test it, just change it in one place only and then recompile as needed.
  14. Remy Lebeau

    Using VMWare: what files remain on host?

    In my case, I used to have one IDE installed on the host itself, and then a bunch of other IDEs installed in VMs, so I kept my source files on the host and used shared folders for the VMs to access the files. Worked fine.
  15. Remy Lebeau

    Bulk change of Manifest settings??

    I would probably just disable the default application manifest altogether in all of the projects, and point them all to an external manifest that they can share. Then changes are needed in only one place.
  16. Remy Lebeau

    C++Builder Journal website is down

    For anyone who still visits the BCBJ website/forum, it appears to be down the past couple of days. The journal itself shutdown in 2016. The domains are not set to expire yet. I wonder if this is just a technical outage, or if this is BCBJ's finale? I have tried contacting the owner, but my emails are coming back as undeliverable.
  17. Remy Lebeau

    Multiple instances of C++Builder IDE

    One reason this is allowed is to allow debugging IDE plugins/packages at runtime. You can have a plugin/package binary loaded in one instance of the IDE while another instance of the IDE has the plugin/package's project loaded and is debugging the plugin/package while it is running in the 1st instance.
  18. Remy Lebeau

    TIDHTTPServer not receiving POST

    When the OnCreateSession event is called, the VHTTPSession parameter is initially nil. Users of the OnCreateSession event are expected to CREATE a new TIdHTTPSession object (hence the name of the event!) and assign it to the parameter before exiting (that is why the parameter is a 'var'), if they want to, ie when using a TIdHTTPSession-derived class to track custom data. After the event exits, if the parameter is still nil then TIdHTTPServer calls LSessionList.CreateUniqueSession() to create a default session object. Since you are accessing an invalid object, you should be getting an Access Violation exception that aborts the current request, sends a 500 response to the client, and calls the OnCommandError event. Sorry, I missed that when you posted your code earlier.
  19. Remy Lebeau

    TIDHTTPServer not receiving POST

    On a side note: Indy has logging components, such as TIdLogFile, that you can attach to each connection's IOHandler.Intercept property, such as in the server's OnConnect event. I would suggest doing that instead of rolling your own logging code. If, for no other reason, than because you are opening and closing the log file on every message logged, which is highly inefficient. There is no possible way that HeadersCanContinue() and CreatePostStream() would not be called for every request. So I have to assume that the changes you made to Indy's source code did not take effect, ie you did not recompile Indy, and then recompile your app with the updated library. If that is not the case, then something else is going on that I can't see. Obviously, OnCommandOther would not be called for a GET request, OnCommandGet would be called instead. At this point, your debugging efforts are not really helping. And I don't have a working environment that I can test with. So, we are at a stalemate. Can you, at least, provide a complete log or Wireshark capture of the entire HTTP conservsation that is not working for you? Also, are you using plain HTTP or secure HTTPS? You said originally that the problem did not start happening until you made a modification to your web server - what was that modification exactly?
  20. Remy Lebeau

    Binary data in String?

    That is the UTF-8 (not ANSI) encoded form of Unicode codepoint U+0081, which is a non-visual control character.
  21. Remy Lebeau

    Binary data in String?

    https://en.wikipedia.org/wiki/ISO/IEC_8859-1 there are 65 undefined characters
  22. Remy Lebeau

    Binary data in String?

    Many ANSI charsets DON'T have all 256 bytes mapped.
  23. Remy Lebeau

    TIDHTTPServer not receiving POST

    Then, the ONLY possible way that the OnCommand... event would not be called in between the OnParseAuthentication event and a subsequent OnHeadersAvailable event, as your earlier log suggested, is if TIdHTTPServer.SessionState is true and an OnInvalidSession event handler sets its VContinueProcessing parameter to False. Otherwise, what you describe is physically impossible given the following code from TIdCustomHTTPServer.DoExecute(): if not HeadersCanContinue then begin // <-- OnHeadersAvailable called here Break; end; ... if not PreparePostStream then begin // <-- OnCreatePostStream called here Break; end; ... ReadCookiesFromRequestHeader; ... 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); // <-- OnParseAuthentication called here if not LRequestInfo.FAuthExists then begin raise EIdHTTPUnsupportedAuthorisationScheme.Create( RSHTTPUnsupportedAuthorisationScheme); end; end; // Session management GetSessionFromCookie(AContext, LRequestInfo, LResponseInfo, LContinueProcessing); // <-- OnCreateSession and OnInvalidSession called here if LContinueProcessing then begin // These essentially all "retrieve" so they are all "Get"s if LRequestInfo.CommandType in [hcGET, hcPOST, hcHEAD] then begin DoCommandGet(AContext, LRequestInfo, LResponseInfo); // <-- OnCommandGet called here end else begin DoCommandOther(AContext, LRequestInfo, LResponseInfo); // <-- OnCommandOther called here end; end; As you can see, the calls to DoCommand...() are skipped ONLY if GetSessionFromCookie() sets LContinueProcessing to False: 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); // <-- OnInvalidSession called here 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; There there is no possible way you can be receiving multiple HTTP requests on the same TCP connection, as TIdHTTPServer will close the current TCP connection after sending a response, Even if the client requests a keep-alive via the 'Connection' header, it will be ignored when TIdHTTPServer.KeepAlive is False.
  24. Remy Lebeau

    Binary data in String?

    Yes, quite lucky. Most ANSI locales use 1 byte per character, and UTF-16 uses 1 codeunit per character for most Western languages. So, you usually end up with 1 byte -> 2 bytes -> 1 byte conversion, hence why the final size was the same byte size, but may or may not be the same bytes as the original. There is more involved than just nul-padding, which typically only applies for bytes in the $00..$7F (ASCII) range. For non-ASCII characters, it is not a matter of simply padding '<HH>' to '<HH>#0', there is an actual mapping process involved. For example, if Windows-1252 were the locale used for the conversion, and byte $80 (Euro) were encountered, it would be converted to the Unicode character U+20AC, which is bytes $AC $20 in UTF-16LE, not $80 $00 like you are thinking. But yes, the individual bytes of the EXE data would mostly get doubled when converted to Unicode, and then truncated to single bytes when converted back to ANSI. But that does not necessarily mean that you will end up with the same bytes that you started with. For example, using Windows-1252 again, byte $81 (amongst a few others) would end up converted to either Unicode character U+FFFD (Replacement Character) or U+003F (ASCII '?') depending on the converter's implementation, which would thus be bytes $FD $FF or $3F $00 in UTF-16LE respectively, and then converted back to ANSI as byte $3F, which is clearly not the same as the original. If you absolutely need a charset that ensures no data loss when round-tripping bytes from ANSI to Unicode to ANSI, you can use codepage 437 for that, see Is there a code page that matches ASCII and can round trip arbitrary bytes through Unicode? The Unicode won't have the same character values as the original bytes in the ranges of $00..$1F and $7F..$FF, but the result of converting the Unicode back to codepage 437 will produce the original bytes. Nul-padding is not guaranteed, but yes, the String data inside the stream can get messed up. Do not use a TStringStream for binary data. Use TMemoryStream or TBytesStream instead.
  25. Remy Lebeau

    TIDHTTPServer not receiving POST

    This time, you have an extra "xxxxxxxxxxxxxxxxxxxx" between the Authorization and Content-Length headers. In that log, I see a 28-second gap between OnParseAuthentication and OnHeadersAvailable events. Those would have to be for separate requests, but are they on the same TCP connection (HTTP keep-alive)? You should not be able to get a new OnHeadersAvailable event without a preceding OnCommand... event if they are the same TCP connection. It would still be useful to see your ACTUAL code. That would not make sense for OnCommand..., unless your code is trying to validate the credentials and terminate the TCP connection before the OnCommand... event is called. The OnParseAuthentication event should only be used for PARSING credentials out of the request headers, not for VALIDATING the credentials. Any validation should only be done in the OnCommand... events. For the OnCreateSession event, do you have TIdHTTPServer.SessionState set to True or False? It is set to False by default. The only thing that TIdHTTPServer does in between calling the OnParseAuthentication and OnCommand..., events, but only if SessionState=True, is to extract session cookies from the request, compare them to the contents of TIdHTTPServer.SessionList calling the OnInvalidSession event for any cookies that are not in the list, and then calling the OnCreateSession event if a new session needs to be made. So, if SessionState=False, there is no possible way that OnCommand... would not be called after OnParseAuthentication exits. But if SessionState=True, the only way OnCommand... would not be called is if cookie/session handling is hanging/crashing.
×