1*6236dae4SAndroid Build Coastguard Worker /***************************************************************************
2*6236dae4SAndroid Build Coastguard Worker * _ _ ____ _
3*6236dae4SAndroid Build Coastguard Worker * Project ___| | | | _ \| |
4*6236dae4SAndroid Build Coastguard Worker * / __| | | | |_) | |
5*6236dae4SAndroid Build Coastguard Worker * | (__| |_| | _ <| |___
6*6236dae4SAndroid Build Coastguard Worker * \___|\___/|_| \_\_____|
7*6236dae4SAndroid Build Coastguard Worker *
8*6236dae4SAndroid Build Coastguard Worker * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9*6236dae4SAndroid Build Coastguard Worker *
10*6236dae4SAndroid Build Coastguard Worker * This software is licensed as described in the file COPYING, which
11*6236dae4SAndroid Build Coastguard Worker * you should have received as part of this distribution. The terms
12*6236dae4SAndroid Build Coastguard Worker * are also available at https://curl.se/docs/copyright.html.
13*6236dae4SAndroid Build Coastguard Worker *
14*6236dae4SAndroid Build Coastguard Worker * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15*6236dae4SAndroid Build Coastguard Worker * copies of the Software, and permit persons to whom the Software is
16*6236dae4SAndroid Build Coastguard Worker * furnished to do so, under the terms of the COPYING file.
17*6236dae4SAndroid Build Coastguard Worker *
18*6236dae4SAndroid Build Coastguard Worker * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19*6236dae4SAndroid Build Coastguard Worker * KIND, either express or implied.
20*6236dae4SAndroid Build Coastguard Worker *
21*6236dae4SAndroid Build Coastguard Worker * SPDX-License-Identifier: curl
22*6236dae4SAndroid Build Coastguard Worker *
23*6236dae4SAndroid Build Coastguard Worker ***************************************************************************/
24*6236dae4SAndroid Build Coastguard Worker #include "tool_setup.h"
25*6236dae4SAndroid Build Coastguard Worker
26*6236dae4SAndroid Build Coastguard Worker #ifndef CURL_DISABLE_IPFS
27*6236dae4SAndroid Build Coastguard Worker #include "curlx.h"
28*6236dae4SAndroid Build Coastguard Worker #include "dynbuf.h"
29*6236dae4SAndroid Build Coastguard Worker
30*6236dae4SAndroid Build Coastguard Worker #include "tool_cfgable.h"
31*6236dae4SAndroid Build Coastguard Worker #include "tool_msgs.h"
32*6236dae4SAndroid Build Coastguard Worker #include "tool_ipfs.h"
33*6236dae4SAndroid Build Coastguard Worker
34*6236dae4SAndroid Build Coastguard Worker #include "memdebug.h" /* keep this as LAST include */
35*6236dae4SAndroid Build Coastguard Worker
36*6236dae4SAndroid Build Coastguard Worker /* ensure input ends in slash */
ensure_trailing_slash(char ** input)37*6236dae4SAndroid Build Coastguard Worker static CURLcode ensure_trailing_slash(char **input)
38*6236dae4SAndroid Build Coastguard Worker {
39*6236dae4SAndroid Build Coastguard Worker if(*input && **input) {
40*6236dae4SAndroid Build Coastguard Worker size_t len = strlen(*input);
41*6236dae4SAndroid Build Coastguard Worker if(((*input)[len - 1] != '/')) {
42*6236dae4SAndroid Build Coastguard Worker struct curlx_dynbuf dyn;
43*6236dae4SAndroid Build Coastguard Worker curlx_dyn_init(&dyn, len + 2);
44*6236dae4SAndroid Build Coastguard Worker
45*6236dae4SAndroid Build Coastguard Worker if(curlx_dyn_addn(&dyn, *input, len)) {
46*6236dae4SAndroid Build Coastguard Worker Curl_safefree(*input);
47*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
48*6236dae4SAndroid Build Coastguard Worker }
49*6236dae4SAndroid Build Coastguard Worker
50*6236dae4SAndroid Build Coastguard Worker Curl_safefree(*input);
51*6236dae4SAndroid Build Coastguard Worker
52*6236dae4SAndroid Build Coastguard Worker if(curlx_dyn_addn(&dyn, "/", 1))
53*6236dae4SAndroid Build Coastguard Worker return CURLE_OUT_OF_MEMORY;
54*6236dae4SAndroid Build Coastguard Worker
55*6236dae4SAndroid Build Coastguard Worker *input = curlx_dyn_ptr(&dyn);
56*6236dae4SAndroid Build Coastguard Worker }
57*6236dae4SAndroid Build Coastguard Worker }
58*6236dae4SAndroid Build Coastguard Worker
59*6236dae4SAndroid Build Coastguard Worker return CURLE_OK;
60*6236dae4SAndroid Build Coastguard Worker }
61*6236dae4SAndroid Build Coastguard Worker
ipfs_gateway(void)62*6236dae4SAndroid Build Coastguard Worker static char *ipfs_gateway(void)
63*6236dae4SAndroid Build Coastguard Worker {
64*6236dae4SAndroid Build Coastguard Worker char *ipfs_path = NULL;
65*6236dae4SAndroid Build Coastguard Worker char *gateway_composed_file_path = NULL;
66*6236dae4SAndroid Build Coastguard Worker FILE *gateway_file = NULL;
67*6236dae4SAndroid Build Coastguard Worker char *gateway = curl_getenv("IPFS_GATEWAY");
68*6236dae4SAndroid Build Coastguard Worker
69*6236dae4SAndroid Build Coastguard Worker /* Gateway is found from environment variable. */
70*6236dae4SAndroid Build Coastguard Worker if(gateway) {
71*6236dae4SAndroid Build Coastguard Worker if(ensure_trailing_slash(&gateway))
72*6236dae4SAndroid Build Coastguard Worker goto fail;
73*6236dae4SAndroid Build Coastguard Worker return gateway;
74*6236dae4SAndroid Build Coastguard Worker }
75*6236dae4SAndroid Build Coastguard Worker
76*6236dae4SAndroid Build Coastguard Worker /* Try to find the gateway in the IPFS data folder. */
77*6236dae4SAndroid Build Coastguard Worker ipfs_path = curl_getenv("IPFS_PATH");
78*6236dae4SAndroid Build Coastguard Worker
79*6236dae4SAndroid Build Coastguard Worker if(!ipfs_path) {
80*6236dae4SAndroid Build Coastguard Worker char *home = getenv("HOME");
81*6236dae4SAndroid Build Coastguard Worker if(home && *home)
82*6236dae4SAndroid Build Coastguard Worker ipfs_path = aprintf("%s/.ipfs/", home);
83*6236dae4SAndroid Build Coastguard Worker /* fallback to "~/.ipfs", as that is the default location. */
84*6236dae4SAndroid Build Coastguard Worker }
85*6236dae4SAndroid Build Coastguard Worker
86*6236dae4SAndroid Build Coastguard Worker if(!ipfs_path || ensure_trailing_slash(&ipfs_path))
87*6236dae4SAndroid Build Coastguard Worker goto fail;
88*6236dae4SAndroid Build Coastguard Worker
89*6236dae4SAndroid Build Coastguard Worker gateway_composed_file_path = aprintf("%sgateway", ipfs_path);
90*6236dae4SAndroid Build Coastguard Worker
91*6236dae4SAndroid Build Coastguard Worker if(!gateway_composed_file_path)
92*6236dae4SAndroid Build Coastguard Worker goto fail;
93*6236dae4SAndroid Build Coastguard Worker
94*6236dae4SAndroid Build Coastguard Worker gateway_file = fopen(gateway_composed_file_path, FOPEN_READTEXT);
95*6236dae4SAndroid Build Coastguard Worker Curl_safefree(gateway_composed_file_path);
96*6236dae4SAndroid Build Coastguard Worker
97*6236dae4SAndroid Build Coastguard Worker if(gateway_file) {
98*6236dae4SAndroid Build Coastguard Worker int c;
99*6236dae4SAndroid Build Coastguard Worker struct curlx_dynbuf dyn;
100*6236dae4SAndroid Build Coastguard Worker curlx_dyn_init(&dyn, MAX_GATEWAY_URL_LEN);
101*6236dae4SAndroid Build Coastguard Worker
102*6236dae4SAndroid Build Coastguard Worker /* get the first line of the gateway file, ignore the rest */
103*6236dae4SAndroid Build Coastguard Worker while((c = getc(gateway_file)) != EOF && c != '\n' && c != '\r') {
104*6236dae4SAndroid Build Coastguard Worker char c_char = (char)c;
105*6236dae4SAndroid Build Coastguard Worker if(curlx_dyn_addn(&dyn, &c_char, 1))
106*6236dae4SAndroid Build Coastguard Worker goto fail;
107*6236dae4SAndroid Build Coastguard Worker }
108*6236dae4SAndroid Build Coastguard Worker
109*6236dae4SAndroid Build Coastguard Worker fclose(gateway_file);
110*6236dae4SAndroid Build Coastguard Worker gateway_file = NULL;
111*6236dae4SAndroid Build Coastguard Worker
112*6236dae4SAndroid Build Coastguard Worker if(curlx_dyn_len(&dyn))
113*6236dae4SAndroid Build Coastguard Worker gateway = curlx_dyn_ptr(&dyn);
114*6236dae4SAndroid Build Coastguard Worker
115*6236dae4SAndroid Build Coastguard Worker if(gateway)
116*6236dae4SAndroid Build Coastguard Worker ensure_trailing_slash(&gateway);
117*6236dae4SAndroid Build Coastguard Worker
118*6236dae4SAndroid Build Coastguard Worker if(!gateway)
119*6236dae4SAndroid Build Coastguard Worker goto fail;
120*6236dae4SAndroid Build Coastguard Worker
121*6236dae4SAndroid Build Coastguard Worker Curl_safefree(ipfs_path);
122*6236dae4SAndroid Build Coastguard Worker
123*6236dae4SAndroid Build Coastguard Worker return gateway;
124*6236dae4SAndroid Build Coastguard Worker }
125*6236dae4SAndroid Build Coastguard Worker fail:
126*6236dae4SAndroid Build Coastguard Worker if(gateway_file)
127*6236dae4SAndroid Build Coastguard Worker fclose(gateway_file);
128*6236dae4SAndroid Build Coastguard Worker Curl_safefree(gateway);
129*6236dae4SAndroid Build Coastguard Worker Curl_safefree(ipfs_path);
130*6236dae4SAndroid Build Coastguard Worker return NULL;
131*6236dae4SAndroid Build Coastguard Worker }
132*6236dae4SAndroid Build Coastguard Worker
133*6236dae4SAndroid Build Coastguard Worker /*
134*6236dae4SAndroid Build Coastguard Worker * Rewrite ipfs://<cid> and ipns://<cid> to an HTTP(S)
135*6236dae4SAndroid Build Coastguard Worker * URL that can be handled by an IPFS gateway.
136*6236dae4SAndroid Build Coastguard Worker */
ipfs_url_rewrite(CURLU * uh,const char * protocol,char ** url,struct OperationConfig * config)137*6236dae4SAndroid Build Coastguard Worker CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url,
138*6236dae4SAndroid Build Coastguard Worker struct OperationConfig *config)
139*6236dae4SAndroid Build Coastguard Worker {
140*6236dae4SAndroid Build Coastguard Worker CURLcode result = CURLE_URL_MALFORMAT;
141*6236dae4SAndroid Build Coastguard Worker CURLUcode getResult;
142*6236dae4SAndroid Build Coastguard Worker char *gateway = NULL;
143*6236dae4SAndroid Build Coastguard Worker char *gwhost = NULL;
144*6236dae4SAndroid Build Coastguard Worker char *gwpath = NULL;
145*6236dae4SAndroid Build Coastguard Worker char *gwquery = NULL;
146*6236dae4SAndroid Build Coastguard Worker char *gwscheme = NULL;
147*6236dae4SAndroid Build Coastguard Worker char *gwport = NULL;
148*6236dae4SAndroid Build Coastguard Worker char *inputpath = NULL;
149*6236dae4SAndroid Build Coastguard Worker char *cid = NULL;
150*6236dae4SAndroid Build Coastguard Worker char *pathbuffer = NULL;
151*6236dae4SAndroid Build Coastguard Worker char *cloneurl;
152*6236dae4SAndroid Build Coastguard Worker CURLU *gatewayurl = curl_url();
153*6236dae4SAndroid Build Coastguard Worker
154*6236dae4SAndroid Build Coastguard Worker if(!gatewayurl) {
155*6236dae4SAndroid Build Coastguard Worker result = CURLE_FAILED_INIT;
156*6236dae4SAndroid Build Coastguard Worker goto clean;
157*6236dae4SAndroid Build Coastguard Worker }
158*6236dae4SAndroid Build Coastguard Worker
159*6236dae4SAndroid Build Coastguard Worker getResult = curl_url_get(uh, CURLUPART_HOST, &cid, CURLU_URLDECODE);
160*6236dae4SAndroid Build Coastguard Worker if(getResult || !cid)
161*6236dae4SAndroid Build Coastguard Worker goto clean;
162*6236dae4SAndroid Build Coastguard Worker
163*6236dae4SAndroid Build Coastguard Worker /* We might have a --ipfs-gateway argument. Check it first and use it. Error
164*6236dae4SAndroid Build Coastguard Worker * if we do have something but if it is an invalid url.
165*6236dae4SAndroid Build Coastguard Worker */
166*6236dae4SAndroid Build Coastguard Worker if(config->ipfs_gateway) {
167*6236dae4SAndroid Build Coastguard Worker /* ensure the gateway ends in a trailing / */
168*6236dae4SAndroid Build Coastguard Worker if(ensure_trailing_slash(&config->ipfs_gateway) != CURLE_OK) {
169*6236dae4SAndroid Build Coastguard Worker result = CURLE_OUT_OF_MEMORY;
170*6236dae4SAndroid Build Coastguard Worker goto clean;
171*6236dae4SAndroid Build Coastguard Worker }
172*6236dae4SAndroid Build Coastguard Worker
173*6236dae4SAndroid Build Coastguard Worker if(!curl_url_set(gatewayurl, CURLUPART_URL, config->ipfs_gateway,
174*6236dae4SAndroid Build Coastguard Worker CURLU_GUESS_SCHEME)) {
175*6236dae4SAndroid Build Coastguard Worker gateway = strdup(config->ipfs_gateway);
176*6236dae4SAndroid Build Coastguard Worker if(!gateway) {
177*6236dae4SAndroid Build Coastguard Worker result = CURLE_URL_MALFORMAT;
178*6236dae4SAndroid Build Coastguard Worker goto clean;
179*6236dae4SAndroid Build Coastguard Worker }
180*6236dae4SAndroid Build Coastguard Worker
181*6236dae4SAndroid Build Coastguard Worker }
182*6236dae4SAndroid Build Coastguard Worker else {
183*6236dae4SAndroid Build Coastguard Worker result = CURLE_BAD_FUNCTION_ARGUMENT;
184*6236dae4SAndroid Build Coastguard Worker goto clean;
185*6236dae4SAndroid Build Coastguard Worker }
186*6236dae4SAndroid Build Coastguard Worker }
187*6236dae4SAndroid Build Coastguard Worker else {
188*6236dae4SAndroid Build Coastguard Worker /* this is ensured to end in a trailing / within ipfs_gateway() */
189*6236dae4SAndroid Build Coastguard Worker gateway = ipfs_gateway();
190*6236dae4SAndroid Build Coastguard Worker if(!gateway) {
191*6236dae4SAndroid Build Coastguard Worker result = CURLE_FILE_COULDNT_READ_FILE;
192*6236dae4SAndroid Build Coastguard Worker goto clean;
193*6236dae4SAndroid Build Coastguard Worker }
194*6236dae4SAndroid Build Coastguard Worker
195*6236dae4SAndroid Build Coastguard Worker if(curl_url_set(gatewayurl, CURLUPART_URL, gateway, 0)) {
196*6236dae4SAndroid Build Coastguard Worker result = CURLE_URL_MALFORMAT;
197*6236dae4SAndroid Build Coastguard Worker goto clean;
198*6236dae4SAndroid Build Coastguard Worker }
199*6236dae4SAndroid Build Coastguard Worker }
200*6236dae4SAndroid Build Coastguard Worker
201*6236dae4SAndroid Build Coastguard Worker /* check for unsupported gateway parts */
202*6236dae4SAndroid Build Coastguard Worker if(curl_url_get(gatewayurl, CURLUPART_QUERY, &gwquery, 0)
203*6236dae4SAndroid Build Coastguard Worker != CURLUE_NO_QUERY) {
204*6236dae4SAndroid Build Coastguard Worker result = CURLE_URL_MALFORMAT;
205*6236dae4SAndroid Build Coastguard Worker goto clean;
206*6236dae4SAndroid Build Coastguard Worker }
207*6236dae4SAndroid Build Coastguard Worker
208*6236dae4SAndroid Build Coastguard Worker /* get gateway parts */
209*6236dae4SAndroid Build Coastguard Worker if(curl_url_get(gatewayurl, CURLUPART_HOST,
210*6236dae4SAndroid Build Coastguard Worker &gwhost, CURLU_URLDECODE)) {
211*6236dae4SAndroid Build Coastguard Worker goto clean;
212*6236dae4SAndroid Build Coastguard Worker }
213*6236dae4SAndroid Build Coastguard Worker
214*6236dae4SAndroid Build Coastguard Worker if(curl_url_get(gatewayurl, CURLUPART_SCHEME,
215*6236dae4SAndroid Build Coastguard Worker &gwscheme, CURLU_URLDECODE)) {
216*6236dae4SAndroid Build Coastguard Worker goto clean;
217*6236dae4SAndroid Build Coastguard Worker }
218*6236dae4SAndroid Build Coastguard Worker
219*6236dae4SAndroid Build Coastguard Worker curl_url_get(gatewayurl, CURLUPART_PORT, &gwport, CURLU_URLDECODE);
220*6236dae4SAndroid Build Coastguard Worker curl_url_get(gatewayurl, CURLUPART_PATH, &gwpath, CURLU_URLDECODE);
221*6236dae4SAndroid Build Coastguard Worker
222*6236dae4SAndroid Build Coastguard Worker /* get the path from user input */
223*6236dae4SAndroid Build Coastguard Worker curl_url_get(uh, CURLUPART_PATH, &inputpath, CURLU_URLDECODE);
224*6236dae4SAndroid Build Coastguard Worker /* inputpath might be NULL or a valid pointer now */
225*6236dae4SAndroid Build Coastguard Worker
226*6236dae4SAndroid Build Coastguard Worker /* set gateway parts in input url */
227*6236dae4SAndroid Build Coastguard Worker if(curl_url_set(uh, CURLUPART_SCHEME, gwscheme, CURLU_URLENCODE) ||
228*6236dae4SAndroid Build Coastguard Worker curl_url_set(uh, CURLUPART_HOST, gwhost, CURLU_URLENCODE) ||
229*6236dae4SAndroid Build Coastguard Worker curl_url_set(uh, CURLUPART_PORT, gwport, CURLU_URLENCODE))
230*6236dae4SAndroid Build Coastguard Worker goto clean;
231*6236dae4SAndroid Build Coastguard Worker
232*6236dae4SAndroid Build Coastguard Worker /* if the input path is just a slash, clear it */
233*6236dae4SAndroid Build Coastguard Worker if(inputpath && (inputpath[0] == '/') && !inputpath[1])
234*6236dae4SAndroid Build Coastguard Worker *inputpath = '\0';
235*6236dae4SAndroid Build Coastguard Worker
236*6236dae4SAndroid Build Coastguard Worker /* ensure the gateway path ends with a trailing slash */
237*6236dae4SAndroid Build Coastguard Worker ensure_trailing_slash(&gwpath);
238*6236dae4SAndroid Build Coastguard Worker
239*6236dae4SAndroid Build Coastguard Worker pathbuffer = aprintf("%s%s/%s%s", gwpath, protocol, cid,
240*6236dae4SAndroid Build Coastguard Worker inputpath ? inputpath : "");
241*6236dae4SAndroid Build Coastguard Worker if(!pathbuffer) {
242*6236dae4SAndroid Build Coastguard Worker goto clean;
243*6236dae4SAndroid Build Coastguard Worker }
244*6236dae4SAndroid Build Coastguard Worker
245*6236dae4SAndroid Build Coastguard Worker if(curl_url_set(uh, CURLUPART_PATH, pathbuffer, CURLU_URLENCODE)) {
246*6236dae4SAndroid Build Coastguard Worker goto clean;
247*6236dae4SAndroid Build Coastguard Worker }
248*6236dae4SAndroid Build Coastguard Worker
249*6236dae4SAndroid Build Coastguard Worker /* Free whatever it has now, rewriting is next */
250*6236dae4SAndroid Build Coastguard Worker Curl_safefree(*url);
251*6236dae4SAndroid Build Coastguard Worker
252*6236dae4SAndroid Build Coastguard Worker if(curl_url_get(uh, CURLUPART_URL, &cloneurl, CURLU_URLENCODE)) {
253*6236dae4SAndroid Build Coastguard Worker goto clean;
254*6236dae4SAndroid Build Coastguard Worker }
255*6236dae4SAndroid Build Coastguard Worker /* we need to strdup the URL so that we can call free() on it later */
256*6236dae4SAndroid Build Coastguard Worker *url = strdup(cloneurl);
257*6236dae4SAndroid Build Coastguard Worker curl_free(cloneurl);
258*6236dae4SAndroid Build Coastguard Worker if(!*url)
259*6236dae4SAndroid Build Coastguard Worker goto clean;
260*6236dae4SAndroid Build Coastguard Worker
261*6236dae4SAndroid Build Coastguard Worker result = CURLE_OK;
262*6236dae4SAndroid Build Coastguard Worker
263*6236dae4SAndroid Build Coastguard Worker clean:
264*6236dae4SAndroid Build Coastguard Worker free(gateway);
265*6236dae4SAndroid Build Coastguard Worker curl_free(gwhost);
266*6236dae4SAndroid Build Coastguard Worker curl_free(gwpath);
267*6236dae4SAndroid Build Coastguard Worker curl_free(gwquery);
268*6236dae4SAndroid Build Coastguard Worker curl_free(inputpath);
269*6236dae4SAndroid Build Coastguard Worker curl_free(gwscheme);
270*6236dae4SAndroid Build Coastguard Worker curl_free(gwport);
271*6236dae4SAndroid Build Coastguard Worker curl_free(cid);
272*6236dae4SAndroid Build Coastguard Worker curl_free(pathbuffer);
273*6236dae4SAndroid Build Coastguard Worker curl_url_cleanup(gatewayurl);
274*6236dae4SAndroid Build Coastguard Worker {
275*6236dae4SAndroid Build Coastguard Worker switch(result) {
276*6236dae4SAndroid Build Coastguard Worker case CURLE_URL_MALFORMAT:
277*6236dae4SAndroid Build Coastguard Worker helpf(tool_stderr, "malformed target URL");
278*6236dae4SAndroid Build Coastguard Worker break;
279*6236dae4SAndroid Build Coastguard Worker case CURLE_FILE_COULDNT_READ_FILE:
280*6236dae4SAndroid Build Coastguard Worker helpf(tool_stderr, "IPFS automatic gateway detection failed");
281*6236dae4SAndroid Build Coastguard Worker break;
282*6236dae4SAndroid Build Coastguard Worker case CURLE_BAD_FUNCTION_ARGUMENT:
283*6236dae4SAndroid Build Coastguard Worker helpf(tool_stderr, "--ipfs-gateway was given a malformed URL");
284*6236dae4SAndroid Build Coastguard Worker break;
285*6236dae4SAndroid Build Coastguard Worker default:
286*6236dae4SAndroid Build Coastguard Worker break;
287*6236dae4SAndroid Build Coastguard Worker }
288*6236dae4SAndroid Build Coastguard Worker }
289*6236dae4SAndroid Build Coastguard Worker return result;
290*6236dae4SAndroid Build Coastguard Worker }
291*6236dae4SAndroid Build Coastguard Worker #endif /* !CURL_DISABLE_IPFS */
292