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