877 lines
34 KiB
ObjectPascal
877 lines
34 KiB
ObjectPascal
/// curl library direct access classes
|
|
// - this unit is a part of the freeware Synopse framework,
|
|
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
|
|
unit SynCurl;
|
|
|
|
{
|
|
This file is part of Synopse framework.
|
|
|
|
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
|
|
Synopse Informatique - https://synopse.info
|
|
|
|
*** BEGIN LICENSE BLOCK *****
|
|
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
The contents of this file are subject to the Mozilla Public License Version
|
|
1.1 (the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
for the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is Synopse mORMot framework.
|
|
|
|
The Initial Developer of the Original Code is Arnaud Bouchez.
|
|
|
|
Portions created by the Initial Developer are Copyright (C) 2022
|
|
the Initial Developer. All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
- Marcos Douglas B. Santos (mdbs99)
|
|
- Pavel Mashlyakovskii (mpv)
|
|
|
|
|
|
Alternatively, the contents of this file may be used under the terms of
|
|
either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
in which case the provisions of the GPL or the LGPL are applicable instead
|
|
of those above. If you wish to allow use of your version of this file only
|
|
under the terms of either the GPL or the LGPL, and not to allow others to
|
|
use your version of this file under the terms of the MPL, indicate your
|
|
decision by deleting the provisions above and replace them with the notice
|
|
and other provisions required by the GPL or the LGPL. If you do not delete
|
|
the provisions above, a recipient may use your version of this file under
|
|
the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
***** END LICENSE BLOCK *****
|
|
|
|
}
|
|
|
|
{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
|
|
|
|
interface
|
|
|
|
uses
|
|
{$ifdef MSWINDOWS}
|
|
Windows,
|
|
SynWinSock,
|
|
{$else}
|
|
{$ifdef FPC}
|
|
dynlibs,
|
|
SynFPCSock,
|
|
SynFPCLinux,
|
|
{$endif FPC}
|
|
{$ifdef KYLIX3}
|
|
LibC,
|
|
SynFPCSock, // shared with Kylix
|
|
SynKylix,
|
|
{$endif KYLIX3}
|
|
{$endif MSWINDOWS}
|
|
SysUtils,
|
|
Classes;
|
|
|
|
{ -------------- curl library low-level interfaces, constants and types }
|
|
|
|
const
|
|
/// low-level libcurl library file name, depending on the running OS
|
|
LIBCURL_DLL = {$ifdef Darwin} 'libcurl.dylib' {$else}
|
|
{$ifdef Linux} 'libcurl.so' {$else}
|
|
{$ifdef CPU64} 'libcurl-x64.dll' {$else}
|
|
'libcurl.dll' {$endif}{$endif}{$endif};
|
|
|
|
{$Z4}
|
|
type
|
|
/// low-level exception raised during libcurl library access
|
|
ECurl = class(Exception);
|
|
|
|
/// low-level options for libcurl library API calls
|
|
TCurlOption = (
|
|
coPort = 3,
|
|
coTimeout = 13,
|
|
coInFileSize = 14,
|
|
coLowSpeedLimit = 19,
|
|
coLowSpeedTime = 20,
|
|
coResumeFrom = 21,
|
|
coCRLF = 27,
|
|
coSSLVersion = 32,
|
|
coTimeCondition = 33,
|
|
coTimeValue = 34,
|
|
coVerbose = 41,
|
|
coHeader = 42,
|
|
coNoProgress = 43,
|
|
coNoBody = 44,
|
|
coFailOnError = 45,
|
|
coUpload = 46,
|
|
coPost = 47,
|
|
coFTPListOnly = 48,
|
|
coFTPAppend = 50,
|
|
coNetRC = 51,
|
|
coFollowLocation = 52,
|
|
coTransferText = 53,
|
|
coPut = 54,
|
|
coAutoReferer = 58,
|
|
coProxyPort = 59,
|
|
coPostFieldSize = 60,
|
|
coHTTPProxyTunnel = 61,
|
|
coSSLVerifyPeer = 64,
|
|
coMaxRedirs = 68,
|
|
coFileTime = 69,
|
|
coMaxConnects = 71,
|
|
coClosePolicy = 72,
|
|
coFreshConnect = 74,
|
|
coForbidResue = 75,
|
|
coConnectTimeout = 78,
|
|
coHTTPGet = 80,
|
|
coSSLVerifyHost = 81,
|
|
coHTTPVersion = 84,
|
|
coFTPUseEPSV = 85,
|
|
coSSLEngineDefault = 90,
|
|
coDNSUseGlobalCache = 91,
|
|
coDNSCacheTimeout = 92,
|
|
coCookieSession = 96,
|
|
coBufferSize = 98,
|
|
coNoSignal = 99,
|
|
coProxyType = 101,
|
|
coUnrestrictedAuth = 105,
|
|
coFTPUseEPRT = 106,
|
|
coHTTPAuth = 107,
|
|
coFTPCreateMissingDirs = 110,
|
|
coProxyAuth = 111,
|
|
coFTPResponseTimeout = 112,
|
|
coIPResolve = 113,
|
|
coMaxFileSize = 114,
|
|
coFTPSSL = 119,
|
|
coTCPNoDelay = 121,
|
|
coFTPSSLAuth = 129,
|
|
coIgnoreContentLength = 136,
|
|
coFTPSkipPasvIp = 137,
|
|
coTimeoutMs = 155, // since libcurl 7.16.2 - April 11 2007
|
|
coConnectTimeoutMs = 156,
|
|
coFile = 10001,
|
|
coWriteData = coFile,
|
|
coURL = 10002,
|
|
coProxy = 10004,
|
|
coUserPwd = 10005,
|
|
coProxyUserPwd = 10006,
|
|
coRange = 10007,
|
|
coInFile = 10009,
|
|
coErrorBuffer = 10010,
|
|
coPostFields = 10015,
|
|
coReferer = 10016,
|
|
coFTPPort = 10017,
|
|
coUserAgent = 10018,
|
|
coCookie = 10022,
|
|
coHTTPHeader = 10023,
|
|
coHTTPPost = 10024,
|
|
coSSLCert = 10025,
|
|
coSSLCertPasswd = 10026,
|
|
coQuote = 10028,
|
|
coWriteHeader = 10029,
|
|
coCookieFile = 10031,
|
|
coCustomRequest = 10036,
|
|
coStdErr = 10037,
|
|
coPostQuote = 10039,
|
|
coWriteInfo = 10040,
|
|
coProgressData = 10057,
|
|
coXferInfoData = coProgressData,
|
|
coInterface = 10062,
|
|
coKRB4Level = 10063,
|
|
coCAInfo = 10065,
|
|
coTelnetOptions = 10070,
|
|
coRandomFile = 10076,
|
|
coEGDSocket = 10077,
|
|
coCookieJar = 10082,
|
|
coSSLCipherList = 10083,
|
|
coSSLCertType = 10086,
|
|
coSSLKey = 10087,
|
|
coSSLKeyType = 10088,
|
|
coSSLEngine = 10089,
|
|
coPreQuote = 10093,
|
|
coDebugData = 10095,
|
|
coCAPath = 10097,
|
|
coShare = 10100,
|
|
coEncoding = 10102,
|
|
coAcceptEncoding = coEncoding,
|
|
coPrivate = 10103,
|
|
coHTTP200Aliases = 10104,
|
|
coSSLCtxData = 10109,
|
|
coNetRCFile = 10118,
|
|
coSourceUserPwd = 10123,
|
|
coSourcePreQuote = 10127,
|
|
coSourcePostQuote = 10128,
|
|
coIOCTLData = 10131,
|
|
coSourceURL = 10132,
|
|
coSourceQuote = 10133,
|
|
coFTPAccount = 10134,
|
|
coCookieList = 10135,
|
|
coUnixSocketPath = 10231,
|
|
coWriteFunction = 20011,
|
|
coReadFunction = 20012,
|
|
coProgressFunction = 20056,
|
|
coHeaderFunction = 20079,
|
|
coDebugFunction = 20094,
|
|
coSSLCtxtFunction = 20108,
|
|
coIOCTLFunction = 20130,
|
|
coXferInfoFunction = 20219,
|
|
coInFileSizeLarge = 30115,
|
|
coResumeFromLarge = 30116,
|
|
coMaxFileSizeLarge = 30117,
|
|
coPostFieldSizeLarge = 30120
|
|
);
|
|
|
|
/// low-level result codes for libcurl library API calls
|
|
TCurlResult = (
|
|
crOK, crUnsupportedProtocol, crFailedInit, crURLMalformat, crURLMalformatUser,
|
|
crCouldNotResolveProxy, crCouldNotResolveHost, crCouldNotConnect,
|
|
crFTPWeirdServerReply, crFTPAccessDenied, crFTPUserPasswordIncorrect,
|
|
crFTPWeirdPassReply, crFTPWeirdUserReply, crFTPWeirdPASVReply,
|
|
crFTPWeird227Format, crFTPCantGetHost, crFTPCantReconnect, crFTPCouldNotSetBINARY,
|
|
crPartialFile, crFTPCouldNotRetrFile, crFTPWriteError, crFTPQuoteError,
|
|
crHTTPReturnedError, crWriteError, crMalFormatUser, crFTPCouldNotStorFile,
|
|
crReadError, crOutOfMemory, crOperationTimeouted,
|
|
crFTPCouldNotSetASCII, crFTPPortFailed, crFTPCouldNotUseREST, crFTPCouldNotGetSize,
|
|
crHTTPRangeError, crHTTPPostError, crSSLConnectError, crBadDownloadResume,
|
|
crFileCouldNotReadFile, crLDAPCannotBind, crLDAPSearchFailed,
|
|
crLibraryNotFound, crFunctionNotFound, crAbortedByCallback,
|
|
crBadFunctionArgument, crBadCallingOrder, crInterfaceFailed,
|
|
crBadPasswordEntered, crTooManyRedirects, crUnknownTelnetOption,
|
|
crTelnetOptionSyntax, crObsolete, crSSLPeerCertificate, crGotNothing,
|
|
crSSLEngineNotFound, crSSLEngineSetFailed, crSendError, crRecvError,
|
|
crShareInUse, crSSLCertProblem, crSSLCipher, crSSLCACert, crBadContentEncoding,
|
|
crLDAPInvalidURL, crFileSizeExceeded, crFTPSSLFailed, crSendFailRewind,
|
|
crSSLEngineInitFailed, crLoginDenied, crTFTPNotFound, crTFTPPerm,
|
|
crTFTPDiskFull, crTFTPIllegal, crTFTPUnknownID, crTFTPExists, crTFTPNoSuchUser
|
|
);
|
|
|
|
CURLSHcode = (CURLSHE_OK, // all is fine
|
|
CURLSHE_BAD_OPTION, // 1
|
|
CURLSHE_IN_USE, // 2
|
|
CURLSHE_INVALID, // 3
|
|
CURLSHE_NOMEM, // 4 out of memory
|
|
CURLSHE_NOT_BUILT_IN, // 5 feature not present in lib
|
|
CURLSHE_LAST); // never use
|
|
|
|
CURLSHoption = (CURLSHOPT_NONE,
|
|
CURLSHOPT_SHARE,
|
|
CURLSHOPT_UNSHARE,
|
|
CURLSHOPT_LOCKFUNC,
|
|
CURLSHOPT_UNLOCKFUNC,
|
|
CURLSHOPT_USERDATA,
|
|
CURLSHOPT_LAST);
|
|
|
|
/// low-level information enumeration for libcurl library API calls
|
|
TCurlInfo = (
|
|
ciNone,
|
|
ciLastOne = 28,
|
|
ciEffectiveURL = 1048577,
|
|
ciContentType = 1048594,
|
|
ciPrivate = 1048597,
|
|
ciRedirectURL = 1048607,
|
|
ciPrimaryIP = 1048608,
|
|
ciLocalIP = 1048617,
|
|
ciResponseCode = 2097154,
|
|
ciHeaderSize = 2097163,
|
|
ciRequestSize = 2097164,
|
|
ciSSLVerifyResult = 2097165,
|
|
ciFileTime = 2097166,
|
|
ciRedirectCount = 2097172,
|
|
ciHTTPConnectCode = 2097174,
|
|
ciHTTPAuthAvail = 2097175,
|
|
ciProxyAuthAvail = 2097176,
|
|
ciOS_Errno = 2097177,
|
|
ciNumConnects = 2097178,
|
|
ciPrimaryPort = 2097192,
|
|
ciLocalPort = 2097194,
|
|
ciTotalTime = 3145731,
|
|
ciNameLookupTime = 3145732,
|
|
ciConnectTime = 3145733,
|
|
ciPreTRansferTime = 3145734,
|
|
ciSizeUpload = 3145735,
|
|
ciSizeDownload = 3145736,
|
|
ciSpeedDownload = 3145737,
|
|
ciSpeedUpload = 3145738,
|
|
ciContentLengthDownload = 3145743,
|
|
ciContentLengthUpload = 3145744,
|
|
ciStartTransferTime = 3145745,
|
|
ciRedirectTime = 3145747,
|
|
ciAppConnectTime = 3145761,
|
|
ciSSLEngines = 4194331,
|
|
ciCookieList = 4194332,
|
|
ciCertInfo = 4194338,
|
|
ciSizeDownloadT = 6291464,
|
|
ciTotalTimeT = 6291506, // (6) can be used for calculation "Content download time"
|
|
ciNameLookupTimeT = 6291507, // (1) DNS lookup
|
|
ciConnectTimeT = 6291508, // (2) connect time
|
|
ciPreTransferTimeT = 6291509, // (4)
|
|
ciStartTransferTimeT = 6291510, // (5) Time to first byte
|
|
ciAppConnectTimeT = 6291512 // (3) SSL handshake
|
|
);
|
|
{$ifdef LIBCURLMULTI}
|
|
/// low-level result codes for libcurl library API calls in "multi" mode
|
|
TCurlMultiCode = (
|
|
cmcCallMultiPerform = -1,
|
|
cmcOK = 0,
|
|
cmcBadHandle,
|
|
cmcBadEasyHandle,
|
|
cmcOutOfMemory,
|
|
cmcInternalError,
|
|
cmcBadSocket,
|
|
cmcUnknownOption,
|
|
cmcAddedAlready,
|
|
cmcRecursiveApiCall
|
|
);
|
|
|
|
/// low-level options for libcurl library API calls in "multi" mode
|
|
TCurlMultiOption = (
|
|
cmoPipeLining = 3,
|
|
cmoMaxConnects = 6,
|
|
cmoMaxHostConnections = 7,
|
|
cmoMaxPipelineLength = 8,
|
|
cmoMaxTotalConnections = 13,
|
|
cmoSocketData = 10002,
|
|
cmoTimerData = 10005,
|
|
cmoPipeliningSiteBL = 10011,
|
|
cmoPipeliningServerBL = 10012,
|
|
cmoPushData = 10015,
|
|
cmoSocketFunction = 20001,
|
|
cmoTimerFunction = 20004,
|
|
cmoPushFunction = 20014,
|
|
cmoContentLengthPenaltySize = 30009,
|
|
cmoChunkLengthPenaltySize = 30010
|
|
);
|
|
{$endif LIBCURLMULTI}
|
|
|
|
/// low-level version identifier of the libcurl library
|
|
TCurlVersion = (cvFirst,cvSecond,cvThird,cvFour,cvLast);
|
|
/// low-level initialization option for libcurl library API
|
|
// - currently, only giSSL is set, since giWin32 is redundant with WinHTTP
|
|
TCurlGlobalInit = set of (giNone,giSSL,giWin32,giAll);
|
|
/// low-level message state for libcurl library API
|
|
TCurlMsg = (cmNone, cmDone);
|
|
|
|
PAnsiCharArray = array[0..(MaxInt div SizeOf(PAnsiChar))-1] of PAnsiChar;
|
|
|
|
/// low-level version information for libcurl library
|
|
TCurlVersionInfo = record
|
|
age: TCurlVersion;
|
|
version: PAnsiChar;
|
|
version_num: cardinal;
|
|
host: PAnsiChar;
|
|
features: longint;
|
|
ssl_version: PAnsiChar;
|
|
ssl_version_num: PAnsiChar;
|
|
libz_version: PAnsiChar;
|
|
protocols: ^PAnsiCharArray;
|
|
ares: PAnsiChar;
|
|
ares_num: longint;
|
|
libidn: PAnsiChar;
|
|
end;
|
|
PCurlVersionInfo = ^TCurlVersionInfo;
|
|
|
|
/// low-level access to the libcurl library instance
|
|
TCurl = type pointer;
|
|
/// low-level string list type for libcurl library API
|
|
TCurlSList = type pointer;
|
|
PCurlSList = ^TCurlSList;
|
|
TCurlShare = type pointer;
|
|
PPCurlSListArray = ^PCurlSListArray;
|
|
PCurlSListArray = array[0..(MaxInt div SizeOf(PCurlSList))-1] of PCurlSList;
|
|
/// low-level access to the libcurl library instance in "multi" mode
|
|
TCurlMulti = type pointer;
|
|
/// low-level access to one libcurl library socket instance
|
|
TCurlSocket = type TSocket;
|
|
|
|
/// low-level certificate information for libcurl library API
|
|
TCurlCertInfo = packed record
|
|
num_of_certs: integer;
|
|
{$ifdef CPUX64}_align: array[0..3] of byte;{$endif}
|
|
certinfo: PPCurlSListArray;
|
|
end;
|
|
PCurlCertInfo = ^TCurlCertInfo;
|
|
|
|
/// low-level message information for libcurl library API
|
|
TCurlMsgRec = packed record
|
|
msg: TCurlMsg;
|
|
{$ifdef CPUX64}_align: array[0..3] of byte;{$endif}
|
|
easy_handle: TCurl;
|
|
data: packed record case byte of
|
|
0: (whatever: Pointer);
|
|
1: (result: TCurlResult);
|
|
end;
|
|
end;
|
|
PCurlMsgRec = ^TCurlMsgRec;
|
|
|
|
/// low-level file description event handler for libcurl library API
|
|
TCurlWaitFD = packed record
|
|
fd: TCurlSocket;
|
|
events: SmallInt;
|
|
revents: SmallInt;
|
|
{$ifdef CPUX64}_align: array[0..3] of byte;{$endif}
|
|
end;
|
|
PCurlWaitFD = ^TCurlWaitFD;
|
|
|
|
/// low-level write callback function signature for libcurl library API
|
|
curl_write_callback = function (buffer: PAnsiChar; size,nitems: integer;
|
|
outstream: pointer): integer; cdecl;
|
|
/// low-level read callback function signature for libcurl library API
|
|
curl_read_callback = function (buffer: PAnsiChar; size,nitems: integer;
|
|
instream: pointer): integer; cdecl;
|
|
|
|
curl_lock_data = (CURL_LOCK_DATA_NONE = 0,
|
|
CURL_LOCK_DATA_SHARE,
|
|
CURL_LOCK_DATA_COOKIE,
|
|
CURL_LOCK_DATA_DNS,
|
|
CURL_LOCK_DATA_SSL_SESSION,
|
|
CURL_LOCK_DATA_CONNECT,
|
|
CURL_LOCK_DATA_PSL,
|
|
CURL_LOCK_DATA_LAST);
|
|
|
|
curl_lock_access = (CURL_LOCK_ACCESS_NONE = 0,
|
|
CURL_LOCK_ACCESS_SHARED = 1,
|
|
CURL_LOCK_ACCESS_SINGLE = 2,
|
|
CURL_LOCK_ACCESS_LAST);
|
|
|
|
/// lock function signature for CURLSHOPT_LOCKFUNC
|
|
curl_lock_function = procedure (handle: TCurl; data: curl_lock_data;
|
|
locktype: curl_lock_access; userptr: pointer); cdecl;
|
|
/// unlock function signature for CURLSHOPT_UNLOCKFUNC
|
|
curl_unlock_function = procedure (handle: TCurl; data: curl_lock_data;
|
|
userptr: pointer); cdecl;
|
|
{$Z1}
|
|
|
|
|
|
var
|
|
/// low-level late binding functions access to the libcurl library API
|
|
// - ensure you called LibCurlInitialize or CurlIsAvailable functions to
|
|
// setup this global instance before using any of its internal functions
|
|
// - see also https://curl.haxx.se/libcurl/c/libcurl-multi.html interface
|
|
curl: packed record
|
|
/// hold a reference to the loaded library
|
|
// - PtrInt(Module)=0 before initialization, or PtrInt(Module) = -1
|
|
// on initialization failure, or PtrInt(Module)>0 if loaded
|
|
{$ifdef FPC}
|
|
Module: TLibHandle;
|
|
{$else}
|
|
Module: THandle;
|
|
{$endif FPC}
|
|
/// in case CurlEnableShare is called this array holds a
|
|
// critical section per curl_lock_data
|
|
share_cs: array[curl_lock_data] of TRTLCriticalSection;
|
|
/// global TCurlShare object, created by CurlEnableGlobalShare
|
|
globalShare: TCurlShare;
|
|
/// initialize the library
|
|
global_init: function(flags: TCurlGlobalInit): TCurlResult; cdecl;
|
|
/// finalize the library
|
|
global_cleanup: procedure; cdecl;
|
|
/// returns run-time libcurl version info
|
|
version_info: function(age: TCurlVersion): PCurlVersionInfo; cdecl;
|
|
// start a libcurl easy session
|
|
easy_init: function: pointer; cdecl;
|
|
/// set options for a curl easy handle
|
|
easy_setopt: function(curl: TCurl; option: TCurlOption): TCurlResult; cdecl varargs;
|
|
/// perform a blocking file transfer
|
|
easy_perform: function(curl: TCurl): TCurlResult; cdecl;
|
|
/// end a libcurl easy handle
|
|
easy_cleanup: procedure(curl: TCurl); cdecl;
|
|
/// extract information from a curl handle
|
|
easy_getinfo: function(curl: TCurl; info: TCurlInfo; out value): TCurlResult; cdecl;
|
|
/// clone a libcurl session handle
|
|
easy_duphandle: function(curl: TCurl): pointer; cdecl;
|
|
/// reset all options of a libcurl session handle
|
|
easy_reset: procedure(curl: TCurl); cdecl;
|
|
/// return string describing error code
|
|
easy_strerror: function(code: TCurlResult): PAnsiChar; cdecl;
|
|
/// add a string to an slist
|
|
slist_append: function(list: TCurlSList; s: PAnsiChar): TCurlSList; cdecl;
|
|
/// free an entire slist
|
|
slist_free_all: procedure(list: TCurlSList); cdecl;
|
|
/// create a shared object
|
|
share_init: function: pointer; cdecl;
|
|
/// clean up a shared object
|
|
share_cleanup: function(share_handle: TCurlShare): CURLSHcode; cdecl;
|
|
/// set options for a shared object
|
|
share_setopt: function(share: TCurlShare; option: CURLSHoption): CURLSHcode; cdecl varargs;
|
|
/// return the text description of an error code
|
|
share_strerror: function(code: CURLSHcode): PAnsiChar; cdecl;
|
|
{$ifdef LIBCURLMULTI} 12
|
|
/// add an easy handle to a multi session
|
|
multi_add_handle: function(mcurl: TCurlMulti; curl: TCurl): TCurlMultiCode; cdecl;
|
|
/// set data to associate with an internal socket
|
|
multi_assign: function(mcurl: TCurlMulti; socket: TCurlSocket; data: pointer): TCurlMultiCode; cdecl;
|
|
/// close down a multi session
|
|
multi_cleanup: function(mcurl: TCurlMulti): TCurlMultiCode; cdecl;
|
|
/// extracts file descriptor information from a multi handle
|
|
multi_fdset: function(mcurl: TCurlMulti; read, write, exec: PFDSet; out max: integer): TCurlMultiCode; cdecl;
|
|
/// read multi stack informationals
|
|
multi_info_read: function(mcurl: TCurlMulti; out msgsqueue: integer): PCurlMsgRec; cdecl;
|
|
/// create a multi handle
|
|
multi_init: function: TCurlMulti; cdecl;
|
|
/// reads/writes available data from each easy handle
|
|
multi_perform: function(mcurl: TCurlMulti; out runningh: integer): TCurlMultiCode; cdecl;
|
|
/// remove an easy handle from a multi session
|
|
multi_remove_handle: function(mcurl: TCurlMulti; curl: TCurl): TCurlMultiCode; cdecl;
|
|
/// set options for a curl multi handle
|
|
multi_setopt: function(mcurl: TCurlMulti; option: TCurlMultiOption): TCurlMultiCode; cdecl varargs;
|
|
/// reads/writes available data given an action
|
|
multi_socket_action: function(mcurl: TCurlMulti; socket: TCurlSocket; mask: Integer; out runningh: integer): TCurlMultiCode; cdecl;
|
|
/// reads/writes available data - deprecated call
|
|
multi_socket_all: function(mcurl: TCurlMulti; out runningh: integer): TCurlMultiCode; cdecl;
|
|
/// return string describing error code
|
|
multi_strerror: function(code: TCurlMultiCode): PAnsiChar; cdecl;
|
|
/// retrieve how long to wait for action before proceeding
|
|
multi_timeout: function(mcurl: TCurlMulti; out ms: integer): TCurlMultiCode; cdecl;
|
|
/// polls on all easy handles in a multi handle
|
|
multi_wait: function(mcurl: TCurlMulti; fds: PCurlWaitFD; fdscount: cardinal; ms: integer; out ret: integer): TCurlMultiCode; cdecl;
|
|
{$endif LIBCURLMULTI}
|
|
/// contains numerical information about the initialized libcurl instance
|
|
info: TCurlVersionInfo;
|
|
/// contains textual information about the initialized libcurl instance
|
|
infoText: string;
|
|
end;
|
|
|
|
/// initialize the libcurl API, accessible via the curl global variable
|
|
// - do nothing if the library has already been loaded
|
|
// - will raise ECurl exception on any loading issue
|
|
procedure LibCurlInitialize(engines: TCurlGlobalInit=[giAll];
|
|
const dllname: TFileName= LIBCURL_DLL);
|
|
|
|
/// return TRUE if a curl library is available
|
|
// - will load and initialize it, calling LibCurlInitialize if necessary,
|
|
// catching any exception during the process
|
|
function CurlIsAvailable: boolean;
|
|
|
|
/// Callback used by libcurl to write data; Usage:
|
|
// curl.easy_setopt(fHandle,coWriteFunction,@CurlWriteRawByteString);
|
|
// curl.easy_setopt(curlHandle,coFile,@curlRespBody);
|
|
// where curlRespBody should be a generic AnsiString/RawByteString, i.e.
|
|
// in practice a SockString or a RawByteString
|
|
function CurlWriteRawByteString(buffer: PAnsiChar; size,nitems: integer;
|
|
opaque: pointer): integer; cdecl;
|
|
|
|
/// enable libcurl multiple easy handles to share data
|
|
// - is called automatically during libcurl initialization
|
|
// - shared objects are: DNS cache, TLS session cache and connection cache
|
|
// - this way, each single transfer can take advantage of the context of the
|
|
// other transfer(s)
|
|
// - do nothing if the global share has already been enabled
|
|
// - see https://curl.se/libcurl/c/libcurl-share.html for details
|
|
function CurlEnableGlobalShare: boolean;
|
|
|
|
/// disable a global share for libcurl
|
|
// - is called automatically in finalization section
|
|
// - can be called on purpose, to ensure there is no active HTTP requests
|
|
// and prevent CURLSHE_IN_USE error
|
|
// - you can re-enable the libcurl global share by CurlEnableGlobalShare
|
|
function CurlDisableGlobalShare: CURLSHcode;
|
|
|
|
implementation
|
|
|
|
{$ifdef LIBCURLSTATIC}
|
|
|
|
{$ifdef FPC}
|
|
{$ifdef ANDROID}
|
|
{$ifdef CPUAARCH64}
|
|
{$L .\static\aarch64-android\libcurl.a}
|
|
{$endif CPUAARCH64}
|
|
{$ifdef CPUARM}
|
|
{$L .\static\arm-android\libcurl.a}
|
|
{$endif CPUARM}
|
|
{$linklib libz.so}
|
|
{$endif ANDROID}
|
|
|
|
function curl_global_init(flags: TCurlGlobalInit): TCurlResult; cdecl; external;
|
|
/// finalize the library
|
|
procedure curl_global_cleanup cdecl; external;
|
|
/// returns run-time libcurl version info
|
|
function curl_version_info(age: TCurlVersion): PCurlVersionInfo; cdecl; external;
|
|
// start a libcurl easy session
|
|
function curl_easy_init: pointer; cdecl; external;
|
|
/// set options for a curl easy handle
|
|
function curl_easy_setopt(curl: TCurl; option: TCurlOption): TCurlResult; cdecl varargs; external;
|
|
/// perform a blocking file transfer
|
|
function curl_easy_perform(curl: TCurl): TCurlResult; cdecl; external;
|
|
/// end a libcurl easy handle
|
|
procedure curl_easy_cleanup(curl: TCurl); cdecl; external;
|
|
/// extract information from a curl handle
|
|
function curl_easy_getinfo(curl: TCurl; info: TCurlInfo; out value): TCurlResult; cdecl; external;
|
|
/// clone a libcurl session handle
|
|
function curl_easy_duphandle(curl: TCurl): pointer; cdecl; external;
|
|
/// reset all options of a libcurl session handle
|
|
procedure curl_easy_reset(curl: TCurl); cdecl; external;
|
|
/// return string describing error code
|
|
function curl_easy_strerror(code: TCurlResult): PAnsiChar; cdecl; external;
|
|
/// add a string to an slist
|
|
function curl_slist_append(list: TCurlSList; s: PAnsiChar): TCurlSList; cdecl; external;
|
|
/// free an entire slist
|
|
procedure curl_slist_free_all(list: TCurlSList); cdecl; external;
|
|
/// create a shared object
|
|
function curl_share_init: pointer; cdecl; external;
|
|
/// clean up a shared object
|
|
function curl_share_cleanup(share_handle: TCurlShare): CURLSHcode; cdecl; external;
|
|
/// set options for a shared object
|
|
function curl_share_setopt(share: TCurlShare; option: CURLSHoption): CURLSHcode; cdecl varargs; external;
|
|
/// return string describing error code
|
|
function curl_share_strerror(code: CURLSHcode): PAnsiChar; cdecl; external;
|
|
{$ifdef LIBCURLMULTI}
|
|
/// add an easy handle to a multi session
|
|
function curl_multi_add_handle(mcurl: TCurlMulti; curl: TCurl): TCurlMultiCode; cdecl; external;
|
|
/// set data to associate with an internal socket
|
|
function curl_multi_assign(mcurl: TCurlMulti; socket: TCurlSocket; data: pointer): TCurlMultiCode; cdecl; external;
|
|
/// close down a multi session
|
|
function curl_multi_cleanup(mcurl: TCurlMulti): TCurlMultiCode; cdecl; external;
|
|
/// extracts file descriptor information from a multi handle
|
|
function curl_multi_fdset(mcurl: TCurlMulti; read, write, exec: PFDSet; out max: integer): TCurlMultiCode; cdecl; external;
|
|
/// read multi stack informationals
|
|
function curl_multi_info_read(mcurl: TCurlMulti; out msgsqueue: integer): PCurlMsgRec; cdecl; external;
|
|
/// create a multi handle
|
|
function curl_multi_init: TCurlMulti; cdecl; external;
|
|
/// reads/writes available data from each easy handle
|
|
function curl_multi_perform(mcurl: TCurlMulti; out runningh: integer): TCurlMultiCode; cdecl; external;
|
|
/// remove an easy handle from a multi session
|
|
function curl_multi_remove_handle(mcurl: TCurlMulti; curl: TCurl): TCurlMultiCode; cdecl; external;
|
|
/// set options for a curl multi handle
|
|
function curl_multi_setopt(mcurl: TCurlMulti; option: TCurlMultiOption): TCurlMultiCode; cdecl varargs; external;
|
|
/// reads/writes available data given an action
|
|
function curl_multi_socket_action(mcurl: TCurlMulti; socket: TCurlSocket; mask: Integer; out runningh: integer): TCurlMultiCode; cdecl; external;
|
|
/// reads/writes available data - deprecated call
|
|
function curl_multi_socket_all(mcurl: TCurlMulti; out runningh: integer): TCurlMultiCode; cdecl; external;
|
|
/// return string describing error code
|
|
function curl_multi_strerror(code: TCurlMultiCode): PAnsiChar; cdecl; external;
|
|
/// retrieve how long to wait for action before proceeding
|
|
function curl_multi_timeout(mcurl: TCurlMulti; out ms: integer): TCurlMultiCode; cdecl; external;
|
|
/// polls on all easy handles in a multi handle
|
|
function curl_multi_wait(mcurl: TCurlMulti; fds: PCurlWaitFD; fdscount: cardinal; ms: integer; out ret: integer): TCurlMultiCode; cdecl; external;
|
|
{$endif LIBCURLMULTI}
|
|
{$endif FPC}
|
|
var
|
|
curl_static_initialized: boolean;
|
|
{$endif LIBCURLSTATIC}
|
|
|
|
type
|
|
// some internal cross-compiler array of bytes string definition
|
|
SockString = {$ifdef HASCODEPAGE}RawByteString{$else}AnsiString{$endif};
|
|
// some internal cross-compiler PtrInt definition
|
|
{$ifndef FPC} PtrInt = {$ifdef CPU64}Int64{$else}integer{$endif}; {$endif}
|
|
|
|
function CurlWriteRawByteString(buffer: PAnsiChar; size,nitems: integer;
|
|
opaque: pointer): integer; cdecl;
|
|
var storage: ^SockString absolute opaque;
|
|
n: integer;
|
|
begin
|
|
if storage=nil then
|
|
result := 0 else begin
|
|
n := length(storage^);
|
|
result := size*nitems;
|
|
SetLength(storage^,n+result);
|
|
Move(buffer^,PPAnsiChar(opaque)^[n],result);
|
|
end;
|
|
end;
|
|
|
|
function CurlIsAvailable: boolean;
|
|
begin
|
|
{$ifdef LIBCURLSTATIC}
|
|
if not curl_static_initialized then
|
|
LibCurlInitialize;
|
|
result := true;
|
|
{$else}
|
|
try
|
|
if curl.Module=0 then
|
|
LibCurlInitialize;
|
|
result := PtrInt(curl.Module)>0;
|
|
except
|
|
result := false;
|
|
end;
|
|
{$endif LIBCURLSTATIC}
|
|
end;
|
|
|
|
procedure LibCurlInitialize(engines: TCurlGlobalInit; const dllname: TFileName);
|
|
{$ifndef LIBCURLSTATIC}
|
|
var P: PPointer;
|
|
api: integer;
|
|
h: {$ifdef FPC}TLibHandle{$else}THandle{$endif FPC};
|
|
const NAMES: array[0..{$ifdef LIBCURLMULTI}30{$else}16{$endif}] of string = (
|
|
'global_init','global_cleanup','version_info',
|
|
'easy_init','easy_setopt','easy_perform','easy_cleanup','easy_getinfo',
|
|
'easy_duphandle','easy_reset','easy_strerror','slist_append','slist_free_all',
|
|
'share_init', 'share_cleanup','share_setopt', 'share_strerror'
|
|
{$ifdef LIBCURLMULTI},
|
|
'multi_add_handle','multi_assign','multi_cleanup','multi_fdset',
|
|
'multi_info_read','multi_init','multi_perform','multi_remove_handle',
|
|
'multi_setopt','multi_socket_action','multi_socket_all','multi_strerror',
|
|
'multi_timeout','multi_wait'
|
|
{$endif LIBCURLMULTI} );
|
|
{$endif}
|
|
begin
|
|
EnterCriticalSection(SynSockCS);
|
|
try
|
|
{$ifdef LIBCURLSTATIC}
|
|
curl_static_initialized := true;
|
|
curl.global_init := @curl_global_init;
|
|
curl.global_cleanup := @curl_global_cleanup;
|
|
curl.version_info := @curl_version_info;
|
|
curl.easy_init := @curl_easy_init;
|
|
curl.easy_setopt := @curl_easy_setopt;
|
|
curl.easy_perform := @curl_easy_perform;
|
|
curl.easy_cleanup := @curl_easy_cleanup;
|
|
curl.easy_getinfo := @curl_easy_getinfo;
|
|
curl.easy_duphandle := @curl_easy_duphandle;
|
|
curl.easy_reset := @curl_easy_reset;
|
|
curl.easy_strerror := @curl_easy_strerror;
|
|
curl.slist_append := @curl_slist_append;
|
|
curl.slist_free_all := @curl_slist_free_all;
|
|
curl.share_init := @curl_share_init;
|
|
curl.share_cleanup := @curl_share_cleanup;
|
|
curl.share_setopt := @curl_share_setopt;
|
|
curl.share_strerror := @curl_share_strerror;
|
|
{$ifdef LIBCURLMULTI}
|
|
curl.multi_add_handle := @curl_multi_add_handle;
|
|
curl.multi_assign := @curl_multi_assign;
|
|
curl.multi_cleanup := @curl_multi_cleanup;
|
|
curl.multi_fdset := @curl_multi_fdset;
|
|
curl.multi_info_read := @curl_multi_info_read;
|
|
curl.multi_init := @curl_multi_init;
|
|
curl.multi_perform := @curl_multi_perform;
|
|
curl.multi_remove_handle := @curl_multi_remove_handle;
|
|
curl.multi_setopt := @curl_multi_setopt;
|
|
curl.multi_socket_action := @curl_multi_socket_action;
|
|
curl.multi_socket_all := @curl_multi_socket_all;
|
|
curl.multi_strerror := @curl_multi_strerror;
|
|
curl.multi_timeout := @curl_multi_timeout;
|
|
curl.multi_wait := @curl_multi_wait;
|
|
{$endif LIBCURLMULTI}
|
|
{$else}
|
|
h := 0;
|
|
if curl.Module=0 then // try to load libcurl once
|
|
try
|
|
{$ifdef MSWINDOWS}
|
|
h := SafeLoadLibrary(ExtractFilePath(paramstr(0))+dllname);
|
|
if h=0 then h := SafeLoadLibrary(dllname);
|
|
{$else}
|
|
h := LoadLibrary(dllname);
|
|
{$endif}
|
|
{$ifdef Darwin} // another common names on MacOS
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl.4.dylib');
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl.3.dylib');
|
|
{$else}
|
|
{$ifdef Linux} // another common names on POSIX
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl.so.4');
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl.so.3');
|
|
// for latest Linux Mint and other similar distros using gnutls
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl-gnutls.so.4');
|
|
if h=0 then
|
|
h := LoadLibrary('libcurl-gnutls.so.3');
|
|
{$endif Linux}
|
|
{$endif Darwin}
|
|
if h=0 then
|
|
raise ECurl.CreateFmt('Unable to find %s'{$ifdef Linux}+
|
|
': try e.g. sudo apt-get install libcurl3'{$ifdef CPUX86}+':i386'{$endif}
|
|
{$endif Linux},[dllname]);
|
|
P := @@curl.global_init;
|
|
for api := low(NAMES) to high(NAMES) do begin
|
|
P^ := GetProcAddress(h,PChar('curl_'+NAMES[api]));
|
|
if P^=nil then
|
|
raise ECurl.CreateFmt('Unable to find %s() in %s',[NAMES[api],LIBCURL_DLL]);
|
|
inc(P);
|
|
end;
|
|
curl.Module := h;
|
|
except
|
|
on E: Exception do begin
|
|
if h<>0 then
|
|
FreeLibrary(h);
|
|
PtrInt(curl.Module) := -1; // <>0 so that won't try to load any more
|
|
raise;
|
|
end;
|
|
end;
|
|
{$endif LIBCURLSTATIC}
|
|
curl.global_init(engines);
|
|
curl.info := curl.version_info(cvFour)^;
|
|
curl.infoText := format('%s version %s',[LIBCURL_DLL,curl.info.version]);
|
|
if curl.info.ssl_version<>nil then
|
|
curl.infoText := format('%s using %s',[curl.infoText,curl.info.ssl_version]);
|
|
curl.globalShare := nil;
|
|
CurlEnableGlobalShare; // won't hurt, and may benefit even for the OS
|
|
// api := 0; with curl.info do while protocols[api]<>nil do begin
|
|
// write(protocols[api], ' '); inc(api); end; writeln(#13#10,curl.infoText);
|
|
finally
|
|
LeaveCriticalSection(SynSockCS);
|
|
end;
|
|
end;
|
|
|
|
procedure curlShareLock(handle: TCurl; data: curl_lock_data;
|
|
locktype: curl_lock_access; userptr: pointer); cdecl;
|
|
begin
|
|
EnterCriticalSection(curl.share_cs[data]);
|
|
end;
|
|
|
|
procedure curlShareUnLock(handle: TCurl; data: curl_lock_data;
|
|
userptr: pointer); cdecl;
|
|
begin
|
|
LeaveCriticalSection(curl.share_cs[data]);
|
|
end;
|
|
|
|
function CurlEnableGlobalShare: boolean;
|
|
var
|
|
d: curl_lock_data;
|
|
begin
|
|
result := false;
|
|
if not CurlIsAvailable or (curl.globalShare<>nil) then
|
|
exit; // not available, or already shared
|
|
curl.globalShare := curl.share_init;
|
|
if curl.globalShare = nil then
|
|
exit; // something went wrong (out of memory, etc.) and therefore the share object was not created
|
|
for d := low(d) to high(d) do
|
|
InitializeCriticalSection(curl.share_cs[d]);
|
|
curl.share_setopt(curl.globalShare,CURLSHOPT_LOCKFUNC,@curlShareLock);
|
|
curl.share_setopt(curl.globalShare,CURLSHOPT_UNLOCKFUNC,@curlShareUnLock);
|
|
curl.share_setopt(curl.globalShare,CURLSHOPT_SHARE,CURL_LOCK_DATA_DNS);
|
|
curl.share_setopt(curl.globalShare,CURLSHOPT_SHARE,CURL_LOCK_DATA_SSL_SESSION);
|
|
// CURL_LOCK_DATA_CONNECT triggers GPF e.g. on Debian Burster 10
|
|
if curl.info.version_num>=$00074400 then // seems to be fixed in 7.68
|
|
// see https://github.com/curl/curl/issues/4544
|
|
curl.share_setopt(curl.globalShare,CURLSHOPT_SHARE,CURL_LOCK_DATA_CONNECT);
|
|
// CURL_LOCK_DATA_CONNECT triggers GPF on Debian Burster 10
|
|
result := true;
|
|
end;
|
|
|
|
function CurlDisableGlobalShare: CURLSHcode;
|
|
var
|
|
d: curl_lock_data;
|
|
begin
|
|
result := CURLSHE_OK;
|
|
if curl.globalShare = nil then
|
|
exit; // already disabled
|
|
result := curl.share_cleanup(curl.globalShare);
|
|
if result = CURLSHE_OK then
|
|
curl.globalShare := nil;
|
|
for d := low(d) to high(d) do
|
|
DeleteCriticalSection(curl.share_cs[d]);
|
|
end;
|
|
|
|
initialization
|
|
{$ifdef LIBCURLSTATIC}
|
|
//LibCurlInitialize;
|
|
{$endif LIBCURLSTATIC}
|
|
|
|
finalization
|
|
{$ifdef LIBCURLSTATIC}
|
|
if curl_static_initialized then begin
|
|
CurlDisableGlobalShare;
|
|
curl.global_cleanup;
|
|
end;
|
|
{$else}
|
|
if PtrInt(curl.Module)>0 then begin
|
|
CurlDisableGlobalShare;
|
|
curl.global_cleanup;
|
|
FreeLibrary(curl.Module);
|
|
end;
|
|
{$endif LIBCURLSTATIC}
|
|
|
|
end.
|