-
Content Count
3000 -
Joined
-
Last visited
-
Days Won
135
Everything posted by Remy Lebeau
-
If you are using HTTP for authentication, then why not just keep using HTTP for the file transfers, too? That is doable. However, there is nothing in the FTP protocol to handle such tokens, so that will have to be transmitted as the username/password/account for the session. But once authenticated, you can set the TIdFTPServerContext.HomeDir and TIdFTPServerContext.CurrentDir properties as needed. There is nothing in the FTP protocol to handle that. I would suggest simply deleting the token from the database as soon as a client logs in with it, making it a session token. That way, noone else can login with that same token, and it will be invalidated once the client disconnects. Yes, you can store custom data in the TIdFTPServerContext on a per-client basis. You definately do not want to query the database on every transfer. That will not work. There is nothing in the FTP protocol that would allow for such data on a per-request basis. But, the client can login with the token, and the server can validate it at the time of login and remember that the client is allowed to upload, and then the client can freely upload files as needed. Not necessary if the token is deleted as soon as it is redeemed during login. However, if would be trivial to remember the expire time on a per-client basis, and the server could validate the expiration on each file transfer, if needed. Same restrictions apply as above. Well, then now you are back to hitting the database on each file transfer, which is not a good thing for performance. You don't need to use CommandHandlers for this. TIdFTPServer does that internally for you. TIdFTPServer has OnUserLogin, OnUserAccount, OnStoreFile, and OnRetrieveFile events that you can use instead. Yes, a windows service would be a good idea. That is a very broad question with many possible different solutions.
-
Yes. You cannot bind to an IP that does not belong to the local device that your server is running on. Alternatively, you can bind to '0.0.0.0' to listen on all local IPv4 addresses with a single socket (use '::0' for IPv6). The TIdFTP.ExternalIP property is for use with active-mode file transfers when the FTP session passes through a proxy/router. In active mode, an FTP client has to open a listening socket to receive an inbound TCP connection from the FTP server. As such, it needs to tell the FTP server which IP to connect to, which can't be the local IP when the client is running behind a proxy/router. The TIdFTP.ServerHOST property is used with virtual FTP server hosting, it has nothing to do with file transfers. An FTP server can host multiple sites on the same server IP. So, to differentiate which site an FTP client wants to access, it must send a HOST command to the FTP server. When the TIdFTP.UseHOST property is True, TIdFTP sends the HOST command before performing user authentication. If the TIdFTP.ServerHOST property is blank, the TIdFTP.Host property is used. TIdFTPServer has an OnHostCheck event, and a TIdFTPServerContext.Host property, for handling HOST commands from clients. Only the TIdFTP.ExternalIP property deals with file transfers. The TIdFTP.ServerHOST property has nothing to do with that. The TIdFTP.DataPort property is the local port that TIdFTP will listen on, and the FTP server will connect to, for active-mode transfers. If it is 0 (the default), an available random ephemeral port will be chosen by the OS. If TIdFTP is running behind a proxy/router, you need to set the TIdFTP.ExternalIP property to the proxy/router's public IP, and enable Port Forwarding on your proxy/router to forward inbound traffic from the router's public IP/DataPort to TIdFTP's listening Binding.IP/DataPort. If TIdFTPServer is running behind a proxy/router, you don't need to set the TIdFTP.ExternalIP (unless TIdFTP is also behind a proxy/router), but you do need to enable Port Forwarding on your server's proxy/router to forward inbound traffic from the router's public IP/Port to TIdFTPServer's listening Binding.IP/Port (not DataPort), as well as port forwarding for any ports you setup in TIdFTPServer's DefaultDataPort or PASVBoundPort(Min|Max) properties. The TIdFTP.Host property is the hostname/IP that TIdFTP will connect to when you call TIdFTP.Connect(), so it can't be blank. Having TIdFTP connect to the proxy/router's public IP will not work, since there is no FTP server running at that IP. That is merely the proxy/router's public IP to the outside world. The TIdFTP.ServerHOST property has no effect on where TIdFTP.Connect() connects to.
-
Yes, it is an RTL function, but it has different implementations for different platforms, and they want the POSIX implementation to be inlined, as it is just a 1-line call to the POSIX unlink() function, so ideal for inlining: function DeleteFile(const FileName: string): Boolean; ... {$IFDEF POSIX} var M: TMarshaller; begin Result := unlink(M.AsAnsi(FileName, CP_UTF8).ToPointer) <> -1; end; {$ENDIF POSIX} The Windows implementation used to be a 1-line call as well, to the Win32 DeleteFile() function, and thus was inlined, too: function DeleteFile(const FileName: string): Boolean; {$IFDEF MSWINDOWS} begin Result := Winapi.Windows.DeleteFile(PChar(FileName)); end; {$ENDIF MSWINDOWS} But, it is no longer inlined. In XE, new logic was added to make SysUtils.DeleteFile() call the Win32 GetFileAttributes() and RemoveDirectory() functions when deleting a symbolic link to a directory (why that was added, I did not know): function DeleteFile(const FileName: string): Boolean; {$IFDEF MSWINDOWS} var Flags, LastError: Cardinal; begin Result := Winapi.Windows.DeleteFile(PChar(FileName)); if not Result then begin LastError := GetLastError; Flags := GetFileAttributes(PChar(FileName)); if (Flags <> INVALID_FILE_ATTRIBUTES) and (faSymLink and Flags <> 0) and (faDirectory and Flags <> 0) then begin Result := RemoveDirectory(PChar(FileName)); Exit; end; SetLastError(LastError); end; end; {$ENDIF MSWINDOWS} As such, the inline keyword for that implementation was removed in XE2.
-
It means the function in question has been marked with the 'inline' specifier, but the function uses code from the Posix.Unistd unit, and that unit is not in a 'uses' clause in scope of the code that is calling the function, so the function can't be inlined at the call site. You posted this in a VCL forum, but the VCL's version of DeleteFile() does not use any Posix units, so you must be using FMX's version of DeleteFile() instead.
-
Depth First Search vs. Breadth First Search in directories
Remy Lebeau replied to dummzeuch's topic in Algorithms, Data Structures and Class Design
That is how I feel for most things in the IOUtils unit! It is just bad implementations all around. -
The WM_QUERYENDSESSION documentation clearly states that data saving should be deferred to the WM_ENDSESSION message, so you don't really need the MSG_SAVEDATA approach: And the WM_ENDSESSION documentation says: System shutdown can be aborted, in which case there would be no need to save any unsaved data, as long as your app has not been terminated yet. WM_QUERYENDSESSION is just asking for permission for the system to be shut down. The actual shutdown has not taken place yet. But, once an actual shutdown begins, unsaved data should be saved automatically. If you want to prompt the user whether unsaved data should be saved, do that in response to WM_CLOSE (or the TForm.OnCloseQuery event) instead: If you receive WM_ENDSESSION before receiving WM_CLOSE/OnCloseQuery then simply don't prompt the user.
-
Destroying TList with Managed Types
Remy Lebeau replied to Trevor S's topic in RTL and Delphi Object Pascal
No, because the as operator forces the compiler to create a hidden variable for the interface, before it is then passed to the const parameter. Had the code been written like this instead, THEN there would have been a problem: List.Add(TItem.Create); Correct. -
Destroying TList with Managed Types
Remy Lebeau replied to Trevor S's topic in RTL and Delphi Object Pascal
That is not the reason. Your test functions may simply be holding a hidden local reference to the IItem that you create and add to the list, so the item is not fully released until after the list has been freed first. Try this instead to isolate that reference so it gets released sooner: procedure TestDestroyOnly; var List: TItemList; procedure AddItem; begin List.Add(TItem.Create as IItem); end; begin Writeln( 'Start Test - Destroy Only'); List := TItemList.Create; try AddItem; finally List.Free; end; Writeln( 'End Test - Destroy Only'); end; procedure TestWithExplicitClear; var List: TItemList; procedure AddItem; begin List.Add(TItem.Create as IItem); end; begin Writeln( 'Start Test - Explicit Clear'); List := TItemList.Create; try AddItem; List.Clear; finally List.Free; end; Writeln( 'End Test - Explicit Clear'); end; -
All VCL TGraphic-derived classes have a SaveToFile() method, you don't need to save to a TFileStream manually: uses Vcl.Imaging.PngImage; procedure SaveBitmapToPNG(ABitmap: TBitmap; const AFileName: String; ACompresionLevel: Integer = 7); var img: TPngImage; begin img := TPngImage.Create; try img.CompressionLevel := ACompresionLevel; img.Assign(ABitmap); img.SaveToFile(AFileName); finally img.Free; end; end;
-
Record Circular References
Remy Lebeau replied to Bernard's topic in Algorithms, Data Structures and Class Design
Classes are reference types in Delphi: "Reference types can be forward declared because their size are always known (=SizeOf(pointer))." -
When another user calls, display a notification, and when the receiving user clicks on the notification, the app can then come forward and complete the call.
- 11 replies
-
- ios
- background
-
(and 1 more)
Tagged with:
-
That has been the case for several years now. Near the end of each beta, some testers (not all) are granted permission to publicly blog about their experiences in the beta. Note the disclaimers given at the top of each non-Embarcadero blog mentioned above (and others), eg: "I have been given permission from Embarcadero to blog about some of the new features and improvements." "Published with special permission from Embarcadero" "I’ve specifically been given permission to show updates and new features in the product" etc...
-
TThread.Sleep in iOS/android background mode threads, what to choose ?
Remy Lebeau replied to Rollo62's topic in Cross-platform
Short answer - background tasks that want to stay awake and running need to be done in a background service, not in a thread in an app that moves between the foreground and background. -
TThread.Sleep in iOS/android background mode threads, what to choose ?
Remy Lebeau replied to Rollo62's topic in Cross-platform
Processes and Application Lifecycle Understand the Activity Lifecycle Android is the same way. Background apps have to let the OS know that they are still "running" and should not be killed off. The OS is still free to kill them if it NEEDS to, though (low resources, etc). -
The data you are seeing is not *encrypted*, it is *compressed*. You are telling the server that you will accept responses in a compressed format (on top of encryption used by SSL/TLS), and the server is choosing to actually send a compressed response (you can verify that by looking at the TIdHTTP.Response.ContentEncoding property after the response arrives), however you have not setup TIdHTTP to actually decompress the compressed data for you, so you are seeing the compressed data as-is (well, after it has been String'ified, anyway). Get rid of that assignment to the TIdHTTP.Request.AcceptEncoding property (unless you really want to try your hand at decompressing the data manually). Instead, assign a TIdZLibCompressorBase-derived component, such as TIdCompressorZLib, to the TIdHTTP.Compressor property, and let TIdHTTP manage the AcceptEncoding property for you based on the compressor's capabilities. Don't call the TIdLogFile's Open() and Close() methods directly. Use its Active property instead, which will call Open()/Close() for you: IdLogFile1.Active := True; try LResp := IdHTTP1.Get('https://www.trivial.com.br/envia_arq3.php?senha=violeta&nome=tre'); finally IdLogFile1.Active := False; end; Calling Open() will open the log file you specify, but any sent/received data will not be logged to the file if Active is False.
-
VCL's TBitmap supports only the BMP format (and actual BMPs, not JPEG/PNG-encoded BMPs that recent Windows versions support). Saving a VCL TBitmap to a file with a ".png" extension DOES NOT create a PNG image, it creates a BMP image. PNG is compressed, whereas BMP is (usually) not (though VCL's TBitmap can load a compressed BMP - I think - but it does not produce compressed BMPs). FMX's TBitmap supports many formats, including BMP and PNG. Saving an FMX TBitmap to a file with a ".png" extension creates a real PNG image. Try saving your FMX TBitmap to a file with a ".bmp" extension and you will see a result closer to what VCL's TBitmap creates. BMP and PNG are both lossless formats. The pixel data in both formats may represent the same color arrangements to a viewer, but the way the data is stored in memory and in a file are very different.
-
TThread.Sleep in iOS/android background mode threads, what to choose ?
Remy Lebeau replied to Rollo62's topic in Cross-platform
Yes, Sleep()/usleep() really does block the calling thread, stopping it at the OS level, yielding to other threads so they can run, for at least the specified time (may be more). TEvent.WaitFor() is waiting for its internal event/semaphore object to be signaled by TEvent.SetEvent(), up to the specified time. During that wait time, the calling thread is stopped in a blocking sleep, yes. It is just using a different API to accomplish that. -
It is not ideal, but it is not a problem, since the server will just ignore the 'Content-Type' request header in a GET request. 'application/x-www-webform-urlencoded' would not be a valid media type for an 'Accept' request header.
-
There are numerous examples and tutorials online for how to work with webcams using the Win32 API. That information is accessible via webcam APIs. You don't need VCL or FMX for that. Or, you could simply take the screenshot using an in-memory GDI bitmap, and then copy the raw pixel data from that bitmap into an FMX TBitmap as needed. Look at CreateCompatibleDC(), CreateCompatibleBitmap(), GetObject(), and TBitmap.Canvas.MapBitmap()/UnmapBitmap().
-
The fact that you are even getting an HTTP 403 error at all means the SSL portion is working fine, since HTTPS is encrypted and you are able to see a decrypted response. So the problem has to be related to something else. Maybe you are not accessing the correct URL. Maybe you are not authenticated properly with the server. Maybe you are missing a client-side certificate. We don't know, since we don't know your setup or what the server is expecting. One thing you could try is capture the HTTP request the browser is sending (using your browser's built-in debugger, or an external HTTPS debug proxy like Fiddler), and then capture the HTTP request that TIdHTTP is sending (assign one of Indy's TIdLog... component to the TIdHTTP.Intercept property), and compare them for any differences. If you don't see anything apparent at the HTTP layer (cookies, login credentials, etc), then the issue could be at the SSL layer (ie, certificates, etc) You likely need to talk to the server admins about this, ask them what the server requires to get permission to access it. Have them look at the HTTPS requests on their end to see why the server is rejecting them. You should contact AToZed to double-check, but I do not believe that paid support is still provided, at least for Indy. Probably only for IntraWeb, if at all.
-
Record Circular References
Remy Lebeau replied to Bernard's topic in Algorithms, Data Structures and Class Design
Yeah, THAT is not going to happen anytime soon, either. -
TThread always raises OS Errror
Remy Lebeau replied to pyscripter's topic in RTL and Delphi Object Pascal
It wasn't resetting the last-error at all. That was, and always has been, the sole job of the OS to handle. Whatever value was assigned to the last-error going into the RTL's ThreadProc(), that is the value that was still set when going into TThread.Execute(). And since the issue is reproducible in FPC and Visual Studio then it has nothing to do with Delphi's RTL at all. Something has clearly changed internal to the OS itself, and you are not going to figure out what that is unless you trace into the OS's internal source code for yourself. That is because CreateThread() in Win9x does not allow the lpThreadId parameter to be NULL. If it is, CreateThread() fails with ERROR_INVALID_PARAMETER (87). That behavior was changed in NT4 onward to allow the lpThreadId parameter to be NULL, as the thread's ID is not typically needed by most users. That restriction used to be documented: The restriction is no longer documented since Win9x is no longer supported: -
Record Circular References
Remy Lebeau replied to Bernard's topic in Algorithms, Data Structures and Class Design
Not going to happen. You can't forward-declare records. -
That doesn't sound right. All shipped BPL files should have the IDE's package version in their filenames. Their corresponding BPI/DCP file may not, but Indy does not make use of {$LIBSUFFIX} yet, so even its DCP files should have the package version in their filenames. Unless Embarcadero has created their own unversioned packages. I haven't installed a new IDE in several years, so I wouldn't know how the shipped Indy is compiled anymore.
-
TThread always raises OS Errror
Remy Lebeau replied to pyscripter's topic in RTL and Delphi Object Pascal
Um, maybe I'm missing something, but that test is not passing a NULL pointer for the threadid. But, it doesn't matter anyway, because that test code belongs to WINE's emulated implementation of the Win32 API, it does not belong to the actual Win32 API on Windows.