1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2014, 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 http://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    { NULL, 0 }
276  };
277
278  if(!str)
279    return 1;
280
281  buffer = strdup(str); /* because strtok corrupts it */
282  if(!buffer)
283    return 1;
284
285  for(token = strtok(buffer, sep);
286      token;
287      token = strtok(NULL, sep)) {
288    enum e_action { allow, deny, set } action = allow;
289
290    struct sprotos const *pp;
291
292    /* Process token modifiers */
293    while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */
294      switch (*token++) {
295      case '=':
296        action = set;
297        break;
298      case '-':
299        action = deny;
300        break;
301      case '+':
302        action = allow;
303        break;
304      default: /* Includes case of terminating NULL */
305        Curl_safefree(buffer);
306        return 1;
307      }
308    }
309
310    for(pp=protos; pp->name; pp++) {
311      if(curlx_raw_equal(token, pp->name)) {
312        switch (action) {
313        case deny:
314          *val &= ~(pp->bit);
315          break;
316        case allow:
317          *val |= pp->bit;
318          break;
319        case set:
320          *val = pp->bit;
321          break;
322        }
323        break;
324      }
325    }
326
327    if(!(pp->name)) { /* unknown protocol */
328      /* If they have specified only this protocol, we say treat it as
329         if no protocols are allowed */
330      if(action == set)
331        *val = 0;
332      warnf(config, "unrecognized protocol '%s'\n", token);
333    }
334  }
335  Curl_safefree(buffer);
336  return 0;
337}
338
339/**
340 * Parses the given string looking for an offset (which may be a
341 * larger-than-integer value). The offset CANNOT be negative!
342 *
343 * @param val  the offset to populate
344 * @param str  the buffer containing the offset
345 * @return PARAM_OK if successful, a parameter specific error enum if failure.
346 */
347ParameterError str2offset(curl_off_t *val, const char *str)
348{
349  char *endptr;
350  if(str[0] == '-')
351    /* offsets aren't negative, this indicates weird input */
352    return PARAM_NEGATIVE_NUMERIC;
353
354#if(CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
355  *val = curlx_strtoofft(str, &endptr, 0);
356  if((*val == CURL_OFF_T_MAX || *val == CURL_OFF_T_MIN) && (ERRNO == ERANGE))
357    return PARAM_BAD_NUMERIC;
358#else
359  *val = strtol(str, &endptr, 0);
360  if((*val == LONG_MIN || *val == LONG_MAX) && ERRNO == ERANGE)
361    return PARAM_BAD_NUMERIC;
362#endif
363  if((endptr != str) && (endptr == str + strlen(str)))
364    return PARAM_OK;
365
366  return PARAM_BAD_NUMERIC;
367}
368
369static CURLcode checkpasswd(const char *kind, /* for what purpose */
370                            const size_t i,   /* operation index */
371                            const bool last,  /* TRUE if last operation */
372                            char **userpwd)   /* pointer to allocated string */
373{
374  char *psep;
375  char *osep;
376
377  if(!*userpwd)
378    return CURLE_OK;
379
380  /* Attempt to find the password separator */
381  psep = strchr(*userpwd, ':');
382
383  /* Attempt to find the options separator */
384  osep = strchr(*userpwd, ';');
385
386  if(!psep && **userpwd != ';') {
387    /* no password present, prompt for one */
388    char passwd[256] = "";
389    char prompt[256];
390    size_t passwdlen;
391    size_t userlen = strlen(*userpwd);
392    char *passptr;
393
394    if(osep)
395      *osep = '\0';
396
397    /* build a nice-looking prompt */
398    if(!i && last)
399      curlx_msnprintf(prompt, sizeof(prompt),
400                      "Enter %s password for user '%s':",
401                      kind, *userpwd);
402    else
403      curlx_msnprintf(prompt, sizeof(prompt),
404                      "Enter %s password for user '%s' on URL #%"
405                      CURL_FORMAT_CURL_OFF_TU ":",
406                      kind, *userpwd, i + 1);
407
408    /* get password */
409    getpass_r(prompt, passwd, sizeof(passwd));
410    passwdlen = strlen(passwd);
411
412    if(osep)
413      *osep = ';';
414
415    /* extend the allocated memory area to fit the password too */
416    passptr = realloc(*userpwd,
417                      passwdlen + 1 + /* an extra for the colon */
418                      userlen + 1);   /* an extra for the zero */
419    if(!passptr)
420      return CURLE_OUT_OF_MEMORY;
421
422    /* append the password separated with a colon */
423    passptr[userlen] = ':';
424    memcpy(&passptr[userlen+1], passwd, passwdlen+1);
425    *userpwd = passptr;
426  }
427
428  return CURLE_OK;
429}
430
431ParameterError add2list(struct curl_slist **list, const char *ptr)
432{
433  struct curl_slist *newlist = curl_slist_append(*list, ptr);
434  if(newlist)
435    *list = newlist;
436  else
437    return PARAM_NO_MEM;
438
439  return PARAM_OK;
440}
441
442int ftpfilemethod(struct OperationConfig *config, const char *str)
443{
444  if(curlx_raw_equal("singlecwd", str))
445    return CURLFTPMETHOD_SINGLECWD;
446  if(curlx_raw_equal("nocwd", str))
447    return CURLFTPMETHOD_NOCWD;
448  if(curlx_raw_equal("multicwd", str))
449    return CURLFTPMETHOD_MULTICWD;
450  warnf(config, "unrecognized ftp file method '%s', using default\n", str);
451  return CURLFTPMETHOD_MULTICWD;
452}
453
454int ftpcccmethod(struct OperationConfig *config, const char *str)
455{
456  if(curlx_raw_equal("passive", str))
457    return CURLFTPSSL_CCC_PASSIVE;
458  if(curlx_raw_equal("active", str))
459    return CURLFTPSSL_CCC_ACTIVE;
460  warnf(config, "unrecognized ftp CCC method '%s', using default\n", str);
461  return CURLFTPSSL_CCC_PASSIVE;
462}
463
464long delegation(struct OperationConfig *config, char *str)
465{
466  if(curlx_raw_equal("none", str))
467    return CURLGSSAPI_DELEGATION_NONE;
468  if(curlx_raw_equal("policy", str))
469    return CURLGSSAPI_DELEGATION_POLICY_FLAG;
470  if(curlx_raw_equal("always", str))
471    return CURLGSSAPI_DELEGATION_FLAG;
472  warnf(config, "unrecognized delegation method '%s', using none\n", str);
473  return CURLGSSAPI_DELEGATION_NONE;
474}
475
476/*
477 * my_useragent: returns allocated string with default user agent
478 */
479static char *my_useragent(void)
480{
481  return strdup(CURL_NAME "/" CURL_VERSION);
482}
483
484CURLcode get_args(struct OperationConfig *config, const size_t i)
485{
486  CURLcode result = CURLE_OK;
487  bool last = (config->next ? FALSE : TRUE);
488
489  /* Check we have a password for the given host user */
490  if(config->userpwd && !config->xoauth2_bearer) {
491    result = checkpasswd("host", i, last, &config->userpwd);
492    if(result)
493      return result;
494  }
495
496  /* Check we have a password for the given proxy user */
497  if(config->proxyuserpwd) {
498    result = checkpasswd("proxy", i, last, &config->proxyuserpwd);
499    if(result)
500      return result;
501  }
502
503  /* Check we have a user agent */
504  if(!config->useragent) {
505    config->useragent = my_useragent();
506    if(!config->useragent) {
507      helpf(config->global->errors, "out of memory\n");
508      result = CURLE_OUT_OF_MEMORY;
509    }
510  }
511
512  return result;
513}
514