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: 2022-05-21 16:19:22 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          29 : int elektraCurlgetClose (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED)
     142             : {
     143          29 :         Data * data = elektraPluginGetData (handle);
     144          29 :         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          29 : int elektraCurlgetOpen (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     288             : {
     289          29 :         KeySet * config = elektraPluginGetConfig (handle);
     290          29 :         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             : #if VERBOSE
     494             :         curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
     495             : #endif
     496           0 :         curl_easy_setopt (curl, CURLOPT_URL, data->getUrl);
     497           0 :         if (data->user)
     498             :         {
     499           0 :                 curl_easy_setopt (curl, CURLOPT_USERNAME, data->user);
     500             :         }
     501           0 :         if (data->password)
     502             :         {
     503           0 :                 curl_easy_setopt (curl, CURLOPT_PASSWORD, data->password);
     504             :         }
     505           0 :         if (data->getProto == PROTO_HTTPS || data->getProto == PROTO_FTPS || data->useSSL)
     506             :         {
     507           0 :                 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     508           0 :                 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     509           0 :                 curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     510             :         }
     511             :         else
     512             :         {
     513           0 :                 curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     514             :         }
     515           0 :         if (data->getProto == PROTO_SCP || data->getProto == PROTO_SFTP)
     516             :         {
     517           0 :                 setupSSH (curl, data);
     518             :         }
     519           0 :         curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, NULL);
     520           0 :         FILE * fp = fdopen (fd, "w+");
     521           0 :         curl_easy_setopt (curl, CURLOPT_WRITEDATA, fp);
     522           0 :         CURLcode res;
     523           0 :         res = curl_easy_perform (curl);
     524           0 :         long respCode = 0;
     525           0 :         curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &respCode);
     526           0 :         curl_easy_cleanup (curl);
     527           0 :         if (res != CURLE_OK || respCode != 200)
     528             :         {
     529           0 :                 fclose (fp);
     530           0 :                 return NULL;
     531             :         }
     532             :         return fp;
     533             : }
     534             : 
     535           0 : static int moveFile (const char * source, const char * dest)
     536             : {
     537           0 :         FILE * inFile = NULL;
     538           0 :         FILE * outFile = NULL;
     539           0 :         struct stat buf;
     540           0 :         if (stat (source, &buf) == -1) return -1;
     541           0 :         size_t fileSize = buf.st_size;
     542           0 :         char * buffer = elektraMalloc (fileSize);
     543           0 :         inFile = fopen (source, "rb");
     544           0 :         size_t bytesRead = 0;
     545           0 :         while (bytesRead < fileSize)
     546             :         {
     547           0 :                 size_t bytes = fread (buffer + bytesRead, 1, (size_t) fileSize, inFile);
     548           0 :                 if (bytes == 0) break;
     549           0 :                 bytesRead += bytes;
     550             :         }
     551           0 :         if (bytesRead < fileSize)
     552             :         {
     553           0 :                 elektraFree (buffer);
     554           0 :                 fclose (inFile);
     555           0 :                 return -1;
     556             :         }
     557           0 :         fclose (inFile);
     558           0 :         outFile = fopen (dest, "wb+");
     559             : 
     560           0 :         size_t bytesWritten = 0;
     561           0 :         while (bytesWritten < fileSize)
     562             :         {
     563           0 :                 size_t bytes = fwrite (buffer, 1, fileSize, outFile);
     564           0 :                 if (bytes == 0) break;
     565           0 :                 bytesWritten += bytes;
     566             :         }
     567           0 :         fclose (outFile);
     568           0 :         elektraFree (buffer);
     569             : 
     570           0 :         if (bytesWritten < fileSize)
     571             :         {
     572             :                 return -1;
     573             :         }
     574           0 :         if (unlink (source))
     575             :         {
     576           0 :                 return -1;
     577             :         }
     578             :         return 0;
     579             : }
     580             : 
     581          29 : int elektraCurlgetGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey)
     582          29 : {
     583          29 :         if (!elektraStrCmp (keyName (parentKey), "system:/elektra/modules/curlget"))
     584             :         {
     585          29 :                 KeySet * contract =
     586          29 :                         ksNew (30, keyNew ("system:/elektra/modules/curlget", KEY_VALUE, "curlget plugin waits for your orders", KEY_END),
     587             :                                keyNew ("system:/elektra/modules/curlget/exports", KEY_END),
     588             :                                keyNew ("system:/elektra/modules/curlget/exports/get", KEY_FUNC, elektraCurlgetGet, KEY_END),
     589             :                                keyNew ("system:/elektra/modules/curlget/exports/set", KEY_FUNC, elektraCurlgetSet, KEY_END),
     590             :                                keyNew ("system:/elektra/modules/curlget/exports/commit", KEY_FUNC, elektraCurlgetCommit, KEY_END),
     591             :                                keyNew ("system:/elektra/modules/curlget/exports/open", KEY_FUNC, elektraCurlgetOpen, KEY_END),
     592             :                                keyNew ("system:/elektra/modules/curlget/exports/close", KEY_FUNC, elektraCurlgetClose, KEY_END),
     593             :                                keyNew ("system:/elektra/modules/curlget/exports/error", KEY_FUNC, elektraCurlgetError, KEY_END),
     594             :                                keyNew ("system:/elektra/modules/curlget/exports/checkfile", KEY_FUNC, elektraCurlgetCheckFile, KEY_END),
     595             : #include ELEKTRA_README
     596             :                                keyNew ("system:/elektra/modules/curlget/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
     597          29 :                 ksAppend (returned, contract);
     598          29 :                 ksDel (contract);
     599             : 
     600          29 :                 return 1; // success
     601             :         }
     602             : 
     603           0 :         Data * data = elektraPluginGetData (handle);
     604           0 :         if (!data)
     605             :         {
     606             :                 return -1;
     607             :         }
     608           0 :         int fd = 0;
     609           0 :         char name[] = TMP_NAME;
     610           0 :         fd = mkstemp (name);
     611           0 :         if (*(data->lastHash)) unlink (data->tmpFile);
     612           0 :         data->tmpFile = name;
     613             : 
     614           0 :         if (data->path) keySetString (parentKey, data->path);
     615           0 :         if (elektraResolveFilename (parentKey, ELEKTRA_RESOLVER_TEMPFILE_NONE) == -1)
     616             :         {
     617             :                 return -1;
     618             :         }
     619           0 :         if (data->path) elektraFree (data->path);
     620           0 :         data->path = elektraStrDup (keyString (parentKey));
     621             : 
     622           0 :         if (fd == -1)
     623             :         {
     624           0 :                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for reading. Reason: %s", data->path, strerror (errno));
     625           0 :                 return -1;
     626             :         }
     627           0 :         FILE * fp = fetchFile (data, fd);
     628           0 :         if (!fp)
     629             :         {
     630           0 :                 close (fd);
     631           0 :                 unlink (data->tmpFile);
     632           0 :                 data->tmpFile = NULL;
     633           0 :                 fp = fopen (data->path, "rb");
     634           0 :                 if (fp && data->useLocalCopy)
     635             :                 {
     636           0 :                         ELEKTRA_ADD_RESOURCE_WARNINGF (parentKey, "Failed to fetch configuration from %s, falling back to local copy %s\n",
     637             :                                                        data->getUrl, data->path);
     638             :                 }
     639             :                 else
     640             :                 {
     641           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to read configuration. Reason: %s\n", strerror (errno));
     642           0 :                         return -1;
     643             :                 }
     644             :         }
     645           0 :         fseek (fp, 0L, SEEK_END);
     646           0 :         size_t size = ftell (fp);
     647           0 :         rewind (fp);
     648           0 :         unsigned char buffer[size];
     649           0 :         fread (&buffer, sizeof (char), size, fp);
     650           0 :         fclose (fp);
     651           0 :         unsigned char * hash = hashBuffer (buffer, size);
     652           0 :         if (!*(data->lastHash))
     653             :         {
     654           0 :                 memcpy (data->lastHash, hash, MD5_DIGEST_LENGTH);
     655           0 :                 if (data->useLocalCopy)
     656             :                 {
     657           0 :                         moveFile (data->tmpFile, data->path);
     658             :                 }
     659             :                 else
     660             :                 {
     661           0 :                         if (data->path) elektraFree (data->path);
     662           0 :                         data->path = elektraStrDup (data->tmpFile);
     663             :                 }
     664           0 :                 data->tmpFile = NULL;
     665           0 :                 keySetString (parentKey, data->path);
     666             :         }
     667           0 :         else if (data->tmpFile)
     668             :         {
     669           0 :                 if (strncmp ((char *) data->lastHash, (char *) hash, MD5_DIGEST_LENGTH))
     670             :                 {
     671             :                         // remote file has changed
     672             :                         // if preferRemote is set: replace local copy with
     673             :                         // the remote version.
     674           0 :                         if (data->preferRemote)
     675             :                         {
     676           0 :                                 moveFile (data->tmpFile, data->path);
     677           0 :                                 data->tmpFile = NULL;
     678           0 :                                 keySetString (parentKey, data->path);
     679           0 :                                 memcpy (data->lastHash, hash, MD5_DIGEST_LENGTH);
     680             :                         }
     681             :                         else
     682             :                         {
     683             :                                 // else drop remote version
     684             :                                 goto UNLINK_TMP;
     685             :                         }
     686             :                 }
     687             :                 else
     688             :                 {
     689           0 :                 UNLINK_TMP:
     690             :                         // remote file is the same as our local copy
     691           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     692           0 :                         data->tmpFile = NULL;
     693           0 :                         keySetString (parentKey, data->path);
     694             :                 }
     695             :         }
     696           0 :         elektraFree (hash);
     697           0 :         return 1; // success
     698             : }
     699             : 
     700           0 : int elektraCurlgetSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
     701             : {
     702             :         // get all keys
     703           0 :         Data * data = elektraPluginGetData (handle);
     704           0 :         if (!data) return -1;
     705           0 :         int retval = 1;
     706           0 :         if (data->setPhase == 0)
     707             :         {
     708           0 :                 char name[] = TMP_NAME;
     709           0 :                 int fd = mkstemp (name);
     710           0 :                 if (data->tmpFile) unlink (data->tmpFile);
     711           0 :                 data->tmpFile = name;
     712           0 :                 FILE * fp = fetchFile (data, fd);
     713           0 :                 if (fp)
     714           0 :                 {
     715           0 :                         fseek (fp, 0L, SEEK_END);
     716           0 :                         size_t size = ftell (fp);
     717           0 :                         rewind (fp);
     718           0 :                         unsigned char buffer[size];
     719           0 :                         fread (&buffer, sizeof (char), size, fp);
     720           0 :                         fclose (fp);
     721           0 :                         unsigned char * hash = hashBuffer (buffer, size);
     722           0 :                         ++(data->setPhase);
     723           0 :                         if (strncmp ((char *) data->lastHash, (char *) hash, MD5_DIGEST_LENGTH))
     724             :                         {
     725           0 :                                 ELEKTRA_SET_CONFLICTING_STATE_ERROR (parentKey, "Remote file has changed");
     726           0 :                                 retval = -1;
     727             :                         }
     728           0 :                         elektraFree (hash);
     729           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     730           0 :                         data->tmpFile = NULL;
     731           0 :                         keySetString (parentKey, name);
     732             :                 }
     733             :                 else
     734             :                 {
     735           0 :                         close (fd);
     736           0 :                         ELEKTRA_SET_CONFLICTING_STATE_ERRORF (
     737             :                                 parentKey, "Failed to fetch configuration from %s. Aborting because consistency can't be ensured",
     738             :                                 data->getUrl);
     739           0 :                         if (data->tmpFile) unlink (data->tmpFile);
     740           0 :                         data->tmpFile = NULL;
     741           0 :                         if (data->useLocalCopy) keySetString (parentKey, data->path);
     742             : 
     743             :                         retval = -1;
     744             :                 }
     745           0 :                 close (fd);
     746           0 :                 if (retval != -1)
     747             :                 {
     748           0 :                         keySetString (parentKey, name);
     749           0 :                         data->tmpFile = name;
     750           0 :                         retval = 1;
     751             :                 }
     752           0 :                 if (!data->useLocalCopy && data->path)
     753             :                 {
     754           0 :                         unlink (data->path);
     755           0 :                         if (data->path) elektraFree (data->path);
     756           0 :                         data->path = NULL;
     757             :                 }
     758             :         }
     759           0 :         else if (data->setPhase == 1)
     760             :         {
     761           0 :                 FILE * fp;
     762           0 :                 const char * tmpFile = keyString (parentKey);
     763           0 :                 fp = fopen (tmpFile, "rb");
     764           0 :                 if (!fp)
     765             :                 {
     766           0 :                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Failed to open %s for reading. Reason: %s", tmpFile, strerror (errno));
     767           0 :                         return -1;
     768             :                 }
     769           0 :                 fseek (fp, 0L, SEEK_END);
     770           0 :                 size_t size = ftell (fp);
     771           0 :                 rewind (fp);
     772           0 :                 CURL * curl;
     773           0 :                 CURLcode res = 0;
     774           0 :                 curl = curl_easy_init ();
     775           0 :                 if (curl)
     776             :                 {
     777             : #if VERBOSE
     778             :                         curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
     779             : #endif
     780           0 :                         if (data->user)
     781             :                         {
     782           0 :                                 curl_easy_setopt (curl, CURLOPT_USERNAME, data->user);
     783             :                         }
     784           0 :                         if (data->password)
     785             :                         {
     786           0 :                                 curl_easy_setopt (curl, CURLOPT_PASSWORD, data->password);
     787             :                         }
     788           0 :                         curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
     789           0 :                         if (data->putProto == PROTO_HTTP || data->putProto == PROTO_HTTPS)
     790             :                         {
     791           0 :                                 if (data->putProto == PROTO_HTTPS || data->useSSL)
     792             :                                 {
     793           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     794           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     795           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     796             :                                 }
     797             :                                 else
     798             :                                 {
     799           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     800             :                                 }
     801             : 
     802           0 :                                 curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     803             : 
     804           0 :                                 if (data->uploadMethod == POST)
     805             :                                 {
     806           0 :                                         struct curl_httppost * formpost = NULL;
     807           0 :                                         struct curl_httppost * lastptr = NULL;
     808           0 :                                         struct curl_slist * headerlist = NULL;
     809           0 :                                         static const char buf[] = "Expect:";
     810           0 :                                         curl_formadd (&formpost, &lastptr, CURLFORM_COPYNAME, data->postFieldName, CURLFORM_FILE, tmpFile,
     811             :                                                       CURLFORM_FILENAME, data->uploadFileName, CURLFORM_END);
     812           0 :                                         headerlist = curl_slist_append (headerlist, buf);
     813           0 :                                         curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerlist);
     814           0 :                                         curl_easy_setopt (curl, CURLOPT_HTTPPOST, formpost);
     815           0 :                                         curl_easy_setopt (curl, CURLOPT_AUTOREFERER, 1L);
     816           0 :                                         res = curl_easy_perform (curl);
     817           0 :                                         if (res != CURLE_OK)
     818             :                                         {
     819             : 
     820           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP POST) failed: %s\n",
     821             :                                                                              curl_easy_strerror (res));
     822           0 :                                                 retval = -1;
     823             :                                         }
     824           0 :                                         curl_formfree (formpost);
     825           0 :                                         curl_slist_free_all (headerlist);
     826             :                                 }
     827           0 :                                 else if (data->uploadMethod == PUT)
     828             :                                 {
     829           0 :                                         curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     830           0 :                                         curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     831           0 :                                         curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, "PUT");
     832           0 :                                         curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     833           0 :                                         res = curl_easy_perform (curl);
     834           0 :                                         if (res != CURLE_OK)
     835             :                                         {
     836           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP PUT) failed. Reason: %s",
     837             :                                                                              curl_easy_strerror (res));
     838           0 :                                                 retval = -1;
     839             :                                         }
     840             :                                 }
     841             :                                 else
     842             :                                 {
     843           0 :                                         curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     844           0 :                                         curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     845           0 :                                         curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     846           0 :                                         res = curl_easy_perform (curl);
     847           0 :                                         if (res != CURLE_OK)
     848             :                                         {
     849           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (HTTP PUT) failed. Reason: %s",
     850             :                                                                              curl_easy_strerror (res));
     851           0 :                                                 retval = -1;
     852             :                                         }
     853             :                                 }
     854             :                         }
     855           0 :                         else if (data->putProto == PROTO_FTP || data->putProto == PROTO_FTPS)
     856             :                         {
     857           0 :                                 if (data->putProto == PROTO_FTPS || data->useSSL)
     858             :                                 {
     859           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, (long) data->sslVerifyPeer);
     860           0 :                                         curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, (long) data->sslVerifyHost);
     861           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
     862             :                                 }
     863             :                                 else
     864             :                                 {
     865           0 :                                         curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
     866             :                                 }
     867             : 
     868           0 :                                 if (data->uploadFileName)
     869           0 :                                 {
     870           0 :                                         char uploadUrl[strlen (data->uploadUrl) + strlen (data->uploadFileName) + 2];
     871           0 :                                         snprintf (uploadUrl, sizeof (uploadUrl), "%s/%s", data->uploadUrl, data->uploadFileName);
     872           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, uploadUrl);
     873             :                                 }
     874             :                                 else
     875             :                                 {
     876           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     877             :                                 }
     878             : 
     879           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     880           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     881           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     882           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     883           0 :                                 res = curl_easy_perform (curl);
     884           0 :                                 if (res != CURLE_OK)
     885             :                                 {
     886           0 :                                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (FTP PUT) failed. Reason: %s",
     887             :                                                                      curl_easy_strerror (res));
     888           0 :                                         retval = -1;
     889             :                                 }
     890             :                         }
     891           0 :                         else if (data->putProto == PROTO_SCP || data->putProto == PROTO_SFTP)
     892             :                         {
     893           0 :                                 setupSSH (curl, data);
     894           0 :                                 if (data->uploadFileName)
     895           0 :                                 {
     896           0 :                                         char uploadUrl[strlen (data->uploadUrl) + strlen (data->uploadFileName) + 2];
     897           0 :                                         snprintf (uploadUrl, sizeof (uploadUrl), "%s/%s", data->uploadUrl, data->uploadFileName);
     898           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, uploadUrl);
     899             :                                 }
     900             :                                 else
     901             :                                 {
     902           0 :                                         curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     903             :                                 }
     904             : 
     905           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     906           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     907           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     908           0 :                                 res = curl_easy_perform (curl);
     909           0 :                                 if (res != CURLE_OK)
     910             :                                 {
     911           0 :                                         if (data->putProto == PROTO_SCP)
     912           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (SCP) failed. Reason: %s",
     913             :                                                                              curl_easy_strerror (res));
     914           0 :                                         else if (data->putProto == PROTO_SFTP)
     915           0 :                                                 ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (SFTP) failed. Reason: %s",
     916             :                                                                              curl_easy_strerror (res));
     917             :                                         retval = -1;
     918             :                                 }
     919             :                         }
     920           0 :                         else if (data->putProto == PROTO_SMB) // lgtm [cpp/empty-block]
     921             :                         {
     922             :                         }
     923             :                         else
     924             :                         {
     925           0 :                                 curl_easy_setopt (curl, CURLOPT_URL, data->uploadUrl);
     926           0 :                                 curl_easy_setopt (curl, CURLOPT_UPLOAD, 1L);
     927           0 :                                 curl_easy_setopt (curl, CURLOPT_READDATA, fp);
     928           0 :                                 curl_easy_setopt (curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
     929           0 :                                 res = curl_easy_perform (curl);
     930           0 :                                 if (res != CURLE_OK)
     931             :                                 {
     932           0 :                                         ELEKTRA_SET_RESOURCE_ERRORF (parentKey, "Curl upload (default) . Reason: %s",
     933             :                                                                      curl_easy_strerror (res));
     934           0 :                                         retval = -1;
     935             :                                 }
     936             :                         }
     937           0 :                         curl_easy_cleanup (curl);
     938           0 :                         fclose (fp);
     939             :                 }
     940           0 :                 if (retval != -1)
     941             :                 {
     942           0 :                         if (data->useLocalCopy)
     943             :                         {
     944           0 :                                 if (moveFile (tmpFile, data->path))
     945             :                                 {
     946           0 :                                         if (data->tmpFile)
     947             :                                         {
     948           0 :                                                 unlink (data->tmpFile);
     949             :                                         }
     950             :                                 }
     951           0 :                                 data->tmpFile = NULL;
     952           0 :                                 keySetString (parentKey, data->path);
     953             :                         }
     954             :                         else
     955             :                         {
     956           0 :                                 if (data->tmpFile)
     957             :                                 {
     958           0 :                                         unlink (data->tmpFile);
     959           0 :                                         data->tmpFile = NULL;
     960             :                                 }
     961           0 :                                 if (data->path)
     962             :                                 {
     963           0 :                                         unlink (data->path);
     964           0 :                                         elektraFree (data->path);
     965           0 :                                         data->path = NULL;
     966             :                                 }
     967           0 :                                 if (tmpFile)
     968             :                                 {
     969           0 :                                         unlink (tmpFile);
     970             :                                 }
     971             :                         }
     972             :                 }
     973             :                 else
     974             :                 {
     975           0 :                         if (!data->useLocalCopy)
     976             :                         {
     977           0 :                                 if (data->tmpFile)
     978             :                                 {
     979           0 :                                         unlink (data->tmpFile);
     980           0 :                                         data->tmpFile = NULL;
     981             :                                 }
     982           0 :                                 if (data->path)
     983             :                                 {
     984           0 :                                         unlink (data->path);
     985           0 :                                         elektraFree (data->path);
     986           0 :                                         data->path = NULL;
     987             :                                 }
     988           0 :                                 if (tmpFile)
     989             :                                 {
     990           0 :                                         unlink (tmpFile);
     991             :                                 }
     992             :                         }
     993             :                         else
     994             :                         {
     995           0 :                                 if (tmpFile) unlink (tmpFile);
     996           0 :                                 if (data->tmpFile)
     997             :                                 {
     998           0 :                                         unlink (data->tmpFile);
     999           0 :                                         data->tmpFile = NULL;
    1000             :                                 }
    1001             :                         }
    1002             :                 }
    1003             :         }
    1004             : 
    1005             :         return retval; // success
    1006             : }
    1007             : 
    1008           0 : int elektraCurlgetCommit (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
    1009             : {
    1010           0 :         return elektraCurlgetSet (handle, returned, parentKey);
    1011             : }
    1012             : 
    1013          29 : Plugin * ELEKTRA_PLUGIN_EXPORT
    1014             : {
    1015             :         // clang-format off
    1016          29 :     return elektraPluginExport ("curlget",
    1017             :             ELEKTRA_PLUGIN_GET, &elektraCurlgetGet,
    1018             :             ELEKTRA_PLUGIN_SET, &elektraCurlgetSet,
    1019             :             ELEKTRA_PLUGIN_OPEN,        &elektraCurlgetOpen,
    1020             :             ELEKTRA_PLUGIN_CLOSE,       &elektraCurlgetClose,
    1021             :             ELEKTRA_PLUGIN_ERROR,       &elektraCurlgetError,
    1022             :             ELEKTRA_PLUGIN_COMMIT,      &elektraCurlgetCommit,
    1023             :             ELEKTRA_PLUGIN_END);
    1024             : }
    1025             : 

Generated by: LCOV version 1.13