From 76d2f02e5ad08a50684810436555f44b26e2e620 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Sun, 4 Aug 2024 22:26:28 +0200 Subject: [PATCH 01/21] Put class declaration into header file. Also change the DRIVER_MODIFICATION number even though I'm not sure if this should be done since this shouldn't change anything in the driver per se. --- urlApp/src/URLDriver.cpp | 34 +--------------------------------- urlApp/src/URLDriver.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 urlApp/src/URLDriver.h diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 1d0efa4..b9c781b 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -22,43 +22,11 @@ #include using namespace Magick; -#include "ADDriver.h" - #include - -#define DRIVER_VERSION 2 -#define DRIVER_REVISION 3 -#define DRIVER_MODIFICATION 0 +#include static const char *driverName = "URLDriver"; -/** URL driver; reads images from URLs, such as Web cameras and Axis video servers, but also files, etc. */ -class URLDriver : public ADDriver { -public: - URLDriver(const char *portName, int maxBuffers, size_t maxMemory, - int priority, int stackSize); - - /* These are the methods that we override from ADDriver */ - virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); - virtual void report(FILE *fp, int details); - void URLTask(); /**< Should be private, but gets called from C, so must be public */ - -protected: - int URLName; - #define FIRST_URL_DRIVER_PARAM URLName - -private: - /* These are the methods that are new to this class */ - virtual asynStatus readImage(); - - /* Our data */ - Image image; - epicsEventId startEventId; - epicsEventId stopEventId; -}; - -#define URLNameString "URL_NAME" - asynStatus URLDriver::readImage() { diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h new file mode 100644 index 0000000..736e36a --- /dev/null +++ b/urlApp/src/URLDriver.h @@ -0,0 +1,32 @@ +#include "ADDriver.h" + +#define DRIVER_VERSION 2 +#define DRIVER_REVISION 3 +#define DRIVER_MODIFICATION 1 + +/** URL driver; reads images from URLs, such as Web cameras and Axis video servers, but also files, etc. */ +class URLDriver : public ADDriver { +public: + URLDriver(const char *portName, int maxBuffers, size_t maxMemory, + int priority, int stackSize); + + /* These are the methods that we override from ADDriver */ + virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); + virtual void report(FILE *fp, int details); + void URLTask(); /**< Should be private, but gets called from C, so must be public */ + +protected: + int URLName; + #define FIRST_URL_DRIVER_PARAM URLName + +private: + /* These are the methods that are new to this class */ + virtual asynStatus readImage(); + + /* Our data */ + Image image; + epicsEventId startEventId; + epicsEventId stopEventId; +}; + +#define URLNameString "URL_NAME" \ No newline at end of file From 44d49527073f4cdd1492cd88b6bf39e6982f54a2 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Sun, 4 Aug 2024 22:32:09 +0200 Subject: [PATCH 02/21] Style: remove invisible ^L characters --- urlApp/src/URLDriver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index b9c781b..2c3652d 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -256,7 +256,7 @@ void URLDriver::URLTask() } } - + /** Called when asyn clients call pasynInt32->write(). * This function performs actions for some parameters, including ADAcquire, ADColorMode, etc. * For all parameters it sets the value in the parameter library and calls any registered callbacks.. @@ -305,7 +305,7 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) - + /** Report status of the driver. * Prints details about the driver if details>0. * It then calls the ADDriver::report() method. From 73fbdcef2104ca9461b890321217f34fab5ba4fc Mon Sep 17 00:00:00 2001 From: marcofilho Date: Tue, 6 Aug 2024 23:10:50 +0200 Subject: [PATCH 03/21] Add curl compilation and runtime option Add WITH_CURL compilation option. If compiled with WITH_CURL = YES, CurlUsage.template file will be added and it's records will be created. curl.h will be included. First record - UseCurl - should futurely toggle curl usage in the Driver. --- urlApp/Db/CurlUsage.template | 12 ++++++++++++ urlApp/Db/Makefile | 3 +++ urlApp/src/Makefile | 5 +++++ urlApp/src/URLDriver.cpp | 4 ++++ urlApp/src/URLDriver.h | 14 +++++++++++++- 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 urlApp/Db/CurlUsage.template diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template new file mode 100644 index 0000000..9a096b8 --- /dev/null +++ b/urlApp/Db/CurlUsage.template @@ -0,0 +1,12 @@ +# Records to toggle curl functionalities + +record (bo, "$(P)$(R)UseCurl") +{ + field(DESC, "Toggle curl usage") + field(DTYP, "asynInt32") + field(PINI, "YES") + field(ZNAM, "NO") + field(ONAM, "YES") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))USE_CURL") + info(autosaveFields, "VAL") +} diff --git a/urlApp/Db/Makefile b/urlApp/Db/Makefile index 9c89075..e088e4f 100644 --- a/urlApp/Db/Makefile +++ b/urlApp/Db/Makefile @@ -12,6 +12,9 @@ include $(TOP)/configure/CONFIG # databases, templates, substitutions like this DB += URLDriver.template +ifeq ($(WITH_CURL), YES) + DB += CurlUsage.template +endif #---------------------------------------------------- # If .db template is not named *.template add diff --git a/urlApp/src/Makefile b/urlApp/src/Makefile index 735bc62..df39585 100644 --- a/urlApp/src/Makefile +++ b/urlApp/src/Makefile @@ -43,6 +43,11 @@ ifeq ($(WITH_GRAPHICSMAGICK), YES) include $(ADCORE)/ADApp/commonLibraryMakefile endif +ifeq ($(WITH_CURL),YES) + USR_CXXFLAGS += -DADURL_USE_CURL + USR_SYS_LIBS += curl +endif + #============================= include $(TOP)/configure/RULES diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 2c3652d..b1f7bbf 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -371,6 +371,10 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(URLNameString, asynParamOctet, &URLName); + #ifdef ADURL_USE_CURL + createParam(UseCurlString, asynParamInt32, &useCurl); + #endif + /* Set some default values for parameters */ status = setStringParam (ADManufacturer, "URL Driver"); status |= setStringParam (ADModel, "GraphicsMagick"); diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index 736e36a..ed8d3ee 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -1,5 +1,9 @@ #include "ADDriver.h" +#ifdef ADURL_USE_CURL + #include +#endif + #define DRIVER_VERSION 2 #define DRIVER_REVISION 3 #define DRIVER_MODIFICATION 1 @@ -19,6 +23,10 @@ class URLDriver : public ADDriver { int URLName; #define FIRST_URL_DRIVER_PARAM URLName + #ifdef ADURL_USE_CURL + int useCurl; + #endif + private: /* These are the methods that are new to this class */ virtual asynStatus readImage(); @@ -29,4 +37,8 @@ class URLDriver : public ADDriver { epicsEventId stopEventId; }; -#define URLNameString "URL_NAME" \ No newline at end of file +#define URLNameString "URL_NAME" + +#ifdef ADURL_USE_CURL + #define UseCurlString "USE_CURL" +#endif \ No newline at end of file From e11a8bf76602525883eb7266914e26fa3b348bfe Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 20:46:37 +0200 Subject: [PATCH 04/21] Add curl initialization option. --- urlApp/src/URLDriver.cpp | 11 +++++++++++ urlApp/src/URLDriver.h | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index b1f7bbf..a6884a9 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -27,6 +27,16 @@ using namespace Magick; static const char *driverName = "URLDriver"; +#ifdef ADURL_USE_CURL +void URLDriver::initializeCurl(){ + curl_global_init(CURL_GLOBAL_DEFAULT); + this->curl = curl_easy_init(); + if (!curl){ + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: ERROR, cannot initialize curl pointer.\n", driverName, __func__); + } +} +#endif asynStatus URLDriver::readImage() { @@ -373,6 +383,7 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, #ifdef ADURL_USE_CURL createParam(UseCurlString, asynParamInt32, &useCurl); + this->initializeCurl(); #endif /* Set some default values for parameters */ diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index ed8d3ee..e9d2ff3 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -19,12 +19,18 @@ class URLDriver : public ADDriver { virtual void report(FILE *fp, int details); void URLTask(); /**< Should be private, but gets called from C, so must be public */ + #ifdef ADURL_USE_CURL + void initializeCurl(); + #endif + protected: int URLName; #define FIRST_URL_DRIVER_PARAM URLName #ifdef ADURL_USE_CURL int useCurl; + CURL *curl = NULL; + CURLcode res; #endif private: From 377795a46e36a0ff64253368acd8a867ea7794f5 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 21:33:35 +0200 Subject: [PATCH 05/21] Add record to set CURLOPT_HTTPAUTH --- urlApp/Db/CurlUsage.template | 29 +++++++++++++++++++++++++++++ urlApp/src/URLDriver.cpp | 14 +++++++++++++- urlApp/src/URLDriver.h | 7 +++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index 9a096b8..f356135 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -10,3 +10,32 @@ record (bo, "$(P)$(R)UseCurl") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))USE_CURL") info(autosaveFields, "VAL") } + +record (mbbo, "$(P)$(R)CurlOptHTTPAuth") +{ + field(DESC, "Set CURLOPT_HTTPAUTH") + field(DTYP, "asynInt32") + field(ZRST, "CURLAUTH_BASIC") + field(ZRVL, "0") + field(ONST, "CURLAUTH_DIGEST") + field(ONVL, "1") + field(TWST, "CURLAUTH_DIGEST_IE") + field(TWVL, "2") + field(THST, "CURLAUTH_BEARER") + field(THVL, "3") + field(FRST, "CURLAUTH_NEGOTIATE") + field(FRVL, "4") + field(FVST, "CURLAUTH_NTLM") + field(FVVL, "5") + field(SXST, "CURLAUTH_NTLM_WB") + field(SXVL, "6") + field(SVST, "CURLAUTH_ANY") + field(SVVL, "7") + field(EIST, "CURLAUTH_ANYSAFE") + field(EIVL, "8") + field(NIST, "CURLAUTH_ONLY") + field(NIVL, "9") + field(TEST, "CURLAUTH_AWS_SIGV4") + field(TEVL, "10") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_HTTPAUTH") +} \ No newline at end of file diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index a6884a9..abc24b1 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -277,6 +277,9 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) int function = pasynUser->reason; int adstatus; asynStatus status = asynSuccess; + #ifdef ADURL_USE_CURL + int itemp; + #endif /* Set the parameter and readback in the parameter library. This may be overwritten when we read back the * status at the end, but that's OK */ @@ -294,6 +297,11 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) /* Send the stop event */ epicsEventSignal(this->stopEventId); } + #ifdef ADURL_USE_CURL + } else if (function==curlOptHttpAuth) { + getIntegerParam(curlOptHttpAuth, &itemp); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CurlHttpOptions[itemp]); + #endif } else { /* If this parameter belongs to a base class call its method */ if (function < FIRST_URL_DRIVER_PARAM) status = ADDriver::writeInt32(pasynUser, value); @@ -382,7 +390,11 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(URLNameString, asynParamOctet, &URLName); #ifdef ADURL_USE_CURL - createParam(UseCurlString, asynParamInt32, &useCurl); + createParam(UseCurlString, asynParamInt32, &useCurl); + createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); + + setIntegerParam(useCurl, 0); + setIntegerParam(curlOptHttpAuth, 0); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index e9d2ff3..b5423f4 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -29,8 +29,14 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL int useCurl; + int curlOptHttpAuth; CURL *curl = NULL; CURLcode res; + + /*Array to translate CurlHttpAuth options*/ + long unsigned int CurlHttpOptions [11] = {CURLAUTH_BASIC, CURLAUTH_DIGEST, CURLAUTH_DIGEST_IE, CURLAUTH_BEARER, + CURLAUTH_NEGOTIATE, CURLAUTH_NTLM, CURLAUTH_NTLM_WB, CURLAUTH_ANY, + CURLAUTH_ANYSAFE, CURLAUTH_ONLY, CURLAUTH_AWS_SIGV4}; #endif private: @@ -47,4 +53,5 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL #define UseCurlString "USE_CURL" + #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" #endif \ No newline at end of file From 39d9b34239d15453de5b9db4db9c5314282a1349 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 21:51:05 +0200 Subject: [PATCH 06/21] Add record to set CURLOPT_SSL_VERIFYHOST --- urlApp/Db/CurlUsage.template | 13 +++++++++++++ urlApp/src/URLDriver.cpp | 13 +++++++++---- urlApp/src/URLDriver.h | 6 ++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index f356135..36e5696 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -38,4 +38,17 @@ record (mbbo, "$(P)$(R)CurlOptHTTPAuth") field(TEST, "CURLAUTH_AWS_SIGV4") field(TEVL, "10") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_HTTPAUTH") +} + +record (mbbo, "$(P)$(R)CurlOptSSLVerifyHost") +{ + field(DESC, "Verify Host SSL certificate") + field(DTYP, "asynInt32") + field(ZRST, "NO") + field(ZRVL, "0") + field(ONST, "YES") + field(ONVL, "1") + field(TWST, "YES") + field(TWVL, "2") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYHOST") } \ No newline at end of file diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index abc24b1..ecb08b9 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -301,6 +301,9 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) } else if (function==curlOptHttpAuth) { getIntegerParam(curlOptHttpAuth, &itemp); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CurlHttpOptions[itemp]); + } else if (function==curlOptSSLVerifyHost) { + getIntegerParam(curlOptSSLVerifyHost, &itemp); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)itemp); #endif } else { /* If this parameter belongs to a base class call its method */ @@ -390,11 +393,13 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(URLNameString, asynParamOctet, &URLName); #ifdef ADURL_USE_CURL - createParam(UseCurlString, asynParamInt32, &useCurl); - createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); + createParam(UseCurlString, asynParamInt32, &useCurl); + createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); + createParam(CurlOptSSLVerifyHostString, asynParamInt32, &curlOptSSLVerifyHost); - setIntegerParam(useCurl, 0); - setIntegerParam(curlOptHttpAuth, 0); + setIntegerParam(useCurl, 0); + setIntegerParam(curlOptHttpAuth, 0); + setIntegerParam(curlOptSSLVerifyHost, 2L); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index b5423f4..442baae 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -30,6 +30,7 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL int useCurl; int curlOptHttpAuth; + int curlOptSSLVerifyHost; CURL *curl = NULL; CURLcode res; @@ -52,6 +53,7 @@ class URLDriver : public ADDriver { #define URLNameString "URL_NAME" #ifdef ADURL_USE_CURL - #define UseCurlString "USE_CURL" - #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" + #define UseCurlString "USE_CURL" + #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" + #define CurlOptSSLVerifyHostString "ASYN_CURLOPT_SSL_VERIFYHOST" #endif \ No newline at end of file From 702fc78102d80a085f14324c6b2319ac1981c985 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 22:01:50 +0200 Subject: [PATCH 07/21] Add record for setting CURLOPT_SSL_VERIFYPEER --- urlApp/Db/CurlUsage.template | 10 ++++++++++ urlApp/src/URLDriver.cpp | 5 +++++ urlApp/src/URLDriver.h | 2 ++ 3 files changed, 17 insertions(+) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index 36e5696..ab396b7 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -51,4 +51,14 @@ record (mbbo, "$(P)$(R)CurlOptSSLVerifyHost") field(TWST, "YES") field(TWVL, "2") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYHOST") +} + +record (bo, "$(P)$(R)CurlOptSSLVerifyPeer") +{ + field(DESC, "Verify SSL peer") + field(DTYP, "asynInt32") + field(PINI, "YES") + field(ZNAM, "NO") + field(ONAM, "YES") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYPEER") } \ No newline at end of file diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index ecb08b9..65dbddf 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -304,6 +304,9 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) } else if (function==curlOptSSLVerifyHost) { getIntegerParam(curlOptSSLVerifyHost, &itemp); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)itemp); + } else if (function==curlOptSSLVerifyPeer) { + getIntegerParam(curlOptSSLVerifyPeer, &itemp); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, itemp); #endif } else { /* If this parameter belongs to a base class call its method */ @@ -396,10 +399,12 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(UseCurlString, asynParamInt32, &useCurl); createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); createParam(CurlOptSSLVerifyHostString, asynParamInt32, &curlOptSSLVerifyHost); + createParam(CurlOptSSLVerifyPeerString, asynParamInt32, &curlOptSSLVerifyPeer); setIntegerParam(useCurl, 0); setIntegerParam(curlOptHttpAuth, 0); setIntegerParam(curlOptSSLVerifyHost, 2L); + setIntegerParam(curlOptSSLVerifyPeer, 1); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index 442baae..a8ae594 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -31,6 +31,7 @@ class URLDriver : public ADDriver { int useCurl; int curlOptHttpAuth; int curlOptSSLVerifyHost; + int curlOptSSLVerifyPeer; CURL *curl = NULL; CURLcode res; @@ -56,4 +57,5 @@ class URLDriver : public ADDriver { #define UseCurlString "USE_CURL" #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" #define CurlOptSSLVerifyHostString "ASYN_CURLOPT_SSL_VERIFYHOST" + #define CurlOptSSLVerifyPeerString "ASYN_CURLOPT_SSL_VERIFYPEER" #endif \ No newline at end of file From 308d6661b45f4a0d4301e56a2a4db87e1e48d1ac Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 22:24:47 +0200 Subject: [PATCH 08/21] Get rid of unnecessary itemp variable. No need to use getIntegerParam in this context if we already have the value argument. --- urlApp/src/URLDriver.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 65dbddf..1d5a4c8 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -277,9 +277,6 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) int function = pasynUser->reason; int adstatus; asynStatus status = asynSuccess; - #ifdef ADURL_USE_CURL - int itemp; - #endif /* Set the parameter and readback in the parameter library. This may be overwritten when we read back the * status at the end, but that's OK */ @@ -299,14 +296,11 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) } #ifdef ADURL_USE_CURL } else if (function==curlOptHttpAuth) { - getIntegerParam(curlOptHttpAuth, &itemp); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CurlHttpOptions[itemp]); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CurlHttpOptions[value]); } else if (function==curlOptSSLVerifyHost) { - getIntegerParam(curlOptSSLVerifyHost, &itemp); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)itemp); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)value); } else if (function==curlOptSSLVerifyPeer) { - getIntegerParam(curlOptSSLVerifyPeer, &itemp); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, itemp); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, value); #endif } else { /* If this parameter belongs to a base class call its method */ From 2906040d0a94ac8f4fb448c74ca9429837dc8f14 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 22:49:11 +0200 Subject: [PATCH 09/21] Add record to set CURLOPT_USERNAME (beware) Using this along with to-be-added CURLOPT_PASSWORD would expose credentials in the network. I leave it in the driver as a means to completely express the API functionalities, but there should be better ways of setting your credentials if you mean to. Probably will add a configuration file for that in the future. --- urlApp/Db/CurlUsage.template | 9 +++++++++ urlApp/src/URLDriver.cpp | 26 ++++++++++++++++++++++++++ urlApp/src/URLDriver.h | 6 +++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index ab396b7..5e8b04e 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -61,4 +61,13 @@ record (bo, "$(P)$(R)CurlOptSSLVerifyPeer") field(ZNAM, "NO") field(ONAM, "YES") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYPEER") +} + +record(waveform, "$(P)$(R)CurlOptUserName") +{ + field(DESC, "Username for auth. Try not to use this.") + field(DTYP, "asynOctetWrite") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_USERNAME") + field(FTVL, "CHAR") + field(NELM, "128") } \ No newline at end of file diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 1d5a4c8..00577c1 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -321,7 +321,31 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) return status; } +#ifdef ADURL_USE_CURL +asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual) +{ + + int addr = 0; + int function = pasynUser->reason; + int status = 0; + char userName[MAXCURLSTRCHARS] = {'\0'}; + + status |= setStringParam(addr, function, (char *)value); + + if (function < FIRST_URL_DRIVER_PARAM) { + status |= ADDriver::writeOctet(pasynUser, value, nChars, nActual); + } else if (function == curlOptUserName) { + getStringParam(curlOptUserName, MAXCURLSTRCHARS, userName); + curl_easy_setopt(curl, CURLOPT_USERNAME, userName); + } + + callParamCallbacks(addr); + *nActual = nChars; + return (asynStatus)status; + +} +#endif /** Report status of the driver. @@ -394,11 +418,13 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); createParam(CurlOptSSLVerifyHostString, asynParamInt32, &curlOptSSLVerifyHost); createParam(CurlOptSSLVerifyPeerString, asynParamInt32, &curlOptSSLVerifyPeer); + createParam(CurlOptUserNameString, asynParamOctet, &curlOptUserName); setIntegerParam(useCurl, 0); setIntegerParam(curlOptHttpAuth, 0); setIntegerParam(curlOptSSLVerifyHost, 2L); setIntegerParam(curlOptSSLVerifyPeer, 1); + setStringParam(curlOptUserName, "\0"); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index a8ae594..cc15519 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -20,6 +20,7 @@ class URLDriver : public ADDriver { void URLTask(); /**< Should be private, but gets called from C, so must be public */ #ifdef ADURL_USE_CURL + virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual); void initializeCurl(); #endif @@ -32,6 +33,8 @@ class URLDriver : public ADDriver { int curlOptHttpAuth; int curlOptSSLVerifyHost; int curlOptSSLVerifyPeer; + int curlOptUserName; + #define MAXCURLSTRCHARS 128 CURL *curl = NULL; CURLcode res; @@ -58,4 +61,5 @@ class URLDriver : public ADDriver { #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" #define CurlOptSSLVerifyHostString "ASYN_CURLOPT_SSL_VERIFYHOST" #define CurlOptSSLVerifyPeerString "ASYN_CURLOPT_SSL_VERIFYPEER" -#endif \ No newline at end of file + #define CurlOptUserNameString "ASYN_CURLOPT_USERNAME" +#endif From 0176dc2336dadc57c76ee20830058394639a8cbc Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 22:57:41 +0200 Subject: [PATCH 10/21] Add record for setting CURLOPT_PASSWORD (beware) Same warning about last commit: this shouldn't be used but it's left here as an option for quick testing and better expressing the API functionalities. For actual credentials usage future records should add a path for a configuration file containing the credentials. --- urlApp/Db/CurlUsage.template | 9 +++++++++ urlApp/src/URLDriver.cpp | 11 ++++++++--- urlApp/src/URLDriver.h | 2 ++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index 5e8b04e..9b48adb 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -70,4 +70,13 @@ record(waveform, "$(P)$(R)CurlOptUserName") field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_USERNAME") field(FTVL, "CHAR") field(NELM, "128") +} + +record(waveform, "$(P)$(R)CurlOptPassword") +{ + field(DESC, "Password for auth. Try not to use this.") + field(DTYP, "asynOctetWrite") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_PASSWORD") + field(FTVL, "CHAR") + field(NELM, "128") } \ No newline at end of file diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 00577c1..8f0e646 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -328,7 +328,7 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t int addr = 0; int function = pasynUser->reason; int status = 0; - char userName[MAXCURLSTRCHARS] = {'\0'}; + char param[MAXCURLSTRCHARS] = {'\0'}; status |= setStringParam(addr, function, (char *)value); @@ -336,8 +336,11 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t status |= ADDriver::writeOctet(pasynUser, value, nChars, nActual); } else if (function == curlOptUserName) { - getStringParam(curlOptUserName, MAXCURLSTRCHARS, userName); - curl_easy_setopt(curl, CURLOPT_USERNAME, userName); + getStringParam(curlOptUserName, MAXCURLSTRCHARS, param); + curl_easy_setopt(curl, CURLOPT_USERNAME, param); + } else if (function == curlOptPassword) { + getStringParam(curlOptPassword, MAXCURLSTRCHARS, param); + curl_easy_setopt(curl, CURLOPT_PASSWORD, param); } callParamCallbacks(addr); @@ -419,12 +422,14 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, createParam(CurlOptSSLVerifyHostString, asynParamInt32, &curlOptSSLVerifyHost); createParam(CurlOptSSLVerifyPeerString, asynParamInt32, &curlOptSSLVerifyPeer); createParam(CurlOptUserNameString, asynParamOctet, &curlOptUserName); + createParam(CurlOptPasswordString, asynParamOctet, &curlOptPassword); setIntegerParam(useCurl, 0); setIntegerParam(curlOptHttpAuth, 0); setIntegerParam(curlOptSSLVerifyHost, 2L); setIntegerParam(curlOptSSLVerifyPeer, 1); setStringParam(curlOptUserName, "\0"); + setStringParam(curlOptPassword, "\0"); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index cc15519..d8d4204 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -34,6 +34,7 @@ class URLDriver : public ADDriver { int curlOptSSLVerifyHost; int curlOptSSLVerifyPeer; int curlOptUserName; + int curlOptPassword; #define MAXCURLSTRCHARS 128 CURL *curl = NULL; CURLcode res; @@ -62,4 +63,5 @@ class URLDriver : public ADDriver { #define CurlOptSSLVerifyHostString "ASYN_CURLOPT_SSL_VERIFYHOST" #define CurlOptSSLVerifyPeerString "ASYN_CURLOPT_SSL_VERIFYPEER" #define CurlOptUserNameString "ASYN_CURLOPT_USERNAME" + #define CurlOptPasswordString "ASYN_CURLOPT_PASSWORD" #endif From 78d9c2cf5a895d2477e9f019cb8088155c26e463 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 7 Aug 2024 23:21:28 +0200 Subject: [PATCH 11/21] Add records to set config filepath Should be used to load the file contents and put them into the correct parameters. Uses a very similar mechanism from NDFile.template and functions from asynNDArrayDriver. For now it just checks if the file exists and is accessible, but not it's contents. --- urlApp/Db/CurlConfigFile.template | 91 +++++++++++++++++++++++++++++++ urlApp/Db/CurlUsage.template | 2 + urlApp/Db/Makefile | 1 + urlApp/src/URLDriver.cpp | 45 ++++++++++++++- urlApp/src/URLDriver.h | 8 +++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 urlApp/Db/CurlConfigFile.template diff --git a/urlApp/Db/CurlConfigFile.template b/urlApp/Db/CurlConfigFile.template new file mode 100644 index 0000000..806d2f0 --- /dev/null +++ b/urlApp/Db/CurlConfigFile.template @@ -0,0 +1,91 @@ +#=================================================================# +# Template file: CurlConfigFile.template +# Made to load files. Inspired but not completely copied from NDFile.template +# Marco Montevechi +# aug 11, 2024 + +################################################################### +# These records control Config file loading # +################################################################### + +# File path. +record(waveform, "$(P)$(R)CfgFilePath") +{ + field(PINI, "YES") + field(DTYP, "asynOctetWrite") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_PATH") + field(FTVL, "CHAR") + field(NELM, "256") + info(autosaveFields, "VAL") +} + +record(waveform, "$(P)$(R)CfgFilePath_RBV") +{ + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_PATH") + field(FTVL, "CHAR") + field(NELM, "256") + field(SCAN, "I/O Intr") +} + +record(bi, "$(P)$(R)CfgFilePathExists_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_PATH_EXISTS") + field(ZNAM, "No") + field(ZSV, "MAJOR") + field(ONAM, "Yes") + field(OSV, "NO_ALARM") + field(SCAN, "I/O Intr") +} + +# Filename +record(waveform, "$(P)$(R)CfgFileName") +{ + field(PINI, "YES") + field(DTYP, "asynOctetWrite") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_NAME") + field(FTVL, "CHAR") + field(NELM, "256") + info(autosaveFields, "VAL") +} + +record(waveform, "$(P)$(R)CfgFileName_RBV") +{ + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_NAME") + field(FTVL, "CHAR") + field(NELM, "256") + field(SCAN, "I/O Intr") +} + +# Full filename, including path +record(waveform, "$(P)$(R)CfgFullFileName_RBV") +{ + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FULL_FILE_NAME") + field(FTVL, "CHAR") + field(NELM, "512") + field(SCAN, "I/O Intr") +} + +# Full filename, including path +record(bi, "$(P)$(R)CfgFileValid_RBV") +{ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))FILE_IS_VALID") + field(ZNAM, "No") + field(ZSV, "MAJOR") + field(ONAM, "Yes") + field(OSV, "NO_ALARM") + field(SCAN, "I/O Intr") +} + +record (bo, "$(P)$(R)CurlLoadConfig") +{ + field(DESC, "Load curl configuration from file") + field(DTYP, "asynInt32") + field(ZNAM, "0") + field(ONAM, "1") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))CURL_LOAD_CONFIG") +} \ No newline at end of file diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index 9b48adb..b0b0006 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -1,5 +1,7 @@ # Records to toggle curl functionalities +include "CurlConfigFile.template" + record (bo, "$(P)$(R)UseCurl") { field(DESC, "Toggle curl usage") diff --git a/urlApp/Db/Makefile b/urlApp/Db/Makefile index e088e4f..57df990 100644 --- a/urlApp/Db/Makefile +++ b/urlApp/Db/Makefile @@ -14,6 +14,7 @@ include $(TOP)/configure/CONFIG DB += URLDriver.template ifeq ($(WITH_CURL), YES) DB += CurlUsage.template + DB += CurlConfigFile.template endif #---------------------------------------------------- diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 8f0e646..25bcf89 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -333,6 +333,11 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t status |= setStringParam(addr, function, (char *)value); if (function < FIRST_URL_DRIVER_PARAM) { + + if (function==NDFileName or function==NDFilePath){ + this->completeFullPath(); + } + status |= ADDriver::writeOctet(pasynUser, value, nChars, nActual); } else if (function == curlOptUserName) { @@ -348,6 +353,37 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t return (asynStatus)status; } + +/* Called each time filePath or fileName is changed to check if file is accessible. + Should only be called inside writeOctet() so needn't call callbacks. */ +asynStatus URLDriver::completeFullPath() +{ + + char fullFileName[MAX_FILENAME_LEN]; + int status = 0; + const char * functionName = "completeFullPath"; + struct stat file; + + status = ADDriver::createFileName(2*MAX_FILENAME_LEN, fullFileName); + + if (status) { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: Failed to create full filename.\n", driverName, functionName); + return (asynStatus)status; + } + + setStringParam(NDFullFileName, fullFileName); + + /* Check if file is accessible */ + if (stat(fullFileName, &(file)) == 0 && + S_ISREG(file.st_mode) && + access(fullFileName, R_OK) == 0){setIntegerParam(fileIsValid, 1);} + else {setIntegerParam(fileIsValid, 0);} + + return asynSuccess; + +} + #endif @@ -418,6 +454,8 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, #ifdef ADURL_USE_CURL createParam(UseCurlString, asynParamInt32, &useCurl); + createParam(CurlLoadConfigString, asynParamInt32, &curlLoadConfig); + createParam(CurlFileIsValidString, asynParamInt32, &fileIsValid); createParam(CurlOptHttpAuthString, asynParamInt32, &curlOptHttpAuth); createParam(CurlOptSSLVerifyHostString, asynParamInt32, &curlOptSSLVerifyHost); createParam(CurlOptSSLVerifyPeerString, asynParamInt32, &curlOptSSLVerifyPeer); @@ -428,8 +466,11 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, setIntegerParam(curlOptHttpAuth, 0); setIntegerParam(curlOptSSLVerifyHost, 2L); setIntegerParam(curlOptSSLVerifyPeer, 1); - setStringParam(curlOptUserName, "\0"); - setStringParam(curlOptPassword, "\0"); + setStringParam(curlOptUserName, "\0"); + setStringParam(curlOptPassword, "\0"); + + /* FileTEmplate parameter won't use complicated templates here */ + setStringParam(NDFileTemplate, "%s%s"); this->initializeCurl(); #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index d8d4204..18242ef 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -2,6 +2,9 @@ #ifdef ADURL_USE_CURL #include + #include + #include + #include #endif #define DRIVER_VERSION 2 @@ -21,6 +24,7 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual); + asynStatus completeFullPath(); void initializeCurl(); #endif @@ -30,6 +34,8 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL int useCurl; + int curlLoadConfig; + int fileIsValid; int curlOptHttpAuth; int curlOptSSLVerifyHost; int curlOptSSLVerifyPeer; @@ -59,6 +65,8 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL #define UseCurlString "USE_CURL" + #define CurlLoadConfigString "CURL_LOAD_CONFIG" + #define CurlFileIsValidString "FILE_IS_VALID" #define CurlOptHttpAuthString "ASYN_CURLOPT_HTTPAUTH" #define CurlOptSSLVerifyHostString "ASYN_CURLOPT_SSL_VERIFYHOST" #define CurlOptSSLVerifyPeerString "ASYN_CURLOPT_SSL_VERIFYPEER" From b9ca1931740db556508c19309a0df25de5a7e3c0 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Thu, 15 Aug 2024 18:42:30 +0200 Subject: [PATCH 12/21] Add curl file loading functionality Add CurlLoadConfig record and loadConfigFile function. loadConfigFile() calls writeInt32 and writeOctet for all parameters it can recognize, except username and password. For those, loadConfigFile() calls curl_easy_setopt without ever setting the parameter value. This means setpoint and RBV values for all parameters can be different, so I add readback values for all records. writeInt32 and writeOctet are called inside loadConfigFile(), which is called inside writeInt32. In my understanding this cannot lead to any infinite loop since CurlLoadConfig record has no "ASYN_" prepending it's name, so it's impossible for it to call writeInt32 on itself. Also, I think this is thread safe because the function is only supposed to be called inside writeInt32, which is supposed to be already locked. --- iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd | 1 + urlApp/Db/CurlUsage.template | 55 ++++++++++++ urlApp/src/URLDriver.cpp | 89 ++++++++++++++++++++ urlApp/src/URLDriver.h | 2 + 4 files changed, 147 insertions(+) diff --git a/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd b/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd index 60ad48c..7ea69bb 100755 --- a/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd +++ b/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd @@ -27,6 +27,7 @@ epicsEnvSet("EPICS_DB_INCLUDE_PATH", "$(ADCORE)/db") # int priority, int stackSize) URLDriverConfig("$(PORT)", 0, 0) dbLoadRecords("$(ADURL)/db/URLDriver.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") +dbLoadRecords("$(ADURL)/db/CurlUsage.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") # Create a standard arrays plugin. NDStdArraysConfigure("Image1", 3, 0, "$(PORT)", 0) diff --git a/urlApp/Db/CurlUsage.template b/urlApp/Db/CurlUsage.template index b0b0006..dda1cd8 100644 --- a/urlApp/Db/CurlUsage.template +++ b/urlApp/Db/CurlUsage.template @@ -42,6 +42,36 @@ record (mbbo, "$(P)$(R)CurlOptHTTPAuth") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_HTTPAUTH") } +record (mbbi, "$(P)$(R)CurlOptHTTPAuth_RBV") +{ + field(DESC, "Set CURLOPT_HTTPAUTH") + field(DTYP, "asynInt32") + field(ZRST, "CURLAUTH_BASIC") + field(ZRVL, "0") + field(ONST, "CURLAUTH_DIGEST") + field(ONVL, "1") + field(TWST, "CURLAUTH_DIGEST_IE") + field(TWVL, "2") + field(THST, "CURLAUTH_BEARER") + field(THVL, "3") + field(FRST, "CURLAUTH_NEGOTIATE") + field(FRVL, "4") + field(FVST, "CURLAUTH_NTLM") + field(FVVL, "5") + field(SXST, "CURLAUTH_NTLM_WB") + field(SXVL, "6") + field(SVST, "CURLAUTH_ANY") + field(SVVL, "7") + field(EIST, "CURLAUTH_ANYSAFE") + field(EIVL, "8") + field(NIST, "CURLAUTH_ONLY") + field(NIVL, "9") + field(TEST, "CURLAUTH_AWS_SIGV4") + field(TEVL, "10") + field(SCAN, "I/O Intr") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_HTTPAUTH") +} + record (mbbo, "$(P)$(R)CurlOptSSLVerifyHost") { field(DESC, "Verify Host SSL certificate") @@ -55,6 +85,20 @@ record (mbbo, "$(P)$(R)CurlOptSSLVerifyHost") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYHOST") } +record (mbbi, "$(P)$(R)CurlOptSSLVerifyHost_RBV") +{ + field(DESC, "Verify Host SSL certificate") + field(DTYP, "asynInt32") + field(ZRST, "NO") + field(ZRVL, "0") + field(ONST, "YES") + field(ONVL, "1") + field(TWST, "YES") + field(TWVL, "2") + field(SCAN, "I/O Intr") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYHOST") +} + record (bo, "$(P)$(R)CurlOptSSLVerifyPeer") { field(DESC, "Verify SSL peer") @@ -65,6 +109,17 @@ record (bo, "$(P)$(R)CurlOptSSLVerifyPeer") field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYPEER") } +record (bi, "$(P)$(R)CurlOptSSLVerifyPeer_RBV") +{ + field(DESC, "Verify SSL peer") + field(DTYP, "asynInt32") + field(PINI, "YES") + field(ZNAM, "NO") + field(ONAM, "YES") + field(SCAN, "I/O Intr") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ASYN_CURLOPT_SSL_VERIFYPEER") +} + record(waveform, "$(P)$(R)CurlOptUserName") { field(DESC, "Username for auth. Try not to use this.") diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 25bcf89..fa1f1cc 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -301,6 +301,8 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)value); } else if (function==curlOptSSLVerifyPeer) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, value); + } else if (function==curlLoadConfig) { + this->loadConfigFile(); #endif } else { /* If this parameter belongs to a base class call its method */ @@ -384,6 +386,93 @@ asynStatus URLDriver::completeFullPath() } +asynStatus URLDriver::loadConfigFile() +{ + + const char * functionName = "loadConfigFile"; + char fullFileName[MAX_FILENAME_LEN]; + std::ifstream file; + std::string line, key, value; int valueInt; + asynParamType type; + int param, status = 0; + size_t nActual = 0; + + getStringParam(NDFullFileName, MAX_FILENAME_LEN, fullFileName); + + file.open(fullFileName); + if (!file) { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: ERROR, cannot open file %s.\n", driverName, functionName, fullFileName); + return asynError; + } + + while (getline(file, line)) { + key = line.substr(0,line.find("=")); + value = line.substr(line.find("=")+1, line.back()); + + /* Taking spaces out of strings */ + key.erase(std::remove(key.begin(), key.end(), ' '), key.end()); + value.erase(std::remove(value.begin(), value.end(), ' '), value.end()); + + /* Finding which asyn parameter corersponds to option */ + key = "ASYN_" + key; + asynPortDriver::findParam(key.c_str(), ¶m); + /* asynUser to call writeOctet or writeInt32 later */ + asynUser tempUser{.reason = param}; + + /* If param is credential, set curlOption but don't set asyn record*/ + if (param == curlOptUserName) { + curl_easy_setopt(curl, CURLOPT_USERNAME, value.c_str()); + continue; + } else if (param == curlOptPassword) { + curl_easy_setopt(curl, CURLOPT_PASSWORD, value.c_str()); + continue; + } + + if (param == -1){ + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: ERROR, cannot find parameter %s from config file." + " Is this parameter implemented?\n", + driverName, functionName, key.c_str()); + + return asynError; + } + + asynPortDriver::getParamType(param, &type); + switch (type) { + case asynParamInt32: + try { + valueInt = stoi(value); + } catch (std::invalid_argument&) { + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: ERROR, cannot convert value %s to int for" + " parameter %s\n", + driverName, functionName, value.c_str(), key.c_str()); + return asynError; + } + status |= this->writeInt32(&tempUser, (epicsInt32)valueInt); + break; + case asynParamOctet: + status |= this->writeOctet(&tempUser, value.c_str(), value.size(), &nActual); + break; + default: + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: ERROR, parameter %s is of asynParam type" + " %d. This function only deals with asynParamOctet (%d)" + " and asynParamInt32 (%d).\n", + driverName, functionName, key.c_str(), type, + asynParamOctet, asynParamInt32); + return asynError; + } + + + } + + file.close(); + return (asynStatus)status; + +} + #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index 18242ef..f044acf 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -25,6 +25,7 @@ class URLDriver : public ADDriver { #ifdef ADURL_USE_CURL virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual); asynStatus completeFullPath(); + asynStatus loadConfigFile(); void initializeCurl(); #endif @@ -59,6 +60,7 @@ class URLDriver : public ADDriver { Image image; epicsEventId startEventId; epicsEventId stopEventId; + }; #define URLNameString "URL_NAME" From 116434e5f073e087738eb8b39187a08495b8bf55 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 21 Aug 2024 14:53:29 +0200 Subject: [PATCH 13/21] Add writeCallback and buffer. Use it on readImage. Readbuffer is a char vector just so we can copy all needed bytes to it. writeCallback should simply add stuff to the buffer. readImage clears buffer before adding to it. Unfortunately I think clear cant be called inside writeCallback because it's called more than one time per read. --- urlApp/src/URLDriver.cpp | 34 +++++++++++++++++++++++++++++++++- urlApp/src/URLDriver.h | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index fa1f1cc..3b358de 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -35,6 +35,10 @@ void URLDriver::initializeCurl(){ asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, cannot initialize curl pointer.\n", driverName, __func__); } + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curlBuffer); + } #endif @@ -53,11 +57,31 @@ asynStatus URLDriver::readImage() int depth; const char *map; static const char *functionName = "readImage"; - + getStringParam(URLName, sizeof(URLString), URLString); + #ifdef ADURL_USE_CURL + int use_curl; + getIntegerParam(useCurl, &use_curl); + #endif if (strlen(URLString) == 0) return(asynError); try { + #ifdef ADURL_USE_CURL + if (use_curl) { + this->curlBuffer.clear(); + this->res = curl_easy_perform(curl); + if (res != CURLE_OK){ + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: curl read error %d\n", + driverName, functionName, res); + return(asynError); + } + Blob blob(&curlBuffer[0], curlBuffer.size()); + image.read(blob); + } else { + image.read(URLString); + } + #else image.read(URLString); + #endif imageType = image.type(); depth = image.depth(); nrows = image.rows(); @@ -473,6 +497,14 @@ asynStatus URLDriver::loadConfigFile() } +size_t URLDriver::curlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) +{ + int totalSize = size * nmemb; + + ((std::vector*)userp)->insert(((std::vector*)userp)->end(), (char*)contents, (char*)contents + totalSize); + return totalSize; +} + #endif diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index f044acf..12fd91e 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -27,6 +27,7 @@ class URLDriver : public ADDriver { asynStatus completeFullPath(); asynStatus loadConfigFile(); void initializeCurl(); + static size_t curlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp); #endif protected: @@ -45,6 +46,7 @@ class URLDriver : public ADDriver { #define MAXCURLSTRCHARS 128 CURL *curl = NULL; CURLcode res; + std::vector curlBuffer; /*Array to translate CurlHttpAuth options*/ long unsigned int CurlHttpOptions [11] = {CURLAUTH_BASIC, CURLAUTH_DIGEST, CURLAUTH_DIGEST_IE, CURLAUTH_BEARER, From 404f5dc2b80fd43ae30d68ce369aedfc59c2130f Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 21 Aug 2024 15:30:41 +0200 Subject: [PATCH 14/21] Fix: add needed set of CURLOPT_URL This is obviously needed and acquisition will fail without this. --- urlApp/src/URLDriver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 3b358de..441fa11 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -372,6 +372,9 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t } else if (function == curlOptPassword) { getStringParam(curlOptPassword, MAXCURLSTRCHARS, param); curl_easy_setopt(curl, CURLOPT_PASSWORD, param); + } else if (function == URLName) { + getStringParam(URLName, MAXCURLSTRCHARS, param); + curl_easy_setopt(curl, CURLOPT_URL, param); } callParamCallbacks(addr); From 8cff1529f857fe044348aa604acfffaab1ecf1dd Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 21 Aug 2024 15:36:45 +0200 Subject: [PATCH 15/21] Style: remove trailing whitespaces. --- urlApp/src/URLDriver.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 441fa11..8c39962 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -104,8 +104,8 @@ asynStatus URLDriver::readImage() colorMode = NDColorModeRGB1; break; default: - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: unknown ImageType=%d\n", + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: unknown ImageType=%d\n", driverName, functionName, imageType); return(asynError); break; @@ -125,8 +125,8 @@ asynStatus URLDriver::readImage() storageType = IntegerPixel; break; default: - asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: unsupported depth=%d\n", + asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: unsupported depth=%d\n", driverName, functionName, depth); return(asynError); break; @@ -136,7 +136,7 @@ asynStatus URLDriver::readImage() pImage = this->pArrays[0]; asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: reading URL=%s, dimensions=[%lu,%lu,%lu], ImageType=%d, depth=%d\n", - driverName, functionName, URLString, + driverName, functionName, URLString, (unsigned long)dims[0], (unsigned long)dims[1], (unsigned long)dims[2], imageType, depth); image.write(0, 0, ncols, nrows, map, storageType, pImage->pData); pImage->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode); @@ -151,12 +151,12 @@ asynStatus URLDriver::readImage() } catch(std::exception &error) { - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, - "%s:%s: error reading URL=%s\n", + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s:%s: error reading URL=%s\n", driverName, functionName, error.what()); return(asynError); } - + return(asynSuccess); } @@ -547,7 +547,7 @@ void URLDriver::report(FILE *fp, int details) * \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags. * \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags. */ -URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, +URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, int priority, int stackSize) : ADDriver(portName, 1, 0, maxBuffers, maxMemory, @@ -601,7 +601,7 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, /* Set some default values for parameters */ status = setStringParam (ADManufacturer, "URL Driver"); status |= setStringParam (ADModel, "GraphicsMagick"); - epicsSnprintf(versionString, sizeof(versionString), "%d.%d.%d", + epicsSnprintf(versionString, sizeof(versionString), "%d.%d.%d", DRIVER_VERSION, DRIVER_REVISION, DRIVER_MODIFICATION); setStringParam(NDDriverVersion, versionString); setStringParam(ADSDKVersion, MagickLibVersionText); @@ -626,7 +626,7 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, } /** Configuration command, called directly or from iocsh */ -extern "C" int URLDriverConfig(const char *portName, int maxBuffers, size_t maxMemory, +extern "C" int URLDriverConfig(const char *portName, int maxBuffers, size_t maxMemory, int priority, int stackSize) { /* Initialize GraphicsMagick */ From de4f1ada26e5107aaf55d444dfddcb6c77f17b63 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Wed, 21 Aug 2024 15:59:26 +0200 Subject: [PATCH 16/21] Add WITH_CURL example in CONFIG_SITE WITH_CURL = YES/NO is used to tell the compiler to use or not use curl. I add an example in configure/CONFIG_SITE to make it easy for people to enable/disable this in future. --- configure/CONFIG_SITE | 1 + 1 file changed, 1 insertion(+) diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index 4df968f..34f7f83 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -15,6 +15,7 @@ # Set CHECK_RELEASE to WARN to perform consistency checking but # continue building anyway if conflicts are found. CHECK_RELEASE = YES +# WITH_CURL = YES # Set this when you only want to compile this application # for a subset of the cross-compiled target architectures From b55dc74ef56afb07976d139640f43737296c8df9 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Thu, 22 Aug 2024 16:05:14 +0200 Subject: [PATCH 17/21] Add driver description in ADURL.rst Describe all new records along with brief driver description. --- docs/ADURL/ADURL.rst | 71 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/docs/ADURL/ADURL.rst b/docs/ADURL/ADURL.rst index f1bdfea..7d3f179 100644 --- a/docs/ADURL/ADURL.rst +++ b/docs/ADURL/ADURL.rst @@ -12,15 +12,30 @@ Introduction This is an :doc:`../index` driver for reading images from a URL. It can be used to read images from Web cameras, `Axis video -servers`_, or simply from a disk file. It reads the images using -`GraphicsMagick`_ and can thus read images encoded in any of the -formats supported by GraphicsMagick, such as JPEG, TIFF, PNG, etc. +servers`_, or simply from a disk file. It can read the images +using either `GraphicsMagick`_ or `Curl`_ (if compiled with the +WITH_CURL=YES option on CONFIG_SITE and toggling the UseCurl record) +and can thus read images encoded in any of the formats supported +by GraphicsMagick, such as JPEG, TIFF, PNG, etc. The driver simply reads images from the specified URL at the rate determined by the AcquirePeriod. Web cameras and Axis video servers have a URL address from which the current image can be read. There are often several addresses for different image sizes. +If configured correctly, curl can support both url redirection and authentication. +To avoid tedious, repetitive and error prone configuration processes, curl can +have a configuration file with all `curl options`_ in the format key = value. Example: + +| CURLOPT_HTTPAUTH = 2 +| CURLOPT_USERNAME = User +| CURLOPT_PASSWORD = PassWord + +Each curl option must have a value adequate to the asynParamType that the option +is implemented. For example, CURLOPT_HTTPAUTH = CURLAUTH_BASIC will not work. Also, +the file only configure asyn-implemented options. Unfortunately, it does not configure +curl options that are not yet implemented in the driver. + This driver inherits from :doc:`../ADCore/ADDriver`. It implements many of the parameters in `asynNDArrayDriver.h`_ and in `ADArrayDriver.h`_. It also implements a number of parameters that are specific to the URL @@ -54,6 +69,24 @@ the standard driver parameters. **NOTE: If this value is set to 0 or too small a value can result in the driver using 100% of the CPU and becoming unresponsive to EPICS.** + * - NDFilePath + - $(P)$(R)CfgFilePath + - Path of directory where to find curl configuration file. + * - NDFilePathExists + - $(P)$(R)CfgFilePathExists_RBV + - Is set to one if driver thinks the file exists. Is set to 0 otherwise. + * - NDFileName + - $(P)$(R)CfgFileName + - Curl configuration file name. The file should contain values to be put + into each record in the format: + key = value + Example: + CURLOPT_HTTPAUTH = 2 + CURLOPT_USERNAME = User + CURLOPT_PASSWORD = PassWord + * - NDFullFileName + - $(P)$(R)CfgFullFileName_RBV + - Full filename appended with filepath. URL driver specific parameters @@ -79,6 +112,35 @@ those in asynNDArrayDriver.h and ADDriver.h. * - Selects which os the 10 URLs to read from. - $(P)$(R)URLSelect - mbbo + * - Determines if image is acquired from URL with GraphicsMagick or Curl. + - $(P)$(R)UseCurl + - bo + * - Sets curl's HTTP authentication method. + - $(P)$(R)CurlOptHTTPAuth, $(P)$(R)CurlOptHTTP_RBV + - mbbo, mbbi + * - Toggle curl's hostname verification in SSL certificate. + - $(P)$(R)CurlOptSSLVerifyHost, $(P)$(R)CurlOptSSLVerifyHost_RBV + - mbbo, mbbi + * - Toggle curl's SSL certificate verification. + - $(P)$(R)CurlOptSSLVerifyPerr, $(P)$(R)CurlOptSSLVerifyPeer_RBV + - bo, bi + * - Set curl's authentication username. + - $(P)$(R)CurlOptUserName + - waveform + * - Set curl's authentication password. + - $(P)$(R)CurlOptPassword + - waveform + * - Shows if driver has read permission to file. + - $(P)$(R)CfgFileValid_RBV + - bi + * - $(P)$(R)CurlLoadConfig + - Loads configuration from file in $(P)$(R)CfgFullFileName_RBV. BEWARE: + this will probably make the setpoint and readback curl option records have + different values. For example, if you set $(P)$(R)CurlOptHTTPAuth and then + load a CurlConfigFile that changes it, $(P)$(R)CurlOptHTTPAuth_RBV is going + to have a different file. Also, $(P)$(R)CurlOptUserName and + $(P)$(R)CurlOptUserPassword are going to be set in the curl option, but there + is no readback for them. The URLs for Web cameras and video servers are typically long strings, which are difficult to remember and to type. Thus, for convenience @@ -174,5 +236,6 @@ connected to analog camera through an Axis video server. .. _GraphicsMagick: http://www.graphicsmagick.org/ .. _ADDriver: ../areaDetectorDoc.html#ADDriver .. _Axis video servers: http://www.axis.com/ - +.. _Curl: https://curl.se/ +.. _curl options: https://curl.se/libcurl/c/curl_easy_setopt.html From 1208fb10862b65f76a714e94d2cf609090986d24 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Thu, 22 Aug 2024 16:19:55 +0200 Subject: [PATCH 18/21] Add comments in st_base example file Comments to make it easier to load Curl template file if compiled with WITH_CURL=YES --- iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd b/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd index 7ea69bb..54a6a88 100755 --- a/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd +++ b/iocs/urlIOC/iocBoot/iocURLDriver/st_base.cmd @@ -27,8 +27,12 @@ epicsEnvSet("EPICS_DB_INCLUDE_PATH", "$(ADCORE)/db") # int priority, int stackSize) URLDriverConfig("$(PORT)", 0, 0) dbLoadRecords("$(ADURL)/db/URLDriver.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") -dbLoadRecords("$(ADURL)/db/CurlUsage.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") +#### If using curl uncomment this +## CurlUsage.template includes another template file under $(ADURL)/db/ +# epicsEnvSet("EPICS_DB_INCLUDE_PATH", "$(EPICS_DB_INCLUDE_PATH):$(ADURL)/db/") +# Load curl configuration records +# dbLoadRecords("$(ADURL)/db/CurlUsage.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") # Create a standard arrays plugin. NDStdArraysConfigure("Image1", 3, 0, "$(PORT)", 0) From 2aec6fd5988ca1ebdf4dbb10e6ac8a8866449242 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Thu, 22 Aug 2024 16:36:40 +0200 Subject: [PATCH 19/21] Add comments in code --- urlApp/src/URLDriver.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/urlApp/src/URLDriver.cpp b/urlApp/src/URLDriver.cpp index 8c39962..65c2da0 100644 --- a/urlApp/src/URLDriver.cpp +++ b/urlApp/src/URLDriver.cpp @@ -28,6 +28,9 @@ using namespace Magick; static const char *driverName = "URLDriver"; #ifdef ADURL_USE_CURL +/** Called by class constructor to initialize curl handle pointer + * and set the writeCallback function and read buffer. +*/ void URLDriver::initializeCurl(){ curl_global_init(CURL_GLOBAL_DEFAULT); this->curl = curl_easy_init(); @@ -348,6 +351,14 @@ asynStatus URLDriver::writeInt32(asynUser *pasynUser, epicsInt32 value) } #ifdef ADURL_USE_CURL +/** Only actually implemented if compiled with WITH_CURL = YES. + * Called when asyn clients call pasynOctet->write(). + * In this particular driver, this function sets the curl string configuration options. + * For all parameters it sets the value in the parameter library and calls any registered callbacks.. + * \param[in] pasynUser pasynUser structure that encodes the reason and address. + * \param[in] value Address of the string to write. + * \param[in] nChars Number of characters to write. + * \param[out] nActual Number of characters actually written. */ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t nChars, size_t *nActual) { @@ -384,7 +395,8 @@ asynStatus URLDriver::writeOctet(asynUser *pasynUser, const char *value, size_t } /* Called each time filePath or fileName is changed to check if file is accessible. - Should only be called inside writeOctet() so needn't call callbacks. */ + Should only be called inside writeOctet() so needn't call callbacks, although it + set the fileIsValid parameter to either 0 or 1.*/ asynStatus URLDriver::completeFullPath() { @@ -413,6 +425,9 @@ asynStatus URLDriver::completeFullPath() } +/* Parses options from a configuration file and sets all the parameters that it can + with the options. Only works for asyn-implemented curl options because it calls either + URLDriver::writeInt32 or URLDriver::writeOctet for each option.*/ asynStatus URLDriver::loadConfigFile() { @@ -500,6 +515,11 @@ asynStatus URLDriver::loadConfigFile() } +/* Is called after curl_easy_perform to read from url and store output into *userp buffer + in this particular driver, userp is std::vector * this->curlBuffer. + To avoid accumulating infinite information in memory, buffer should always be cleaned + before calling curl_easy_perform. If can't be cleaned inside this function though, because + it seems it is called several times for each curl_easy_perform call.*/ size_t URLDriver::curlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp) { int totalSize = size * nmemb; @@ -593,7 +613,7 @@ URLDriver::URLDriver(const char *portName, int maxBuffers, size_t maxMemory, setStringParam(curlOptUserName, "\0"); setStringParam(curlOptPassword, "\0"); - /* FileTEmplate parameter won't use complicated templates here */ + /* FileTemplate parameter won't use complicated templates here */ setStringParam(NDFileTemplate, "%s%s"); this->initializeCurl(); #endif From dbe04545b757a25ff7afc30616c11902ca86cde3 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Fri, 23 Aug 2024 13:57:49 +0200 Subject: [PATCH 20/21] Change driver modification/revision numbers Since this PR is supposed to add new functionality, I reset patch number and add to revision number. --- urlApp/src/URLDriver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/urlApp/src/URLDriver.h b/urlApp/src/URLDriver.h index 12fd91e..61768b6 100644 --- a/urlApp/src/URLDriver.h +++ b/urlApp/src/URLDriver.h @@ -8,8 +8,8 @@ #endif #define DRIVER_VERSION 2 -#define DRIVER_REVISION 3 -#define DRIVER_MODIFICATION 1 +#define DRIVER_REVISION 4 +#define DRIVER_MODIFICATION 0 /** URL driver; reads images from URLs, such as Web cameras and Axis video servers, but also files, etc. */ class URLDriver : public ADDriver { From af80cc7b6872d65557438d483597e4a844f9a423 Mon Sep 17 00:00:00 2001 From: marcofilho Date: Fri, 23 Aug 2024 14:08:18 +0200 Subject: [PATCH 21/21] Update RELEASE with curl modifications Update with info about header file change and curl functionality added. --- RELEASE.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELEASE.md b/RELEASE.md index 0fd3e7a..1a71278 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -23,6 +23,13 @@ files respectively, in the configure/ directory of the appropriate release of th Release Notes ============= +R2-4 (22-Aug-2024) +---- +* Added header file and transferred class declaration to there. +* Added curl usage: driver can use either curl or GraphicksMagick to get image from URL. +* Added WITH_CURL compilation option to compile with or without libcurl. +* Add [documentation](docs/ADURL/ADURL.rst) about curl configuration records. + R2-3 (26-May-2021) ---- * Converted documentation to ReST, include in documentation on github.io.