Jump to content

Remy Lebeau

Members
  • Content Count

    2914
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Remy Lebeau

  1. Remy Lebeau

    combobox with custom drop down (treeview)

    The native VCL TComboBox does not support a custom drop down. There is nothing like that for the native VCL TComboBox, so you will have to use a 3rd party solution (for instance, TMS has a TAdvTreeComboBox component, and there is (an older) TreeComboBox available on Torry.net). Otherwise, you would have to write your own component. No.
  2. Indy doesn't care where the certificate files are stored, as long as the paths you supply to them are valid. Using absolute paths should have worked fine. Unless Indy is not the one using the certificate files to begin with...
  3. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    Not like it used to be. A few members are still lingering around the various forums. Several members have transitioned into MVPs, though some members (like me) haven't but are still recognized by a few Embarcadero employees to keep getting old TeamB perks (free versions, beta access, etc). Thanks, fixed.
  4. Yes, I do, and that is a VERY old version of OpenSSL, you need to upgrade. Indy currently supports up to 1.0.2u dated 21/12/2019, which you can find at https://github.com/IndySockets/OpenSSL-Binaries/ Work on supporting 1.1.x is in progress: https://github.com/IndySockets/Indy/pull/299 The Method property is deprecated, you should be using the SSLVersions property instead. TLS 1.0 has been (almost) completely phased out of modern servers. Most modern servers support, if not require, TLS 1.2, so that really should be your minimum going forward nowadays. You should also have TLS 1.1 enabled as a fallback, just in case, but even that is being phased out as well. To enable multiple TLS versions with the Method property, you would have to set it to sslvSSLv23, which will set the SSLVersions property to all SSL/TLS versions supported by Indy (including SSL 2.0, SSL 3.0, and TLS 1.x), but it is better to use the SSLVersions property directly (ie, don't enable SSL 2.0 and 3.0 anymore), eg: SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; Having multiple TLS versions enabled at a time will allow OpenSSL to negotiate the highest version that both client and server support. That being said, on a side note, you do not need to assign ANY of the following SSLIOHandler properties, as Indy handles them internally for you: Setup the necessary Host/Port only in the client component, ie TIdIMAP4. The Connect() method will then copy them into the IOHandler as needed.
  5. Why? What is the actual problem with having them in an application folder?
  6. I'm assuming you are using OpenSSL, which version? Also, what version of the SSL/TLS protocol are you using? Then you should be able to debug it. For instance, using Wireshark, you can see the details of the actual SSL/TLS handshake (at least until the connection is encrypted. It takes a few back-and-forths to get that far). At what stage in the handshake is the server disconnecting? The error means that the IMAP server is closing its end of the TCP connection while the SSL/TLS handshake is still in progress. Typically, this happens if the server encounters something it doesn't like in the client's SSL/TLS handshake, and the server decides to bail out without first sending a TLS alert to explain what is wrong.
  7. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    <sigh> Fine, then let's go the long way around, using a full wrapper class, forget TMethod (even though it will technically work at runtime, despite the compiler warnings): // SOURCE #define BUILDING_DLL #include "mydll.h" class eventHandlers { public: OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; void __fastcall OnWorkBeginHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { if (onWorkBegin) onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } void __fastcall OnWorkEndHandler(TObject *ASender, TWorkMode AWorkMode) { if (onWorkEnd) onWorkEnd((OnWorkMode)AWorkMode); } void __fastcall OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { if (onWork) onWork((OnWorkMode)AWorkMode, AWorkCount); } }; static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { eventHandlers events; events.onWorkBegin = onWorkBegin; events.onWorkEnd = onWorkEnd; events.onWork = onWork; http->OnWorkBegin = &events.OnWorkBeginHandler; http->OnWorkEnd = &events.OnWorkEndHandler; http->OnWork = &events.OnWorkHandler; TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } Odd, they should be. Especially since OnWorkEnd can't be called if OnWorkBegin is not called first. No. Correct, because the event is expecting a class method, not a standalone procedure. Using TMethod is the way to get around that.
  8. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    <sigh> Try this: // SOURCE #define BUILDING_DLL #include "mydll.h" struct userFunctions { OnWorkBeginFunc onWorkBegin; OnWorkEndFunc onWorkEnd; OnWorkFunc onWork; }; static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkBegin((OnWorkMode)AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWorkEnd((OnWorkMode)AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { userFunctions *funcs = static_cast<userFunctions*>(AData); funcs->onWork((OnWorkMode)AWorkMode, AWorkCount); } template <typename EventType, typename HandlerType> static EventType makeEventHandler(void *data, HandlerType *handler) { TMethod m; m.Data = data; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = data; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { userFunctions funcs; if (onWorkBegin) { funcs.onWorkBegin = onWorkBegin; http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(&funcs, &MyOnWorkBeginHandler); } if (onWorkEnd) { funcs.onWorkEnd = onWorkEnd; http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(&funcs, &MyOnWorkEndHandler); } if (onWork) { funcs.onWork = onWork; http->OnWork = makeEventHandler<TWorkEvent>(&funcs, &MyOnWorkHandler); } TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; }
  9. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    My bad, too many *s in the DLL's event handlers. Try this instead: static void __fastcall MyOnWorkBeginHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc>(AData); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *AData, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc func = static_cast<OnWorkEndFunc>(AData); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *AData, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc func = static_cast<OnWorkFunc>(AData); func(AWorkMode, AWorkCount); }
  10. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    Why not just install the latest Indy in C++Builder 6? No, not directly. Nor should you be trying to. What you should do instead is let the caller pass in its own function pointer(s), which your DLL then remembers, and can then call from inside its own OnWork event handler(s). For example, the following uses a little-known trick for assigning a standalone procedure as a VCL event handler, while also passing a user-defined parameter to it. There are other ways this could be implemented, but this is the most straight-forward way I can think of right now for your scenario: // HEADER #ifndef MY_DLL_H #define MY_DLL_H #ifdef __cplusplus extern "C" { #endif #ifdef BUILDING_DLL #define EXPORT __declspec(dllexport) #else #define EXPORT __declspec(dllimport) #endif enum OnWorkMode { WorkReading, WorkWriting }; typedef void (*OnWorkBeginFunc)(OnWorkMode AWorkMode, __int64 AWorkCountMax); typedef void (*OnWorkEndFunc)(OnWorkMode AWorkMode); typedef void (*OnWorkFunc)(OnWorkMode AWorkMode, __int64 AWorkCount); EXPORT char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork); EXPORT void freedata(char *data); #ifdef __cplusplus } #endif #endif // SOURCE #define BUILDING_DLL #include "mydll.h" static void __fastcall MyOnWorkBeginHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCountMax) { OnWorkBeginFunc func = static_cast<OnWorkBeginFunc*>(ASelf); func(AWorkMode, AWorkCountMax); } static void __fastcall MyOnWorkEndHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode) { OnWorkEndFunc *func = static_cast<OnWorkEndFunc*>(ASelf); func(AWorkMode); } static void __fastcall MyOnWorkHandler(void *ASelf, TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) { OnWorkFunc *func = static_cast<OnWorkFunc*>(ASelf); func(AWorkMode, AWorkCount); } template <typename EventType, typename FuncType, typename HandlerType> static EventType makeEventHandler(FuncType *func, HandlerType *handler) { TMethod m; m.Data = func; m.Code = handler; return reinterpret_cast<EventType&>(m); /* alternatively: EventType evt; TMethod &m = reinterpret_cast<TMethod&>(evt); m.Data = func; m.Code = handler; return evt; */ } static bool sslPathSet = false; char* getdata(const char *url, OnWorkBeginFunc onWorkBegin, OnWorkEndFunc onWorkEnd, OnWorkFunc onWork) { char *result = NULL; try { if (!sslPathSet) { sslPathSet = true; IdOpenSSLSetLibPath(ExtractFilePath(ParamStr(0))); } TIdHTTP *http = new TIdHTTP; try { if (onWorkBegin) http->OnWorkBegin = makeEventHandler<TWorkBeginEvent>(onWorkBegin, &MyOnWorkBeginHandler); if (onWorkEnd) http->OnWorkEnd = makeEventHandler<TWorkEndEvent>(onWorkEnd, &MyOnWorkEndHandler); if (onWork) http->OnWork = makeEventHandler<TWorkEvent>(onWork, &MyOnWorkHandler); TIdSSLIOHandlerSocketOpenSSL *SSL = new TIdSSLIOHandlerSocketOpenSSL(http); SSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2; http->IOHandler = SSL; http->ReadTimeout = 10000; UTF8String s = http->Get(url); result = new char[s.Length()+1]; memcpy(result, s.c_str(), s.Length()+1); } __finally { delete http; } } catch (...) { return NULL; } return result; } void freedata(char *data) { delete[] data; } And then you C++Builder 6 code can use it like this: #include "mydll.h" #pragma comment(lib, "mydll.lib") // or add the .lib file to your project void OnWorkBegin(OnWorkMode AWorkMode, __int64 AWorkCountMax) { //... } void OnWorkEnd(OnWorkMode AWorkMode) { //... } void OnWork(OnWorkMode AWorkMode, __int64 AWorkCount) { //... } char *data = getdata("url", &OnWorkBegin, &OnWorkEnd, &OnWork); if (data) { // ... freedata(data); } Yes, because System::String is an alias for System::UnicodeString in CB10.3, but is an alias for System::AnsiString in BCB6. Thus, you were indeed trying to pass a wchar_t* pointer to strcpy() rather than a char* pointer.
  11. Remy Lebeau

    Warning on depreciated symbol, Delphi 11

    Or you can use {$WARN SYMBOL_DEPRECATED OFF} in the code.
  12. Remy Lebeau

    Warning on depreciated symbol, Delphi 11

    Actually, several new ComponentPlatform enum values were introduced and old values were deprecated:
  13. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    That means the DLL itself failed to load into memory, before Indy could even try to access any of its functions. Such a failure is most commonly because the DLL itself, or one of its dependent DLLs or imports, can't be found by the OS. OpenSSL build numbers are represented as letters in the English alphabet. So, 1.0.2q is build 1.0.2.17, 1.0.2u is build 1.0.2.21, etc.
  14. Remy Lebeau

    Cyrillic characters in URL

    Yes. Delphi 11 shipped with the latest Indy that was available 3 months ago.
  15. Remy Lebeau

    Cyrillic characters in URL

    Actually, about 8 months ago, TIdHTTPServer was updated to now default to parsing input parameters using UTF-8 if no charset is specified in the Content-Type header. I have added that note to the StackOverflow post you linked to.
  16. I have no idea, I don't use CrossVcl myself. I'm just going off of what the website say:
  17. Sure, by basically providing a compatibility layer that simulates the Win32 API on OSX/Linux.
  18. Remy Lebeau

    Best way to replace D11 distributed Indy with latest Git Indy?

    Not yet, because I don't have D11 installed to generate them. But anyone is more than welcome to submit a Pull Request to contribute them. Agreed (or at least rename the folder, in case you need to recover it later). That is usually the first thing I do when I install a new IDE version, and then have my projects point to the latest Indy repo source.
  19. Borland tried that once, with Kylix in Delphi 6. It was basically VCL for Linux, and it was a big failure, so it got dropped. VCL is just too dependent on Windows to go cross-platform. Hence why we have FireMonkey now for that task, which has no real relation to VCL, other than a few shared class names and public interfaces, but ultimately based on different architectures.
  20. Remy Lebeau

    update indy to use tls 1.2 in c++builder 6?

    The latest version of Indy 10 (https://github.com/IndySockets/Indy/) supports back to C++Builder 5, and can officially use OpenSSL DLLs up to 1.0.2u (for OpenSSL 1.1.x+, use this WIP code). TLS 1.2 has been available in OpenSSL since 1.0.1, and Indy 10 has supported TLS 1.2 for a long time. OpenSSL DLLs are available at https://github.com/IndySockets/OpenSSL-Binaries (https://indy.fulgan.com/SSL/ is retired). If you are getting errors from Indy's standard TIdSSLIOHandlerSocketOpenSSL component when it tries to load the DLLs, then you are likely using DLLs that are not compatible with your version of Indy. You can call Indy's WhichFailedToLoad() function in the IdSSLOpenSSLHeaders unit to find out why the DLLs are failing to load.
  21. Remy Lebeau

    ExtractHeaderFields with special characters

    Which version of Delphi is that other app written in, and what version of Indy is it using? TIdMultipartFormDataStream encodes non-ASCII characters in all field names and filenames according to RFC 2047, which your earlier example is NOT encoded as, so I doubt the example is coming from Indy, unless maybe it is a really old version. On Windows, depending on what the OS system language is set to, TIdMultipartFormDataStream uses either UTF-8 or the OS language as the charset to encode characters to bytes. And then depending on the charset used, it uses either Quoted-Printable or Base64 to encode those bytes in the Content-Disposition header. These values are reflected in the HeaderCharSet and HeaderEncoding properties of each TIdFormDataField object that the TIdMultipartFormDataStream.Add(...) methods create. Double-check what these values are actually being set to on your system, but you can also set them yourself as needed. I would suggest using HeaderCharSet='utf-8' and HeaderEncoding='B', eg: var mPartStream := TIdMultiPartFormDataStream; field: TIdFormDataField; ... mPartStream := TIdMultiPartFormDataStream.Create; FHttp.Request.ContentType := mPartStream.RequestContentType; for ix := 0 to FPostNames.Count -1 do begin if FPostFiles[ix].IsNull then begin field := mPartStream.AddFormField(FPostNames[ix], FPostValues[ix], 'UTF-8'); field.ContentTransfer := '8bit'; end else begin field := mPartStream.AddFile(FPostNames[ix], FPostFiles[ix].PathName, FPostContentTypes[ix]); end; field.HeaderCharSet := 'UTF-8'; field.HeaderEncoding := 'B'; end; Though, I suspect even this will not give you the end result you are looking for with ExtractHeaderFields(), if it is really trying to url-decode fields that are not url-encoded to begin with. HTTP does not use url-encoding in header content, so what you are experiencing really sounds like a logic bug in the HttpApp framework. But at least this should give your code access to the stream's encoded Base64 data, which you can then decode manually to a Unicode string, such as with Indy's DecodeHeader() function in the IdCoderHeader unit.
  22. Remy Lebeau

    Interfaces defined in base classes

    No, it is not a valid cast, in this case. Obj is declared as IInterface, so it is pointing at the IInterface portion of the TMyClass object. But the object also has other portions in it, for TMyBaseClass, TInterfacedObject, IMyInterface, etc (not drawn exactly as the compiler lays it out, but you should get the idea): ------------------- | TMyClass | | ---------------- | <- Obj points here | | IInterface | | | ---------------- | | ---------------- | | | IMyInterface | | | ---------------- | | ... | ------------------- You are type-casting Obj AS-IS from IInterface to IMyInterface, which tells the compiler to MIS-interpret Obj as pointing to the IMyInterface portion of the object EVEN-THOUGH it is actually pointing at the IInterface portion of the object. Whereas the 'as' operator and Support() function, which use QueryInterface() internally, will return a pointer that properly points to the IMyInterface portion of the object, eg: ------------------- | TMyClass | | ---------------- | <- IMyInterface(Obj) points here | | IInterface | | | ---------------- | | ---------------- | <- (Obj as IMyInterface) points here! | | IMyInterface | | | ---------------- | | ... | ------------------- So, when you call IMyInterface(Obj).DoesNothing(), you are calling DoesNothing() on an invalid IMyInterface, so it does not access the correct area of the TMyClass object. In order for the compiler to access the members of a TMyClass object through an IInterface pointer, an IMyInterface pointer, a TMyBaseClass pointer, etc, the pointer has to be ADJUSTED according to the offset of the pointer's dereferenced type in relation to the implementation class. The compiler knows the offset of the IInterface portion of TMyClass, so given an IInterface pointer it knows how to adjust that pointer to reach TMyClass. Same with IMyInterface, etc. Thus, it adjusts a pointer according to the pointer's DECLARED type. So, if you start out with an invalid pointer to begin with, those adjustments are not performed correctly, and you end up with bad behaviors, such as crashes, corrupted data, etc. That works only if SomeVariable is pointing at the memory address where a valid IWhatever exists. Because you altered the layout of the object in memory, but didn't update your pointer usage accordingly.
  23. Remy Lebeau

    EIdConnClosedGracefully error..

    The only way that can happen is if the HTTP server is closing its end of the TCP connection prematurely before the end of the response has been sent. If the server wants to use a disconnect to signal end-of-response (which is a valid use-case), it has to indicate that up-front in the HTTP response headers, in which case TIdHTTP would simply handle the disconnect internally and your code would not see this error. So, the only way this error can reach your code is if the disconnect is unexpected. No, you don't need to do that in this case, since you are destroying the TIdHTTP object after sending a single HTTP request. That earlier advice only applies if you were reusing the TIdHTTP object for sending multiple HTTP requests. And even then, under ideal conditions, you still should not need to manually disconnect and clear the buffer, TIdHTTP should be handling that internally for you. Also, your CheckUrl() appears to be unnecessary. Just request the URL unconditionally, and handle any errors it may raise.
  24. Remy Lebeau

    ExtractHeaderFields with special characters

    That header is malformed. HTTP headers simply can't have un-encoded non-ASCII characters like that. There are competing standards for how they need to be encoded, though. There is RFC 2183, RFC 2047, RFC 7578, RFC 8187, HTML5, etc. TURLEncoding decodes %HH sequences into bytes, and then charset-decodes those bytes into Unicode. IIRC, it expects the bytes to be UTF-8 encoded by default.
  25. Remy Lebeau

    FMX in VCL app - working natively ?

    Mixing of VCL and FMX together in the same project is NOT officially supported. There are 3rd party solutions to make it work, but out of the box this is a fluke if it actually works.
×