LCOV - code coverage report
Current view: top level - src/plugins/curlget - curlget.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 67 500 13.4 %
Date: 2023-05-29 20:12:37 Functions: 6 16 37.5 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for curlget plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "curlget.h"
      11             : 
      12             : #include <curl/curl.h>
      13             : #include <curl/easy.h>
      14             : #include <errno.h>
      15             : #include <fcntl.h>
      16             : #include <kdberrors.h>
      17             : #include <kdbhelper.h>
      18             : #include <libgen.h>
      19             : #include <openssl/md5.h>
      20             : #include <stdio.h>
      21             : #include <stdlib.h>
      22             : #include <string.h>
      23             : #include <strings.h>
      24             : #include <sys/stat.h>
      25             : #include <sys/types.h>
      26             : #include <time.h>
      27             : #include <unistd.h>
      28             : 
      29             : #include "../resolver/shared.h"
      30             : #include <kdbinvoke.h>
      31             : 
      32             : #define TMP_NAME "/tmp/elektraCurlTempXXXXXX"
      33             : 
      34             : #define DEFAULT_POSTFIELDNAME "file"
      35             : 
      36             : typedef enum
      37             : {
      38             :         NA = 0,
      39             :         PUT,
      40             :         POST
      41             : } HttpUploadMethods;
      42             : typedef enum
      43             : {
      44             :         PROTO_INVALID = 0,
      45             :         PROTO_HTTP,
      46             :         PROTO_HTTPS,
      47             :         PROTO_FTP,
      48             :         PROTO_FTPS,
      49             :         PROTO_SFTP,
      50             :         PROTO_SCP,
      51             :         PROTO_SMB,
      52             : } ElektraCurlProtocol;
      53             : typedef enum
      54             : {
      55             :         SSH_ANY = 0,
      56             :         SSH_AGENT,
      57             :         SSH_PASSWORD,
      58             :         SSH_PUBLICKEY,
      59             :         SSH_PUBKEYPW,
      60             : } SSHAuthType;
      61             : typedef struct
      62             : {
      63             :         char * path;
      64             :         const char * tmpFile;
      65             :         time_t mtime;
      66             :         unsigned char lastHash[MD5_DIGEST_LENGTH];
      67             :         const char * getUrl;
      68             :         const char * uploadUrl;
      69             :         const char * user;
      70             :         const char * password;
      71             :         const char * postFieldName;
      72             :         const char * uploadFileName;
      73             :         char * __uploadFileName;
      74             :         HttpUploadMethods uploadMethod;
      75             :         unsigned short preferRemote;
      76             :         unsigned short setPhase;
      77             :         unsigned short useLocalCopy;
      78             :         unsigned short useSSL;
      79             :         unsigned short sslVerifyPeer;
      80             :         unsigned short sslVerifyHost;
      81             :         const char * keyFile;
      82             :         const char * keyFilePasswd;
      83             :         ElektraCurlProtocol getProto;
      84             :         ElektraCurlProtocol putProto;
      85             :         SSHAuthType sshAuth;
      86             : } Data;
      87             : 
      88           0 : static int elektraResolveFilename (Key * parentKey, ElektraResolveTempfile tmpFile)
      89             : {
      90           0 :         int rc = 0;
      91           0 :         void * handle = elektraInvokeOpen ("resolver", 0, 0);
      92           0 :         if (!handle)
      93             :         {
      94             :                 rc = -1;
      95             :                 goto RESOLVE_FAILED;
      96             :         }
      97           0 :         ElektraResolved * resolved = NULL;
      98           0 :         typedef ElektraResolved * (*resolveFileFunc) (elektraNamespace, const char *, ElektraResolveTempfile, Key *);
      99           0 :         resolveFileFunc resolveFunc = *(resolveFileFunc *) elektraInvokeGetFunction (handle, "filename");
     100             : 
     101           0 :         if (!resolveFunc)
     102             :         {
     103             :                 rc = -1;
     104             :                 goto RESOLVE_FAILED;
     105             :         }
     106             : 
     107           0 :         typedef void (*freeHandleFunc) (ElektraResolved *);
     108           0 :         freeHandleFunc freeHandle = *(freeHandleFunc *) elektraInvokeGetFunction (handle, "freeHandle");
     109             : 
     110           0 :         if (!freeHandle)
     111             :         {
     112             :                 rc = -1;
     113             :                 goto RESOLVE_FAILED;
     114             :         }
     115             : 
     116           0 :         resolved = resolveFunc (keyGetNamespace (parentKey), keyString (parentKey), tmpFile, parentKey);
     117             : 
     118           0 :         if (!resolved)
     119             :         {
     120             :                 rc = -1;
     121             :                 goto RESOLVE_FAILED;
     122             :         }
     123             :         else
     124             :         {
     125           0 :                 keySetString (parentKey, resolved->fullPath);
     126           0 :                 freeHandle (resolved);
     127             :         }
     128             : 
     129           0 : RESOLVE_FAILED:
     130           0 :         elektraInvokeClose (handle, 0);
     131           0 :         return rc;
     132             : }
     133             : 
     134           0 : int elektraCurlgetCheckFile (const char * filename)
     135             : {
     136           0 :         if (filename[0] == '/') return 0;
     137             : 
     138             :         return 1;
     139             : }
     140             : 
     141          31 : int elektraCurlgetClose (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
     142             : {
     143          31 :         Data * data = elektraPluginGetData (handle);
     144          31 :         if (!data) return 0;
     145           2 :         if (data->tmpFile)
     146             :         {
     147           0 :                 unlink (data->tmpFile);
     148           0 :                 data->tmpFile = NULL;
     149             :         }
     150           2 :         if (!data->useLocalCopy && data->path)
     151             :         {
     152           0 :                 unlink (data->path);
     153           0 :                 elektraFree (data->path);
     154           0 :                 data->path = NULL;
     155             :         }
     156           2 :         else if (data->path)
     157             :         {
     158           2 :                 elektraFree (data->path);
     159           2 :                 data->path = NULL;
     160             :         }
     161           2 :         if (data->uploadFileName) elektraFree (data->__uploadFileName);
     162           2 :         elektraFree (data);
     163           2 :         data = NULL;
     164           2 :         elektraPluginSetData (handle, data);
     165           2 :         return 1;
     166             : }
     167             : 
     168           0 : int elektraCurlgetError (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     169             : {
     170           0 :         return 1;
     171             : }
     172             : 
     173           2 : static ElektraCurlProtocol isValidURL (const char * str)
     174             : {
     175           2 :         if (str == NULL) return PROTO_INVALID;
     176             : 
     177           2 :         struct checkProtocolStruct
     178             :         {
     179             :                 ElektraCurlProtocol proto;
     180             :                 const char * url;
     181             :         };
     182             : 
     183           2 :         const struct checkProtocolStruct urlPrefix[] = {
     184             :                 { PROTO_HTTP, "http://" }, { PROTO_HTTPS, "https://" }, { PROTO_FTP, "ftp://" }, { PROTO_FTPS, "ftps://" },
     185             :                 { PROTO_SFTP, "sftp://" }, { PROTO_SCP, "scp://" }, { PROTO_SMB, "smb://" }, { PROTO_INVALID, NULL },
     186             :         };
     187          16 :         for (int i = 0; urlPrefix[i].proto != PROTO_INVALID; ++i)
     188             :         {
     189          14 :                 if (!strncasecmp (str, urlPrefix[i].url, strlen (urlPrefix[i].url))) return urlPrefix[i].proto;
     190             :         }
     191             :         return PROTO_INVALID;
     192             : }
     193             : 
     194           2 : static unsigned short parseURLPath (Data * data, KeySet * config)
     195             : {
     196           2 :         const char * path = keyString (ksLookupByName (config, "/path", 0));
     197           2 :         ElektraCurlProtocol proto = isValidURL (path);
     198           2 :         Key * key = NULL;
     199           2 :         if (proto == PROTO_INVALID)
     200             :         {
     201           2 :                 data->path = elektraStrDup (path);
     202           2 :                 data->useLocalCopy = 1;
     203           2 :                 key = ksLookupByName (config, "/url", KDB_O_NONE);
     204           2 :                 if (key)
     205             :                 {
     206           0 :                         proto = isValidURL (keyString (key));
     207           0 :                         if (proto != PROTO_INVALID)
     208             :                         {
     209           0 :                                 data->getUrl = keyString (key);
     210           0 :                                 data->getProto = proto;
     211           0 :                                 data->uploadUrl = data->getUrl;
     212           0 :                                 data->putProto = data->getProto;
     213             :                         }
     214             :                         else
     215             :                         {
     216           0 :                                 key = ksLookupByName (config, "/url/get", KDB_O_NONE);
     217           0 :                                 if (!key)
     218             :                                 {
     219             :                                         return 0;
     220             :                                 }
     221             :                                 else
     222             :                                 {
     223           0 :                                         proto = isValidURL (keyString (key));
     224           0 :                                         if (proto == PROTO_INVALID)
     225             :                                         {
     226             :                                                 return 0;
     227             :                                         }
     228             :                                         else
     229             :                                         {
     230           0 :                                                 data->getProto = proto;
     231           0 :                                                 data->getUrl = keyString (key);
     232             :                                         }
     233             :                                 }
     234           0 :                                 key = ksLookupByName (config, "/url/put", KDB_O_NONE);
     235           0 :                                 if (key)
     236             :                                 {
     237           0 :                                         proto = isValidURL (keyString (key));
     238           0 :                                         if (proto == PROTO_INVALID)
     239             :                                         {
     240           0 :                                                 data->putProto = data->getProto;
     241           0 :                                                 data->uploadUrl = data->getUrl;
     242             :                                         }
     243             :                                         else
     244             :                                         {
     245           0 :                                                 data->putProto = proto;
     246           0 :                                                 data->uploadUrl = keyString (key);
     247             :                                         }
     248             :                                 }
     249             :                                 else
     250             :                                 {
     251           0 :                                         data->putProto = data->getProto;
     252           0 :                                         data->uploadUrl = data->getUrl;
     253             :                                 }
     254             :                         }
     255             :                 }
     256             :         }
     257             :         else
     258             :         {
     259           0 :                 data->useLocalCopy = 0;
     260           0 :                 if (data->path) elektraFree (data->path);
     261           0 :                 data->path = NULL;
     262           0 :                 data->getUrl = path;
     263           0 :                 data->getProto = proto;
     264           0 :                 key = ksLookupByName (config, "/url/put", KDB_O_NONE);
     265           0 :                 if (!key)
     266             :                 {
     267           0 :                         data->uploadUrl = data->getUrl;
     268           0 :                         data->putProto = data->getProto;
     269             :                 }
     270             :                 else
     271             :                 {
     272           0 :                         proto = isValidURL (keyString (key));
     273           0 :                         if (proto == PROTO_INVALID)
     274             :                         {
     275             :                                 return 0;
     276             :                         }
     277             :                         else
     278             :                         {
     279           0 :                                 data->uploadUrl = keyString (key);
     280           0 :                                 data->putProto = proto;
     281             :                         }
     282             :                 }
     283             :         }
     284             :         return 1;
     285             : }
     286             : 
     287          31 : int elektraCurlgetOpen (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     288             : {
     289          31 :         KeySet * config = elektraPluginGetConfig (handle);
     290          31 :         if (ksLookupByName (config, "/module", 0)) return 0;
     291           2 :         Data * data = elektraCalloc (sizeof (Data));
     292           2 :         if (!parseURLPath (data, config))
     293             :         {
     294           0 :                 elektraFree (data);
     295           0 :                 data = NULL;
     296           0 :                 return 0;
     297             :         }
     298           2 :         Key * key = ksLookupByName (config, "/user", KDB_O_NONE);
     299           2 :         if (key)
     300             :         {
     301           0 :                 data->user = keyString (key);
     302             :         }
     303           2 :         key = ksLookupByName (config, "/password", KDB_O_NONE);
     304           2 :         if (key)
     305             :         {
     306           0 :                 data->password = keyString (key);
     307             :         }
     308           2 :         if (data->putProto == PROTO_HTTP || data->putProto == PROTO_HTTPS)
     309             :         {
     310           0 :                 key = ksLookupByName (config, "/upload/method", KDB_O_NONE);
     311           0 :                 if (key)
     312             :                 {
     313           0 :                         if (!(strcasecmp (keyString (key), "POST")))
     314             :                         {
     315           0 :                                 data->uploadMethod = POST;
     316           0 :                                 key = ksLookupByName (config, "/upload/postfield", KDB_O_NONE);
     317           0 :                                 if (key)
     318             :                                 {
     319           0 :                                         data->postFieldName = keyString (key);
     320             :                                 }
     321             :                                 else
     322             :                                 {
     323           0 :                                         data->postFieldName = DEFAULT_POSTFIELDNAME;
     324             :                                 }
     325             :                         }
     326           0 :                         else if (!(strcasecmp (keyString (key), "PUT")))
     327             :                         {
     328           0 :                                 data->uploadMethod = PUT;
     329             :                         }
     330             :                         else
     331             :                         {
     332           0 :                                 data->uploadMethod = NA;
     333             :                         }
     334             :                 }
     335             :         }
     336           2 :         key = ksLookupByName (config, "upload/filename", KDB_O_NONE);
     337           2 :         if (key)
     338             :         {
     339           0 :                 data->__uploadFileName = elektraStrDup (keyString (key));
     340           0 :                 data->uploadFileName = data->__uploadFileName;
     341             :         }
     342             :         else
     343             :         {
     344           2 :                 if (data->uploadMethod == POST)
     345             :                 {
     346           0 :                         if (!data->useLocalCopy)
     347             :                         {
     348           0 :                                 data->__uploadFileName = elektraStrDup (data->getUrl);
     349           0 :                                 data->uploadFileName = basename (data->__uploadFileName);
     350             :                         }
     351             :                         else
     352             :                         {
     353           0 :                                 data->__uploadFileName = elektraStrDup (data->path);
     354           0 :                                 data->uploadFileName = basename (data->__uploadFileName);
     355             :                         }
     356             :                 }
     357             :         }
     358             : 
     359           2 :         key = ksLookupByName (config, "/ssl/verify", KDB_O_NONE);
     360           2 :         if (key)
     361             :         {
     362           0 :                 if (!strcmp (keyString (key), "1"))
     363             :                 {
     364           0 :                         data->sslVerifyPeer = 1;
     365           0 :                         data->sslVerifyHost = 1;
     366           0 :                         data->useSSL = 1;
     367             :                 }
     368           0 :                 key = ksLookupByName (config, "/ssl/verify/peer", KDB_O_NONE);
     369           0 :                 if (key)
     370             :                 {
     371           0 :                         data->useSSL = 1;
     372           0 :                         if (!strcmp (keyString (key), "1"))
     373           0 :                                 data->sslVerifyPeer = 1;
     374           0 :                         else if (!strcmp (keyString (key), "0"))
     375           0 :                                 data->sslVerifyPeer = 0;
     376             :                 }
     377           0 :                 key = ksLookupByName (config, "/ssl/verify/host", KDB_O_NONE);
     378           0 :                 if (key)
     379             :                 {
     380           0 :                         data->useSSL = 1;
     381           0 :                         if (!strcmp (keyString (key), "1"))
     382           0 :                                 data->sslVerifyHost = 1;
     383           0 :                         else if (!strcmp (keyString (key), "0"))
     384           0 :                                 data->sslVerifyHost = 0;
     385             :                 }
     386             :         }
     387           2 :         key = ksLookupByName (config, "/prefer", KDB_O_NONE);
     388           2 :         data->preferRemote = 1;
     389           2 :         if (key && !strcasecmp (keyString (key), "local"))
     390             :         {
     391           0 :                 data->preferRemote = 0;
     392             :         }
     393           2 :         key = ksLookupByName (config, "/ssh/auth", KDB_O_NONE);
     394           2 :         if (key)
     395             :         {
     396           0 :                 if (!strcasecmp (keyString (key), "password"))
     397           0 :                         data->sshAuth = SSH_PASSWORD;
     398           0 :                 else if (!strcasecmp (keyString (key), "agent"))
     399           0 :                         data->sshAuth = SSH_AGENT;
     400           0 :                 else if (!strcasecmp (keyString (key), "pubkey"))
     401           0 :                         data->sshAuth = SSH_PUBLICKEY;
     402           0 :                 else if (!strcasecmp (keyString (key), "any"))
     403           0 :                         data->sshAuth = SSH_ANY;
     404           0 :                 else if (!strcasecmp (keyString (key), "pubkeypw"))
     405           0 :                         data->sshAuth = SSH_PUBKEYPW;
     406             :         }
     407           2 :         key = ksLookupByName (config, "/ssh/key", KDB_O_NONE);
     408           2 :         if (!key)
     409             :         {
     410           2 :                 data->keyFile = NULL;
     411             :         }
     412             :         else
     413             :         {
     414           0 :                 data->keyFile = keyString (key);
     415             :         }
     416           2 :         key = ksLookupByName (config, "/ssh/key/passwd", KDB_O_NONE);
     417           2 :         if (key)
     418             :         {
     419           0 :                 data->keyFilePasswd = keyString (key);
     420             :         }
     421             :         else
     422             :         {
     423           2 :                 data->keyFilePasswd = NULL;
     424             :         }
     425           2 :         if (data->sshAuth == SSH_PASSWORD)
     426             :         {
     427           0 :                 if (!data->password)
     428             :                 {
     429           0 :                         ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (
     430             :                                 errorKey, "No password specified for SSH password authentication in plugin configuration");
     431           0 :                         if (data->uploadFileName) elektraFree (data->__uploadFileName);
     432           0 :                         elektraFree (data);
     433           0 :                         data = NULL;
     434           0 :                         return 0;
     435             :                 }
     436             :         }
     437             : 
     438           2 :         elektraPluginSetData (handle, data);
     439           2 :         return 1;
     440             : }
     441             : 
     442           0 : static unsigned char * hashBuffer (void * buffer, size_t size)
     443             : {
     444           0 :         MD5_CTX c;
     445           0 :         unsigned char * out = elektraMalloc (MD5_DIGEST_LENGTH);
     446           0 :         MD5_Init (&c);
     447           0 :         MD5_Update (&c, buffer, size);
     448           0 :         MD5_Final (out, &c);
     449           0 :         return out;
     450             : }
     451             : 
     452           0 : static void setupSSH (CURL * curl, Data * data)
     453             : {
     454           0 :         if (data->sshAuth == SSH_PUBLICKEY || data->sshAuth == SSH_PUBKEYPW)
     455             :         {
     456           0 :                 if (data->sshAuth == SSH_PUBKEYPW)
     457           0 :                         curl_easy_setopt (curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PUBLICKEY | CURLSSH_AUTH_PASSWORD);
     458             :                 else
     459           0 :                         curl_easy_setopt (curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PUBLICKEY);
     460             : 
     461           0 :                 curl_easy_setopt (curl, CURLOPT_SSH_PRIVATE_KEYFILE, data->keyFile);
     462           0 :                 if (data->keyFilePasswd)
     463             :                 {
     464           0 :                         curl_easy_setopt (curl, CURLOPT_KEYPASSWD, data->keyFilePasswd);
     465             :                 }
     466             :         }
     467           0 :         else if (data->sshAuth == SSH_AGENT)
     468             :         {
     469           0 :                 curl_easy_setopt (curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_AGENT);
     470             :         }
     471           0 :         else if (data->sshAuth == SSH_PASSWORD)
     472             :         {
     473           0 :                 curl_easy_setopt (curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PASSWORD);
     474             :         }
     475             :         else
     476             :         {
     477           0 :                 curl_easy_setopt (curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_ANY);
     478           0 :                 if (data->keyFilePasswd)
     479             :                 {
     480           0 :                         curl_easy_setopt (curl, CURLOPT_KEYPASSWD, data->keyFilePasswd);
     481             :                 }
     482             :         }
     483           0 : }
     484             : 
     485           0 : static FILE * fetchFile (Data * data, int fd)
     486             : {
     487           0 :         CURL * curl = curl_easy_init ();
     488           0 :         if (!curl)
     489             :         {
     490           0 :                 curl_easy_cleanup (curl);
     491           0 :                 return NULL;
     492             :         }
     493             : 
     494           0 :         curl_easy_setopt (curl, CURLOPT_URL, data->getUrl);
     495           0 :         if (data->user)
     496             :         {
     497           0 :                 curl_easy_setopt (curl, CURLOPT_USERNAME, data->user);
     498             :         }
     499           0 :         if (data->password)
     500             :         {
     501           0 :                 curl_easy_setopt (curl, CURLOPT_PASSWORD, data->password);
     502             :         }
     503           0 :         if (data->getProto == PROTO_HTTPS || data->getProto == PROTO_FTPS || data->useSSL)
     504             :         {
     505           0 :                 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     506           0 :                 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     507           0 :                 curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     508             :         }
     509             :         else
     510             :         {
     511           0 :                 curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     512             :         }
     513           0 :         if (data->getProto == PROTO_SCP || data->getProto == PROTO_SFTP)
     514             :         {
     515           0 :                 setupSSH (curl, data);
     516             :         }
     517           0 :         curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, NULL);
     518           0 :         FILE * fp = fdopen (fd, "w+");
     519           0 :         curl_easy_setopt (curl, CURLOPT_WRITEDATA, fp);
     520           0 :         CURLcode res;
     521           0 :         res = curl_easy_perform (curl);
     522           0 :         long respCode = 0;
     523           0 :         curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &respCode);
     524           0 :         curl_easy_cleanup (curl);
     525           0 :         if (res != CURLE_OK || respCode != 200)
     526             :         {
     527           0 :                 fclose (fp);
     528           0 :                 return NULL;
     529             :         }
     530             :         return fp;
     531             : }
     532             : 
     533           0 : static int moveFile (const char * source, const char * dest)
     534             : {
     535           0 :         FILE * inFile = NULL;
     536           0 :         FILE * outFile = NULL;
     537           0 :         struct stat buf;
     538           0 :         if (stat (source, &buf) == -1) return -1;
     539           0 :         size_t fileSize = buf.st_size;
     540           0 :         char * buffer = elektraMalloc (fileSize);
     541           0 :         inFile = fopen (source, "rb");
     542           0 :         size_t bytesRead = 0;
     543           0 :         while (bytesRead < fileSize)
     544             :         {
     545           0 :                 size_t bytes = fread (buffer + bytesRead, 1, (size_t) fileSize, inFile);
     546           0 :                 if (bytes == 0) break;
     547           0 :                 bytesRead += bytes;
     548             :         }
     549           0 :         if (bytesRead < fileSize)
     550             :         {
     551           0 :                 elektraFree (buffer);
     552           0 :                 fclose (inFile);
     553           0 :                 return -1;
     554             :         }
     555           0 :         fclose (inFile);
     556           0 :         outFile = fopen (dest, "wb+");
     557             : 
     558           0 :         size_t bytesWritten = 0;
     559           0 :         while (bytesWritten < fileSize)
     560             :         {
     561           0 :                 size_t bytes = fwrite (buffer, 1, fileSize, outFile);
     562           0 :                 if (bytes == 0) break;
     563           0 :                 bytesWritten += bytes;
     564             :         }
     565           0 :         fclose (outFile);
     566           0 :         elektraFree (buffer);
     567             : 
     568           0 :         if (bytesWritten < fileSize)
     569             :         {
     570             :                 return -1;
     571             :         }
     572           0 :         if (unlink (source))
     573             :         {
     574           0 :                 return -1;
     575             :         }
     576             :         return 0;
     577             : }
     578             : 
     579          31 : int elektraCurlgetGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey)
     580          31 : {
     581          31 :         if (!elektraStrCmp (keyName (parentKey), "system:/elektra/modules/curlget"))
     582             :         {
     583          31 :                 KeySet * contract =
     584          31 :                         ksNew (30, keyNew ("system:/elektra/modules/curlget", KEY_VALUE, "curlget plugin waits for your orders", KEY_END),
     585             :                                keyNew ("system:/elektra/modules/curlget/exports", KEY_END),
     586             :                                keyNew ("system:/elektra/modules/curlget/exports/get", KEY_FUNC, elektraCurlgetGet, KEY_END),
     587             :                                keyNew ("system:/elektra/modules/curlget/exports/set", KEY_FUNC, elektraCurlgetSet, KEY_END),
     588             :                                keyNew ("system:/elektra/modules/curlget/exports/commit", KEY_FUNC, elektraCurlgetCommit, KEY_END),
     589             :                                keyNew ("system:/elektra/modules/curlget/exports/open", KEY_FUNC, elektraCurlgetOpen, KEY_END),
     590             :                                keyNew ("system:/elektra/modules/curlget/exports/close", KEY_FUNC, elektraCurlgetClose, KEY_END),
     591             :                                keyNew ("system:/elektra/modules/curlget/exports/error", KEY_FUNC, elektraCurlgetError, KEY_END),
     592             :                                keyNew ("system:/elektra/modules/curlget/exports/checkfile", KEY_FUNC, elektraCurlgetCheckFile, KEY_END),
     593             : #include ELEKTRA_README
     594             :                                keyNew ("system:/elektra/modules/curlget/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     595          31 :                 ksAppend (returned, contract);
     596          31 :                 ksDel (contract);
     597             : 
     598          31 :                 return 1; // success
     599             :         }
     600             : 
     601           0 :         Data * data = elektraPluginGetData (handle);
     602           0 :         if (!data)
     603             :         {
     604             :                 return -1;
     605             :         }
     606           0 :         int fd = 0;
     607           0 :         char name[] = TMP_NAME;
     608           0 :         fd = mkstemp (name);
     609           0 :         if (*(data->lastHash)) unlink (data->tmpFile);
     610           0 :         data->tmpFile = name;
     611             : 
     612           0 :         if (data->path) keySetString (parentKey, data->path);
     613           0 :         if (elektraResolveFilename (parentKey, ELEKTRA_RESOLVER_TEMPFILE_NONE) == -1)
     614             :         {
     615             :                 return -1;
     616             :         }
     617           0 :         if (data->path) elektraFree (data->path);
     618           0 :         data->path = elektraStrDup (keyString (parentKey));
     619             : 
     620           0 :         if (fd == -1)
     621             :         {
     622           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for reading. Reason: %s", data->path, strerror (errno));
     623           0 :                 return -1;
     624             :         }
     625           0 :         FILE * fp = fetchFile (data, fd);
     626           0 :         if (!fp)
     627             :         {
     628           0 :                 close (fd);
     629           0 :                 unlink (data->tmpFile);
     630           0 :                 data->tmpFile = NULL;
     631           0 :                 fp = fopen (data->path, "rb");
     632           0 :                 if (fp && data->useLocalCopy)
     633             :                 {
     634           0 :                         ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to fetch configuration from %s, falling back to local copy %s\n",
     635             :                                                        data->getUrl, data->path);
     636             :                 }
     637             :                 else
     638             :                 {
     639           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to read configuration. Reason: %s\n", strerror (errno));
     640           0 :                         return -1;
     641             :                 }
     642             :         }
     643           0 :         fseek (fp, 0L, SEEK_END);
     644           0 :         size_t size = ftell (fp);
     645           0 :         rewind (fp);
     646           0 :         unsigned char buffer[size];
     647           0 :         fread (&buffer, sizeof (char), size, fp);
     648           0 :         fclose (fp);
     649           0 :         unsigned char * hash = hashBuffer (buffer, size);
     650           0 :         if (!*(data->lastHash))
     651             :         {
     652           0 :                 memcpy (data->lastHash, hash, MD5_DIGEST_LENGTH);
     653           0 :                 if (data->useLocalCopy)
     654             :                 {
     655           0 :                         moveFile (data->tmpFile, data->path);
     656             :                 }
     657             :                 else
     658             :                 {
     659           0 :                         if (data->path) elektraFree (data->path);
     660           0 :                         data->path = elektraStrDup (data->tmpFile);
     661             :                 }
     662           0 :                 data->tmpFile = NULL;
     663           0 :                 keySetString (parentKey, data->path);
     664             :         }
     665           0 :         else if (data->tmpFile)
     666             :         {
     667           0 :                 if (strncmp ((char *) data->lastHash, (char *) hash, MD5_DIGEST_LENGTH))
     668             :                 {
     669             :                         // remote file has changed
     670             :                         // if preferRemote is set: replace local copy with
     671             :                         // the remote version.
     672           0 :                         if (data->preferRemote)
     673             :                         {
     674           0 :                                 moveFile (data->tmpFile, data->path);
     675           0 :                                 data->tmpFile = NULL;
     676           0 :                                 keySetString (parentKey, data->path);
     677           0 :                                 memcpy (data->lastHash, hash, MD5_DIGEST_LENGTH);
     678             :                         }
     679             :                         else
     680             :                         {
     681             :                                 // else drop remote version
     682             :                                 goto UNLINK_TMP;
     683             :                         }
     684             :                 }
     685             :                 else
     686             :                 {
     687           0 :                 UNLINK_TMP:
     688             :                         // remote file is the same as our local copy
     689           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     690           0 :                         data->tmpFile = NULL;
     691           0 :                         keySetString (parentKey, data->path);
     692             :                 }
     693             :         }
     694           0 :         elektraFree (hash);
     695           0 :         return 1; // success
     696             : }
     697             : 
     698           0 : int elektraCurlgetSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     699             : {
     700             :         // get all keys
     701           0 :         Data * data = elektraPluginGetData (handle);
     702           0 :         if (!data) return -1;
     703           0 :         int retval = 1;
     704           0 :         if (data->setPhase == 0)
     705             :         {
     706           0 :                 char name[] = TMP_NAME;
     707           0 :                 int fd = mkstemp (name);
     708           0 :                 if (data->tmpFile) unlink (data->tmpFile);
     709           0 :                 data->tmpFile = name;
     710           0 :                 FILE * fp = fetchFile (data, fd);
     711           0 :                 if (fp)
     712           0 :                 {
     713           0 :                         fseek (fp, 0L, SEEK_END);
     714           0 :                         size_t size = ftell (fp);
     715           0 :                         rewind (fp);
     716           0 :                         unsigned char buffer[size];
     717           0 :                         fread (&buffer, sizeof (char), size, fp);
     718           0 :                         fclose (fp);
     719           0 :                         unsigned char * hash = hashBuffer (buffer, size);
     720           0 :                         ++(data->setPhase);
     721           0 :                         if (strncmp ((char *) data->lastHash, (char *) hash, MD5_DIGEST_LENGTH))
     722             :                         {
     723           0 :                                 ELEKTRA_SET_CONFLICTING_STATE_ERROR (parentKey, "Remote file has changed");
     724           0 :                                 retval = -1;
     725             :                         }
     726           0 :                         elektraFree (hash);
     727           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     728           0 :                         data->tmpFile = NULL;
     729           0 :                         keySetString (parentKey, name);
     730             :                 }
     731             :                 else
     732             :                 {
     733           0 :                         close (fd);
     734           0 :                         ELEKTRA_SET_CONFLICTING_STATE_ERRORF (
     735             :                                 parentKey, "Failed to fetch configuration from %s. Aborting because consistency can't be ensured",
     736             :                                 data->getUrl);
     737           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     738           0 :                         data->tmpFile = NULL;
     739           0 :                         if (data->useLocalCopy) keySetString (parentKey, data->path);
     740             : 
     741             :                         retval = -1;
     742             :                 }
     743           0 :                 close (fd);
     744           0 :                 if (retval != -1)
     745             :                 {
     746           0 :                         keySetString (parentKey, name);
     747           0 :                         data->tmpFile = name;
     748           0 :                         retval = 1;
     749             :                 }
     750           0 :                 if (!data->useLocalCopy && data->path)
     751             :                 {
     752           0 :                         unlink (data->path);
     753           0 :                         if (data->path) elektraFree (data->path);
     754           0 :                         data->path = NULL;
     755             :                 }
     756             :         }
     757           0 :         else if (data->setPhase == 1)
     758             :         {
     759           0 :                 FILE * fp;
     760           0 :                 const char * tmpFile = keyString (parentKey);
     761           0 :                 fp = fopen (tmpFile, "rb");
     762           0 :                 if (!fp)
     763             :                 {
     764           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for reading. Reason: %s", tmpFile, strerror (errno));
     765           0 :                         return -1;
     766             :                 }
     767           0 :                 fseek (fp, 0L, SEEK_END);
     768           0 :                 size_t size = ftell (fp);
     769           0 :                 rewind (fp);
     770           0 :                 CURL * curl;
     771           0 :                 CURLcode res = 0;
     772           0 :                 curl = curl_easy_init ();
     773           0 :                 if (curl)
     774             :                 {
     775             : 
     776           0 :                         if (data->user)
     777             :                         {
     778           0 :                                 curl_easy_setopt (curl, CURLOPT_USERNAME, data->user);
     779             :                         }
     780           0 :                         if (data->password)
     781             :                         {
     782           0 :                                 curl_easy_setopt (curl, CURLOPT_PASSWORD, data->password);
     783             :                         }
     784           0 :                         curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
     785           0 :                         if (data->putProto == PROTO_HTTP || data->putProto == PROTO_HTTPS)
     786             :                         {
     787           0 :                                 if (data->putProto == PROTO_HTTPS || data->useSSL)
     788             :                                 {
     789           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     790           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     791           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     792             :                                 }
     793             :                                 else
     794             :                                 {
     795           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     796             :                                 }
     797             : 
     798           0 :                                 curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     799             : 
     800           0 :                                 if (data->uploadMethod == POST)
     801             :                                 {
     802           0 :                                         struct curl_httppost * formpost = NULL;
     803           0 :                                         struct curl_httppost * lastptr = NULL;
     804           0 :                                         struct curl_slist * headerlist = NULL;
     805           0 :                                         static const char buf[] = "Expect:";
     806           0 :                                         curl_formadd (&formpost, &lastptr, CURLFORM_COPYNAME, data->postFieldName, CURLFORM_FILE, tmpFile,
     807             :                                                       CURLFORM_FILENAME, data->uploadFileName, CURLFORM_END);
     808           0 :                                         headerlist = curl_slist_append (headerlist, buf);
     809           0 :                                         curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerlist);
     810           0 :                                         curl_easy_setopt (curl, CURLOPT_HTTPPOST, formpost);
     811           0 :                                         curl_easy_setopt (curl, CURLOPT_AUTOREFERER, 1L);
     812           0 :                                         res = curl_easy_perform (curl);
     813           0 :                                         if (res != CURLE_OK)
     814             :                                         {
     815             : 
     816           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP POST) failed: %s\n",
     817             :                                                                              curl_easy_strerror (res));
     818           0 :                                                 retval = -1;
     819             :                                         }
     820           0 :                                         curl_formfree (formpost);
     821           0 :                                         curl_slist_free_all (headerlist);
     822             :                                 }
     823           0 :                                 else if (data->uploadMethod == PUT)
     824             :                                 {
     825           0 :                                         curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     826           0 :                                         curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     827           0 :                                         curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, "PUT");
     828           0 :                                         curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     829           0 :                                         res = curl_easy_perform (curl);
     830           0 :                                         if (res != CURLE_OK)
     831             :                                         {
     832           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP PUT) failed. Reason: %s",
     833             :                                                                              curl_easy_strerror (res));
     834           0 :                                                 retval = -1;
     835             :                                         }
     836             :                                 }
     837             :                                 else
     838             :                                 {
     839           0 :                                         curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     840           0 :                                         curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     841           0 :                                         curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     842           0 :                                         res = curl_easy_perform (curl);
     843           0 :                                         if (res != CURLE_OK)
     844             :                                         {
     845           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP PUT) failed. Reason: %s",
     846             :                                                                              curl_easy_strerror (res));
     847           0 :                                                 retval = -1;
     848             :                                         }
     849             :                                 }
     850             :                         }
     851           0 :                         else if (data->putProto == PROTO_FTP || data->putProto == PROTO_FTPS)
     852             :                         {
     853           0 :                                 if (data->putProto == PROTO_FTPS || data->useSSL)
     854             :                                 {
     855           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     856           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     857           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     858             :                                 }
     859             :                                 else
     860             :                                 {
     861           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     862             :                                 }
     863             : 
     864           0 :                                 if (data->uploadFileName)
     865           0 :                                 {
     866           0 :                                         char uploadUrl[strlen (data->uploadUrl) + strlen (data->uploadFileName) + 2];
     867           0 :                                         snprintf (uploadUrl, sizeof (uploadUrl), "%s/%s", data->uploadUrl, data->uploadFileName);
     868           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, uploadUrl);
     869             :                                 }
     870             :                                 else
     871             :                                 {
     872           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     873             :                                 }
     874             : 
     875           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     876           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     877           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     878           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     879           0 :                                 res = curl_easy_perform (curl);
     880           0 :                                 if (res != CURLE_OK)
     881             :                                 {
     882           0 :                                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (FTP PUT) failed. Reason: %s",
     883             :                                                                      curl_easy_strerror (res));
     884           0 :                                         retval = -1;
     885             :                                 }
     886             :                         }
     887           0 :                         else if (data->putProto == PROTO_SCP || data->putProto == PROTO_SFTP)
     888             :                         {
     889           0 :                                 setupSSH (curl, data);
     890           0 :                                 if (data->uploadFileName)
     891           0 :                                 {
     892           0 :                                         char uploadUrl[strlen (data->uploadUrl) + strlen (data->uploadFileName) + 2];
     893           0 :                                         snprintf (uploadUrl, sizeof (uploadUrl), "%s/%s", data->uploadUrl, data->uploadFileName);
     894           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, uploadUrl);
     895             :                                 }
     896             :                                 else
     897             :                                 {
     898           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     899             :                                 }
     900             : 
     901           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     902           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     903           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     904           0 :                                 res = curl_easy_perform (curl);
     905           0 :                                 if (res != CURLE_OK)
     906             :                                 {
     907           0 :                                         if (data->putProto == PROTO_SCP)
     908           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (SCP) failed. Reason: %s",
     909             :                                                                              curl_easy_strerror (res));
     910           0 :                                         else if (data->putProto == PROTO_SFTP)
     911           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (SFTP) failed. Reason: %s",
     912             :                                                                              curl_easy_strerror (res));
     913             :                                         retval = -1;
     914             :                                 }
     915             :                         }
     916           0 :                         else if (data->putProto == PROTO_SMB) // lgtm [cpp/empty-block]
     917             :                         {
     918             :                         }
     919             :                         else
     920             :                         {
     921           0 :                                 curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     922           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     923           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     924           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     925           0 :                                 res = curl_easy_perform (curl);
     926           0 :                                 if (res != CURLE_OK)
     927             :                                 {
     928           0 :                                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (default) . Reason: %s",
     929             :                                                                      curl_easy_strerror (res));
     930           0 :                                         retval = -1;
     931             :                                 }
     932             :                         }
     933           0 :                         curl_easy_cleanup (curl);
     934           0 :                         fclose (fp);
     935             :                 }
     936           0 :                 if (retval != -1)
     937             :                 {
     938           0 :                         if (data->useLocalCopy)
     939             :                         {
     940           0 :                                 if (moveFile (tmpFile, data->path))
     941             :                                 {
     942           0 :                                         if (data->tmpFile)
     943             :                                         {
     944           0 :                                                 unlink (data->tmpFile);
     945             :                                         }
     946             :                                 }
     947           0 :                                 data->tmpFile = NULL;
     948           0 :                                 keySetString (parentKey, data->path);
     949             :                         }
     950             :                         else
     951             :                         {
     952           0 :                                 if (data->tmpFile)
     953             :                                 {
     954           0 :                                         unlink (data->tmpFile);
     955           0 :                                         data->tmpFile = NULL;
     956             :                                 }
     957           0 :                                 if (data->path)
     958             :                                 {
     959           0 :                                         unlink (data->path);
     960           0 :                                         elektraFree (data->path);
     961           0 :                                         data->path = NULL;
     962             :                                 }
     963           0 :                                 if (tmpFile)
     964             :                                 {
     965           0 :                                         unlink (tmpFile);
     966             :                                 }
     967             :                         }
     968             :                 }
     969             :                 else
     970             :                 {
     971           0 :                         if (!data->useLocalCopy)
     972             :                         {
     973           0 :                                 if (data->tmpFile)
     974             :                                 {
     975           0 :                                         unlink (data->tmpFile);
     976           0 :                                         data->tmpFile = NULL;
     977             :                                 }
     978           0 :                                 if (data->path)
     979             :                                 {
     980           0 :                                         unlink (data->path);
     981           0 :                                         elektraFree (data->path);
     982           0 :                                         data->path = NULL;
     983             :                                 }
     984           0 :                                 if (tmpFile)
     985             :                                 {
     986           0 :                                         unlink (tmpFile);
     987             :                                 }
     988             :                         }
     989             :                         else
     990             :                         {
     991           0 :                                 if (tmpFile) unlink (tmpFile);
     992           0 :                                 if (data->tmpFile)
     993             :                                 {
     994           0 :                                         unlink (data->tmpFile);
     995           0 :                                         data->tmpFile = NULL;
     996             :                                 }
     997             :                         }
     998             :                 }
     999             :         }
    1000             : 
    1001             :         return retval; // success
    1002             : }
    1003             : 
    1004           0 : int elektraCurlgetCommit (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
    1005             : {
    1006           0 :         return elektraCurlgetSet (handle, returned, parentKey);
    1007             : }
    1008             : 
    1009          31 : Plugin * ELEKTRA_PLUGIN_EXPORT
    1010             : {
    1011             :         // clang-format off
    1012          31 :     return elektraPluginExport ("curlget",
    1013             :             ELEKTRA_PLUGIN_GET, &elektraCurlgetGet,
    1014             :             ELEKTRA_PLUGIN_SET, &elektraCurlgetSet,
    1015             :             ELEKTRA_PLUGIN_OPEN,        &elektraCurlgetOpen,
    1016             :             ELEKTRA_PLUGIN_CLOSE,       &elektraCurlgetClose,
    1017             :             ELEKTRA_PLUGIN_ERROR,       &elektraCurlgetError,
    1018             :             ELEKTRA_PLUGIN_COMMIT,      &elektraCurlgetCommit,
    1019             :             ELEKTRA_PLUGIN_END);
    1020             : }
    1021             : 

Generated by: LCOV version 1.13