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

Generated by: LCOV version 1.13