-
Content Count
3001 -
Joined
-
Last visited
-
Days Won
135
Everything posted by Remy Lebeau
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
https://en.wikipedia.org/wiki/ISO/IEC_8859-1 there are 65 undefined characters -
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
Many ANSI charsets DON'T have all 256 bytes mapped. -
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.
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
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. -
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.
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
Most likely, that code predated the shift to Unicode in Delphi 2009. No, it doesn't. It has the potential to corrupt the data. This is exactly why you SHOULD NOT put binary data into a UnicodeString. Doesn't work. It fills only 1/2 of the UnicodeString's memory with the non-textual binary (because SizeOf(WideChar) is 2, so the SetLength() is allocating twice the number of bytes as were read in), then converts the entire UnicodeString (including the unused bytes) from UTF-16 to ANSI producing complete garbage, and then writes that garbage as-is to file. So yes, the same number of bytes MAY be written as were read in (but that is not guaranteed), but those bytes are useless. That code is copying the binary as-is into an AnsiString of equal byte size, converting that AnsiString to a UTF-16 UnicodeString using the user's default locale, then converting that UnicodeString from UTF-16 back to ANSI using the same locale. Depending on the particular locale used, that MAY be a lossy conversion, you MIGHT end up with the same bytes that you started with, or you MIGHT NOT. This has nothing to do with pointers. You are simply performing 2 separate data conversions (binary/ANSI -> UTF-16 -> binary/ANSI ) that just HAPPEN to produce the same results as the input IN YOUR ENVIRONMENT. -
Then there is no reason why TIdHTTPServer should not be calling the events. So, you are just going to have to debug into the server's code to find out where it is hanging. Are the OnHeadersAvailable, OnCreatePostStream, OnParseAuthentication, and OnCreateSession events being called? In the log you showed, there is an extra > before the Content-Length header. Is that really present in the request, or is that just a typo when posting the log here? Then I strongly suggest you add them.
-
Clearly the server IS receiving the POST request, you can see it in your log file. But if the OnCommand... events are not being called, it usually means the request is incomplete, ie if TIdHTTPServer is waiting for more data that is not received. Note the log also shows the TCP connection being disconnected at the same time the request is received. So, the client is likely dropping the connection on its end before sending the full 1523 bytes that it claimed to be sending. BTW, 10.6.1 is not the latest version of Indy, the current version is 10.6.2.
-
virtualization VMWare Workstation PRO vs MS Hyper-V
Remy Lebeau replied to Drewsky's topic in Delphi IDE and APIs
I second VMWare, but I've always just used the free version, never the paid version. -
Type 72 is PT_CLSID, not sure what attribute 32768 ($8000) is. TIdCoderTNEF does not implement either one at this time. There are a LOT of MAPI properties that are not implemented yet. TIdCoderTnef is mainly concerned with extracting things that make sense to apply to a TIdMessage. I can look into maybe having TIdCoderTnef just skip unknown fields instead of raising an exception on them. Or maybe exposing an event so user code can decide what to do with them.
-
Is there any way to install delphi 10.4 package into Delphi 10.3?
Remy Lebeau replied to HalfBlindCoder's topic in VCL
Package binaries are specific to each compiler version. 10.3 can only use packages that have been compiled for 10.3, etc. If you have the source code for the 10.4 package, you will have to compile it using 10.3's compiler. -
What does this have to do with Indy? You are getting an ActiveX/COM error, not a socket error. Either Outlook is not installed, or it is malfunctioning, or your app simply doesn't have permission to run it.
-
Do bug fix patches really require active subscription?
Remy Lebeau replied to David Heffernan's topic in General Help
https://www.embarcadero.com/update-subscription -
Find exception location from MAP file?
Remy Lebeau replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
Last time I tried that (last year), I couldn't get it to work. So I had to write my own stack tracing code from scratch, which "works" but it doesn't display function names yet, only memory addresses relative to the process' base address. And that project has since been EOL'ed, so I can't go back and update it. -
Prevent Alt or Ctrl + Print Screen
Remy Lebeau replied to Henry Olive's topic in RTL and Delphi Object Pascal
On Windows 7 and later, you can use SetWindowDisplayAffinity() to specify that you want your Form window to only appear on a monitor display. That will omit (black) it out in screen captures, etc. -
Not safely, no. Is it legal to have a cross-process parent/child or owner/owned window relationship?
-
Find exception location from MAP file?
Remy Lebeau replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
The *preferred* base addresses are in the map file, as well as in exe/dll PE headers, but it is not guaranteed that exe/dll modules will actually use those base addresses at runtime (typically, they will, but the OS is free to move them somewhere else). You can use the Win32 GetModuleHandle() function, or the RTL's SysInti.HInstance, to get the actual base address at runtime. -
Is it possible to dynamically generate an editor GUI based on a data type class?
Remy Lebeau replied to wuwuxin's topic in VCL
There are 3rd party Object Inspectors available, if you search around. Or, you could write your own editor using the VCL's TValueListEditor component and some manual RTTI logic. -
How can i retrive components from a dat file;
Remy Lebeau replied to azrael_11's topic in RTL and Delphi Object Pascal
That article is written for saving a single component to a file, not for saving multiple components. I would suggest saving the TForm itself rather than each of its child components individually. -
How to "correctly" pass an array to a C function
Remy Lebeau replied to wuwuxin's topic in RTL and Delphi Object Pascal
Why? The C code clearly names the parameter "num_elements", so it stands to reason that it expects an "element count", not a "byte count". A dynamic array is already a pointer, so you don't need to index into the array, PInteger(data) will suffice. -
No. IFDEF your code to call the relevant OS APIs directly (SHBrowseForFolder()/IFileOpenDialog on Windows, etc). Or else just make your own dialog/Form that iterates the filesystem (ie, via the System.IOUtils.TDirectory class) and display the contents as needed.
-
What is the best way to precisely time/trigger threaded events
Remy Lebeau replied to Yaron's topic in Windows API
No, it is worse than that. TThread.Synchronize() puts the specified method pointer into a global thread-safe queue, along with a waitable event, then posts a signal to the main UI thread letting it know the queue is pending, and then waits on the queued event to be signaled. The main UI thread periodically iterates the queue (whenever it receives the posted signal, as well as whenever the main message loop enters an idle state, or user code calls Classes.CheckSynchronize() directly). All of the queued methods are called in order, and their events are signaled to unblock their waiting threads. -
TFileStream fmShare modes
Remy Lebeau replied to Mark Williams's topic in Algorithms, Data Structures and Class Design
Sharing flags are used with the Mode parameter, not the Rights parameter. Creation and Sharing flags can be OR'ed together in the Mode parameter, eg: Result := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyNone{, 0}); That was true in the old days when fmCreate was defined as $FFFF and thus could not be mixed with any other flags, but that is no longer the case. When the Rights parameter was introduced (which is not used on Windows when fmCreate is used), fmCreate was redefined as $FF00 so it can now be mixed with sharing flags, eg: fs := TFileStream.Create(FileName, fmCreate or fmShareDenyNone{, 0}); If no sharing mode is specified, fmShareCompat is the default, except in the specific case of the old fmCreate value ($FFFF) being used, in which case the default is fmShareExclusive instead for backwards compatibility. But either way, on Windows, fmShareCompat and fmShareExclusive mean the same thing - there is no sharing enabled on the file. -
TIdUDPServer listening to all local addresses
Remy Lebeau replied to caymon's topic in Network, Cloud and Web
No, when ABinding is bound to a wildcard IP like '::0'. Internally, TIdUDPServer uses recvfrom() to read packets, and the information you want is simply not available from recvfrom(). You would need to use recvmsg()/WSARecvMsg() instead (ie, via Indy's TIdStack.ReceiveMsg() method), which requires the IP_PKTINFO/IPV6_RECVPKINFO option be enabled on the socket for recvmsg() to report the receiving adapter's info on each packet read. TIdUDPServer does not support recvmsg(), though. You would have to alter its source code and recompile. Otherwise, you could use TIdUDPClient instead and run your own reading logic with it (despite their names, TIdUDPClient and TIdUDPServer are not true client/server components since UDP is connectionless, so their roles are a little more blurry than with TCP. You can use TIdUDPClient as a server, and TIdUDPServer as a client). Otherwise, you could simply enumerate the local IPs (ie via Indy's TIdStack.GetLocalAddressList() method) and create a separate Binding for each IP individually, and then ABinding.IP will have a meaningful value in the OnUDPRead event. This does mean that TIdUDPServer would need to allocate multiple sockets, though. No. Sending data out from a bound socket will use the IP of the adapter that the socket is bound to. If the socket is bound to a wildcard IP, then the OS will pick which adapter to use, based on its own routing tables for the destination IP. If you want to send out from a specific adapter, you need to either create and bind a separate socket (ie TIdUDPClient) to that adapter, and then send using that socket. use sendmsg()/WSASendMsg() (which Indy does not have a wrapper for), passing in a msghdr struct containing an IP_PKTINFO/IPV6_PKTINFO control message that specifies the desired source IP and interface index. See Setting the source IP for a UDP socket -
TIdUDPServer to listen to all addresses of a system
Remy Lebeau replied to caymon's topic in Network, Cloud and Web
Almost. It would be more accurate to say that it binds to all local network adapters for the Binding's specified IPVersion. The IPVersion is set to IPv4 by default (see the ID_DEFAULT_IP_VERSION constant in IdGlobal.pas). You would need 2 separate Binding objects to bind to all IPv4 and IPv6 adapters at the same time, as TIdUDPServer would have to create separate IPv4 and IPv6 listening sockets (Indy does not support dual-stack sockets at this time: ticket #29). For example: IdUDPServer.DefaultPort := ...; with IdUDPServer.Bindings.Add do begin IP := ...; IPVersion := Id_IPv4; end; with IdUDPServer.Bindings.Add do begin IP := ...; IPVersion := Id_IPv6; end; If you don't create any Bindings at all, TIdUDPServer will create 1 or 2 default Binding objects for you, depending on whether the underlying platform allows separate IPv4+IPv6 sockets to be bound on all adapters on the same port at the same time (not all do, ie Linux and Android do not, but Windows does). The above IP is an IPv6 address, so I'm guessing you have not created an IPv6 Binding to listen on. You can't bind an IPv4-only socket to IPv6 adapters, and vice versa.