257e5c697bf2bbcfeca6e3a771d1e2cb7f7fbce9
[platform/upstream/curl.git] / src / tool_paramhlp.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 #include "tool_setup.h"
23
24 #include "strcase.h"
25
26 #define ENABLE_CURLX_PRINTF
27 /* use our own printf() functions */
28 #include "curlx.h"
29
30 #include "tool_cfgable.h"
31 #include "tool_getparam.h"
32 #include "tool_getpass.h"
33 #include "tool_homedir.h"
34 #include "tool_msgs.h"
35 #include "tool_paramhlp.h"
36 #include "tool_version.h"
37
38 #include "memdebug.h" /* keep this as LAST include */
39
40 struct getout *new_getout(struct OperationConfig *config)
41 {
42   struct getout *node = calloc(1, sizeof(struct getout));
43   struct getout *last = config->url_last;
44   if(node) {
45     /* append this new node last in the list */
46     if(last)
47       last->next = node;
48     else
49       config->url_list = node; /* first node */
50
51     /* move the last pointer */
52     config->url_last = node;
53
54     node->flags = config->default_node_flags;
55   }
56   return node;
57 }
58
59 ParameterError file2string(char **bufp, FILE *file)
60 {
61   char buffer[256];
62   char *ptr;
63   char *string = NULL;
64   size_t stringlen = 0;
65   size_t buflen;
66
67   if(file) {
68     while(fgets(buffer, sizeof(buffer), file)) {
69       ptr = strchr(buffer, '\r');
70       if(ptr)
71         *ptr = '\0';
72       ptr = strchr(buffer, '\n');
73       if(ptr)
74         *ptr = '\0';
75       buflen = strlen(buffer);
76       ptr = realloc(string, stringlen+buflen+1);
77       if(!ptr) {
78         Curl_safefree(string);
79         return PARAM_NO_MEM;
80       }
81       string = ptr;
82       strcpy(string+stringlen, buffer);
83       stringlen += buflen;
84     }
85   }
86   *bufp = string;
87   return PARAM_OK;
88 }
89
90 ParameterError file2memory(char **bufp, size_t *size, FILE *file)
91 {
92   char *newbuf;
93   char *buffer = NULL;
94   size_t alloc = 512;
95   size_t nused = 0;
96   size_t nread;
97
98   if(file) {
99     do {
100       if(!buffer || (alloc == nused)) {
101         /* size_t overflow detection for huge files */
102         if(alloc+1 > ((size_t)-1)/2) {
103           Curl_safefree(buffer);
104           return PARAM_NO_MEM;
105         }
106         alloc *= 2;
107         /* allocate an extra char, reserved space, for null termination */
108         newbuf = realloc(buffer, alloc+1);
109         if(!newbuf) {
110           Curl_safefree(buffer);
111           return PARAM_NO_MEM;
112         }
113         buffer = newbuf;
114       }
115       nread = fread(buffer+nused, 1, alloc-nused, file);
116       nused += nread;
117     } while(nread);
118     /* null terminate the buffer in case it's used as a string later */
119     buffer[nused] = '\0';
120     /* free trailing slack space, if possible */
121     if(alloc != nused) {
122       newbuf = realloc(buffer, nused+1);
123       if(!newbuf) {
124         Curl_safefree(buffer);
125         return PARAM_NO_MEM;
126       }
127       buffer = newbuf;
128     }
129     /* discard buffer if nothing was read */
130     if(!nused) {
131       Curl_safefree(buffer); /* no string */
132     }
133   }
134   *size = nused;
135   *bufp = buffer;
136   return PARAM_OK;
137 }
138
139 void cleanarg(char *str)
140 {
141 #ifdef HAVE_WRITABLE_ARGV
142   /* now that GetStr has copied the contents of nextarg, wipe the next
143    * argument out so that the username:password isn't displayed in the
144    * system process list */
145   if(str) {
146     size_t len = strlen(str);
147     memset(str, ' ', len);
148   }
149 #else
150   (void)str;
151 #endif
152 }
153
154 /*
155  * Parse the string and write the long in the given address. Return PARAM_OK
156  * on success, otherwise a parameter specific error enum.
157  *
158  * Since this function gets called with the 'nextarg' pointer from within the
159  * getparameter a lot, we must check it for NULL before accessing the str
160  * data.
161  */
162
163 ParameterError str2num(long *val, const char *str)
164 {
165   if(str) {
166     char *endptr;
167     long num = strtol(str, &endptr, 10);
168     if((endptr != str) && (endptr == str + strlen(str))) {
169       *val = num;
170       return PARAM_OK;  /* Ok */
171     }
172   }
173   return PARAM_BAD_NUMERIC; /* badness */
174 }
175
176 /*
177  * Parse the string and write the long in the given address. Return PARAM_OK
178  * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
179  *
180  * Since this function gets called with the 'nextarg' pointer from within the
181  * getparameter a lot, we must check it for NULL before accessing the str
182  * data.
183  */
184
185 ParameterError str2unum(long *val, const char *str)
186 {
187   ParameterError result = str2num(val, str);
188   if(result != PARAM_OK)
189     return result;
190   if(*val < 0)
191     return PARAM_NEGATIVE_NUMERIC;
192
193   return PARAM_OK;
194 }
195
196 /*
197  * Parse the string and write the double in the given address. Return PARAM_OK
198  * on success, otherwise a parameter specific error enum.
199  *
200  * Since this function gets called with the 'nextarg' pointer from within the
201  * getparameter a lot, we must check it for NULL before accessing the str
202  * data.
203  */
204
205 ParameterError str2double(double *val, const char *str)
206 {
207   if(str) {
208     char *endptr;
209     double num = strtod(str, &endptr);
210     if((endptr != str) && (endptr == str + strlen(str))) {
211       *val = num;
212       return PARAM_OK;  /* Ok */
213     }
214   }
215   return PARAM_BAD_NUMERIC; /* badness */
216 }
217
218 /*
219  * Parse the string and write the double in the given address. Return PARAM_OK
220  * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS!
221  *
222  * Since this function gets called with the 'nextarg' pointer from within the
223  * getparameter a lot, we must check it for NULL before accessing the str
224  * data.
225  */
226
227 ParameterError str2udouble(double *val, const char *str)
228 {
229   ParameterError result = str2double(val, str);
230   if(result != PARAM_OK)
231     return result;
232   if(*val < 0)
233     return PARAM_NEGATIVE_NUMERIC;
234
235   return PARAM_OK;
236 }
237
238 /*
239  * Parse the string and modify the long in the given address. Return
240  * non-zero on failure, zero on success.
241  *
242  * The string is a list of protocols
243  *
244  * Since this function gets called with the 'nextarg' pointer from within the
245  * getparameter a lot, we must check it for NULL before accessing the str
246  * data.
247  */
248
249 long proto2num(struct OperationConfig *config, long *val, const char *str)
250 {
251   char *buffer;
252   const char *sep = ",";
253   char *token;
254
255   static struct sprotos {
256     const char *name;
257     long bit;
258   } const protos[] = {
259     { "all", CURLPROTO_ALL },
260     { "http", CURLPROTO_HTTP },
261     { "https", CURLPROTO_HTTPS },
262     { "ftp", CURLPROTO_FTP },
263     { "ftps", CURLPROTO_FTPS },
264     { "scp", CURLPROTO_SCP },
265     { "sftp", CURLPROTO_SFTP },
266     { "telnet", CURLPROTO_TELNET },
267     { "ldap", CURLPROTO_LDAP },
268     { "ldaps", CURLPROTO_LDAPS },
269     { "dict", CURLPROTO_DICT },
270     { "file", CURLPROTO_FILE },
271     { "tftp", CURLPROTO_TFTP },
272     { "imap", CURLPROTO_IMAP },
273     { "imaps", CURLPROTO_IMAPS },
274     { "pop3", CURLPROTO_POP3 },
275     { "pop3s", CURLPROTO_POP3S },
276     { "smtp", CURLPROTO_SMTP },
277     { "smtps", CURLPROTO_SMTPS },
278     { "rtsp", CURLPROTO_RTSP },
279     { "gopher", CURLPROTO_GOPHER },
280     { "smb", CURLPROTO_SMB },
281     { "smbs", CURLPROTO_SMBS },
282     { NULL, 0 }
283   };
284
285   if(!str)
286     return 1;
287
288   buffer = strdup(str); /* because strtok corrupts it */
289   if(!buffer)
290     return 1;
291
292   /* Allow strtok() here since this isn't used threaded */
293   /* !checksrc! disable BANNEDFUNC 2 */
294   for(token = strtok(buffer, sep);
295       token;
296       token = strtok(NULL, sep)) {
297     enum e_action { allow, deny, set } action = allow;
298
299     struct sprotos const *pp;
300
301     /* Process token modifiers */
302     while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */
303       switch (*token++) {
304       case '=':
305         action = set;
306         break;
307       case '-':
308         action = deny;
309         break;
310       case '+':
311         action = allow;
312         break;
313       default: /* Includes case of terminating NULL */
314         Curl_safefree(buffer);
315         return 1;
316       }
317     }
318
319     for(pp=protos; pp->name; pp++) {
320       if(curl_strequal(token, pp->name)) {
321         switch(action) {
322         case deny:
323           *val &= ~(pp->bit);
324           break;
325         case allow:
326           *val |= pp->bit;
327           break;
328         case set:
329           *val = pp->bit;
330           break;
331         }
332         break;
333       }
334     }
335
336     if(!(pp->name)) { /* unknown protocol */
337       /* If they have specified only this protocol, we say treat it as
338          if no protocols are allowed */
339       if(action == set)
340         *val = 0;
341       warnf(config->global, "unrecognized protocol '%s'\n", token);
342     }
343   }
344   Curl_safefree(buffer);
345   return 0;
346 }
347
348 /**
349  * Check if the given string is a protocol supported by libcurl
350  *
351  * @param str  the protocol name
352  * @return PARAM_OK  protocol supported
353  * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL  protocol not supported
354  * @return PARAM_REQUIRES_PARAMETER   missing parameter
355  */
356 int check_protocol(const char *str)
357 {
358   const char * const *pp;
359   const curl_version_info_data *curlinfo = curl_version_info(CURLVERSION_NOW);
360   if(!str)
361     return PARAM_REQUIRES_PARAMETER;
362   for(pp = curlinfo->protocols; *pp; pp++) {
363     if(curl_strequal(*pp, str))
364       return PARAM_OK;
365   }
366   return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
367 }
368
369 /**
370  * Parses the given string looking for an offset (which may be a
371  * larger-than-integer value). The offset CANNOT be negative!
372  *
373  * @param val  the offset to populate
374  * @param str  the buffer containing the offset
375  * @return PARAM_OK if successful, a parameter specific error enum if failure.
376  */
377 ParameterError str2offset(curl_off_t *val, const char *str)
378 {
379   char *endptr;
380   if(str[0] == '-')
381     /* offsets aren't negative, this indicates weird input */
382     return PARAM_NEGATIVE_NUMERIC;
383
384 #if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
385   *val = curlx_strtoofft(str, &endptr, 0);
386   if((*val == CURL_OFF_T_MAX || *val == CURL_OFF_T_MIN) && (ERRNO == ERANGE))
387     return PARAM_BAD_NUMERIC;
388 #else
389   *val = strtol(str, &endptr, 0);
390   if((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
391     return PARAM_BAD_NUMERIC;
392 #endif
393   if((endptr != str) && (endptr == str + strlen(str)))
394     return PARAM_OK;
395
396   return PARAM_BAD_NUMERIC;
397 }
398
399 static CURLcode checkpasswd(const char *kind, /* for what purpose */
400                             const size_t i,   /* operation index */
401                             const bool last,  /* TRUE if last operation */
402                             char **userpwd)   /* pointer to allocated string */
403 {
404   char *psep;
405   char *osep;
406
407   if(!*userpwd)
408     return CURLE_OK;
409
410   /* Attempt to find the password separator */
411   psep = strchr(*userpwd, ':');
412
413   /* Attempt to find the options separator */
414   osep = strchr(*userpwd, ';');
415
416   if(!psep && **userpwd != ';') {
417     /* no password present, prompt for one */
418     char passwd[256] = "";
419     char prompt[256];
420     size_t passwdlen;
421     size_t userlen = strlen(*userpwd);
422     char *passptr;
423
424     if(osep)
425       *osep = '\0';
426
427     /* build a nice-looking prompt */
428     if(!i && last)
429       curlx_msnprintf(prompt, sizeof(prompt),
430                       "Enter %s password for user '%s':",
431                       kind, *userpwd);
432     else
433       curlx_msnprintf(prompt, sizeof(prompt),
434                       "Enter %s password for user '%s' on URL #%"
435                       CURL_FORMAT_CURL_OFF_TU ":",
436                       kind, *userpwd, (curl_off_t) (i + 1));
437
438     /* get password */
439     getpass_r(prompt, passwd, sizeof(passwd));
440     passwdlen = strlen(passwd);
441
442     if(osep)
443       *osep = ';';
444
445     /* extend the allocated memory area to fit the password too */
446     passptr = realloc(*userpwd,
447                       passwdlen + 1 + /* an extra for the colon */
448                       userlen + 1);   /* an extra for the zero */
449     if(!passptr)
450       return CURLE_OUT_OF_MEMORY;
451
452     /* append the password separated with a colon */
453     passptr[userlen] = ':';
454     memcpy(&passptr[userlen+1], passwd, passwdlen+1);
455     *userpwd = passptr;
456   }
457
458   return CURLE_OK;
459 }
460
461 ParameterError add2list(struct curl_slist **list, const char *ptr)
462 {
463   struct curl_slist *newlist = curl_slist_append(*list, ptr);
464   if(newlist)
465     *list = newlist;
466   else
467     return PARAM_NO_MEM;
468
469   return PARAM_OK;
470 }
471
472 int ftpfilemethod(struct OperationConfig *config, const char *str)
473 {
474   if(curl_strequal("singlecwd", str))
475     return CURLFTPMETHOD_SINGLECWD;
476   if(curl_strequal("nocwd", str))
477     return CURLFTPMETHOD_NOCWD;
478   if(curl_strequal("multicwd", str))
479     return CURLFTPMETHOD_MULTICWD;
480
481   warnf(config->global, "unrecognized ftp file method '%s', using default\n",
482         str);
483
484   return CURLFTPMETHOD_MULTICWD;
485 }
486
487 int ftpcccmethod(struct OperationConfig *config, const char *str)
488 {
489   if(curl_strequal("passive", str))
490     return CURLFTPSSL_CCC_PASSIVE;
491   if(curl_strequal("active", str))
492     return CURLFTPSSL_CCC_ACTIVE;
493
494   warnf(config->global, "unrecognized ftp CCC method '%s', using default\n",
495         str);
496
497   return CURLFTPSSL_CCC_PASSIVE;
498 }
499
500 long delegation(struct OperationConfig *config, char *str)
501 {
502   if(curl_strequal("none", str))
503     return CURLGSSAPI_DELEGATION_NONE;
504   if(curl_strequal("policy", str))
505     return CURLGSSAPI_DELEGATION_POLICY_FLAG;
506   if(curl_strequal("always", str))
507     return CURLGSSAPI_DELEGATION_FLAG;
508
509   warnf(config->global, "unrecognized delegation method '%s', using none\n",
510         str);
511
512   return CURLGSSAPI_DELEGATION_NONE;
513 }
514
515 /*
516  * my_useragent: returns allocated string with default user agent
517  */
518 static char *my_useragent(void)
519 {
520   return strdup(CURL_NAME "/" CURL_VERSION);
521 }
522
523 CURLcode get_args(struct OperationConfig *config, const size_t i)
524 {
525   CURLcode result = CURLE_OK;
526   bool last = (config->next ? FALSE : TRUE);
527
528   /* Check we have a password for the given host user */
529   if(config->userpwd && !config->oauth_bearer) {
530     result = checkpasswd("host", i, last, &config->userpwd);
531     if(result)
532       return result;
533   }
534
535   /* Check we have a password for the given proxy user */
536   if(config->proxyuserpwd) {
537     result = checkpasswd("proxy", i, last, &config->proxyuserpwd);
538     if(result)
539       return result;
540   }
541
542   /* Check we have a user agent */
543   if(!config->useragent) {
544     config->useragent = my_useragent();
545     if(!config->useragent) {
546       helpf(config->global->errors, "out of memory\n");
547       result = CURLE_OUT_OF_MEMORY;
548     }
549   }
550
551   return result;
552 }