1 /**
2 * \file   curl_utils.c
3 * \brief  curl utilities for the http library.
4 * \author Copyright (c) 2017 Tom van Dijck, João Matos and the Premake project
5 */
6 #ifdef PREMAKE_CURL
7 
8 #include "curl_utils.h"
9 #include "premake.h"
10 #include <string.h>
11 
curlProgressCallback(curl_state * state,double dltotal,double dlnow,double ultotal,double ulnow)12 int curlProgressCallback(curl_state* state, double dltotal, double dlnow, double ultotal, double ulnow)
13 {
14 	lua_State* L = state->L;
15 
16 	(void)ultotal;
17 	(void)ulnow;
18 
19 	if (dltotal == 0) return 0;
20 
21 	/* retrieve the lua progress callback we saved before */
22 	lua_rawgeti(L, LUA_REGISTRYINDEX, state->RefIndex);
23 	lua_pushnumber(L, (lua_Number)dltotal);
24 	lua_pushnumber(L, (lua_Number)dlnow);
25 	int ret = premake_pcall(L, 2, LUA_MULTRET);
26 	if (ret != LUA_OK) {
27 		printLastError(L);
28 		return -1; // abort download
29 	}
30 
31 	return 0;
32 }
33 
34 
curlWriteCallback(char * ptr,size_t size,size_t nmemb,curl_state * state)35 size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb, curl_state* state)
36 {
37 	size_t length = size * nmemb;
38 	buffer_puts(&state->S, ptr, length);
39 	return length;
40 }
41 
42 
curl_init()43 static void curl_init()
44 {
45 	static int initializedHTTP = 0;
46 
47 	if (initializedHTTP)
48 		return;
49 
50 	curl_global_init(CURL_GLOBAL_ALL);
51 	atexit(curl_global_cleanup);
52 	initializedHTTP = 1;
53 }
54 
55 
get_headers(lua_State * L,int headersIndex,struct curl_slist ** headers)56 static void get_headers(lua_State* L, int headersIndex, struct curl_slist** headers)
57 {
58 	lua_pushnil(L);
59 	while (lua_next(L, headersIndex) != 0)
60 	{
61 		const char *item = luaL_checkstring(L, -1);
62 		lua_pop(L, 1);
63 		*headers = curl_slist_append(*headers, item);
64 	}
65 }
66 
67 
curlRequest(lua_State * L,curl_state * state,int optionsIndex,int progressFnIndex,int headersIndex)68 CURL* curlRequest(lua_State* L, curl_state* state, int optionsIndex, int progressFnIndex, int headersIndex)
69 {
70 	char agent[1024];
71 	CURL* curl;
72 
73 	state->L = 0;
74 	state->RefIndex = 0;
75 	state->errorBuffer[0] = '\0';
76 	state->headers = NULL;
77 	buffer_init(&state->S);
78 
79 	curl_init();
80 	curl = curl_easy_init();
81 
82 	if (!curl)
83 		return NULL;
84 
85 	strcpy(agent, "Premake/");
86 	strcat(agent, PREMAKE_VERSION);
87 
88 	curl_easy_setopt(curl, CURLOPT_URL, luaL_checkstring(L, 1));
89 	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
90 	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
91 	curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
92 	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, state->errorBuffer);
93 	curl_easy_setopt(curl, CURLOPT_USERAGENT, agent);
94 
95 	// check if the --insecure option was specified on the commandline.
96 	lua_getglobal(L, "_OPTIONS");
97 	lua_pushstring(L, "insecure");
98 	lua_gettable(L, -2);
99 	if (!lua_isnil(L, -1))
100 	{
101 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
102 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
103 	}
104 	lua_pop(L, 2);
105 
106 	// apply all other options.
107 	if (optionsIndex && lua_istable(L, optionsIndex))
108 	{
109 		lua_pushnil(L);
110 		while (lua_next(L, optionsIndex) != 0)
111 		{
112 			const char* key = luaL_checkstring(L, -2);
113 
114 			if (!strcmp(key, "headers") && lua_istable(L, -1))
115 			{
116 				get_headers(L, lua_gettop(L), &state->headers);
117 				curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers);
118 			}
119 			else if (!strcmp(key, "progress") && lua_isfunction(L, -1))
120 			{
121 				state->L = L;
122 				lua_pushvalue(L, -1);
123 				state->RefIndex = luaL_ref(L, LUA_REGISTRYINDEX);
124 			}
125 			else if (!strcmp(key, "userpwd") && lua_isstring(L, -1))
126 			{
127 				curl_easy_setopt(curl, CURLOPT_USERPWD, luaL_checkstring(L, -1));
128 			}
129 			else if (!strcmp(key, "username") && lua_isstring(L, -1))
130 			{
131 				curl_easy_setopt(curl, CURLOPT_USERNAME, luaL_checkstring(L, -1));
132 			}
133 			else if (!strcmp(key, "password") && lua_isstring(L, -1))
134 			{
135 				curl_easy_setopt(curl, CURLOPT_PASSWORD, luaL_checkstring(L, -1));
136 			}
137 			else if (!strcmp(key, "timeout") && lua_isnumber(L, -1))
138 			{
139 				curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)luaL_checknumber(L, -1));
140 			}
141 			else if (!strcmp(key, "timeoutms") && lua_isnumber(L, -1))
142 			{
143 				curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, (long)luaL_checknumber(L, -1));
144 			}
145 			else if (!strcmp(key, "sslverifyhost") && lua_isnumber(L, -1))
146 			{
147 				curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)luaL_checknumber(L, -1));
148 			}
149 			else if (!strcmp(key, "sslverifypeer") && lua_isnumber(L, -1))
150 			{
151 				curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)luaL_checknumber(L, -1));
152 			}
153 			else if (!strcmp(key, "proxyurl") && lua_isstring(L, -1))
154 			{
155 				curl_easy_setopt(curl, CURLOPT_PROXY, luaL_checkstring(L, -1));
156 			}
157 
158 			// pop the value, leave the key for lua_next
159 			lua_pop(L, 1);
160 		}
161 	}
162 	else
163 	{
164 		if (progressFnIndex && lua_type(L, progressFnIndex) == LUA_TFUNCTION)
165 		{
166 			state->L = L;
167 			lua_pushvalue(L, progressFnIndex);
168 			state->RefIndex = luaL_ref(L, LUA_REGISTRYINDEX);
169 		}
170 
171 		if (headersIndex && lua_istable(L, headersIndex))
172 		{
173 			get_headers(L, headersIndex, &state->headers);
174 			curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers);
175 		}
176 	}
177 
178 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, state);
179 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
180 
181 	if (state->L != 0)
182 	{
183 		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
184 		curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, state);
185 		curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curlProgressCallback);
186 	}
187 
188 	// clear error buffer.
189 	state->errorBuffer[0] = 0;
190 
191 	return curl;
192 }
193 
194 
195 
curlCleanup(CURL * curl,curl_state * state)196 void curlCleanup(CURL* curl, curl_state* state)
197 {
198 	if (state->headers)
199 	{
200 		curl_slist_free_all(state->headers);
201 		state->headers = 0;
202 	}
203 	curl_easy_cleanup(curl);
204 }
205 
206 
207 #endif
208