xref: /third_party/curl/lib/curl_path.c (revision 13498266)
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.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 * SPDX-License-Identifier: curl AND ISC
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if defined(USE_SSH)
28
29#include <curl/curl.h>
30#include "curl_memory.h"
31#include "curl_path.h"
32#include "escape.h"
33#include "memdebug.h"
34
35#define MAX_SSHPATH_LEN 100000 /* arbitrary */
36
37/* figure out the path to work with in this particular request */
38CURLcode Curl_getworkingpath(struct Curl_easy *data,
39                             char *homedir,  /* when SFTP is used */
40                             char **path) /* returns the  allocated
41                                             real path to work with */
42{
43  char *working_path;
44  size_t working_path_len;
45  struct dynbuf npath;
46  CURLcode result =
47    Curl_urldecode(data->state.up.path, 0, &working_path,
48                   &working_path_len, REJECT_ZERO);
49  if(result)
50    return result;
51
52  /* new path to switch to in case we need to */
53  Curl_dyn_init(&npath, MAX_SSHPATH_LEN);
54
55  /* Check for /~/, indicating relative to the user's home directory */
56  if((data->conn->handler->protocol & CURLPROTO_SCP) &&
57     (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) {
58    /* It is referenced to the home directory, so strip the leading '/~/' */
59    if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
60      free(working_path);
61      return CURLE_OUT_OF_MEMORY;
62    }
63  }
64  else if((data->conn->handler->protocol & CURLPROTO_SFTP) &&
65          (!strcmp("/~", working_path) ||
66           ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) {
67    if(Curl_dyn_add(&npath, homedir)) {
68      free(working_path);
69      return CURLE_OUT_OF_MEMORY;
70    }
71    if(working_path_len > 2) {
72      size_t len;
73      const char *p;
74      int copyfrom = 3;
75      /* Copy a separating '/' if homedir does not end with one */
76      len = Curl_dyn_len(&npath);
77      p = Curl_dyn_ptr(&npath);
78      if(len && (p[len-1] != '/'))
79        copyfrom = 2;
80
81      if(Curl_dyn_addn(&npath,
82                       &working_path[copyfrom], working_path_len - copyfrom)) {
83        free(working_path);
84        return CURLE_OUT_OF_MEMORY;
85      }
86    }
87  }
88
89  if(Curl_dyn_len(&npath)) {
90    free(working_path);
91
92    /* store the pointer for the caller to receive */
93    *path = Curl_dyn_ptr(&npath);
94  }
95  else
96    *path = working_path;
97
98  return CURLE_OK;
99}
100
101/* The get_pathname() function is being borrowed from OpenSSH sftp.c
102   version 4.6p1. */
103/*
104 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
105 *
106 * Permission to use, copy, modify, and distribute this software for any
107 * purpose with or without fee is hereby granted, provided that the above
108 * copyright notice and this permission notice appear in all copies.
109 *
110 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
113 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
114 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
115 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
116 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
117 */
118CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
119{
120  const char *cp = *cpp, *end;
121  char quot;
122  unsigned int i, j;
123  size_t fullPathLength, pathLength;
124  bool relativePath = false;
125  static const char WHITESPACE[] = " \t\r\n";
126
127  DEBUGASSERT(homedir);
128  if(!*cp || !homedir) {
129    *cpp = NULL;
130    *path = NULL;
131    return CURLE_QUOTE_ERROR;
132  }
133  /* Ignore leading whitespace */
134  cp += strspn(cp, WHITESPACE);
135  /* Allocate enough space for home directory and filename + separator */
136  fullPathLength = strlen(cp) + strlen(homedir) + 2;
137  *path = malloc(fullPathLength);
138  if(!*path)
139    return CURLE_OUT_OF_MEMORY;
140
141  /* Check for quoted filenames */
142  if(*cp == '\"' || *cp == '\'') {
143    quot = *cp++;
144
145    /* Search for terminating quote, unescape some chars */
146    for(i = j = 0; i <= strlen(cp); i++) {
147      if(cp[i] == quot) {  /* Found quote */
148        i++;
149        (*path)[j] = '\0';
150        break;
151      }
152      if(cp[i] == '\0') {  /* End of string */
153        goto fail;
154      }
155      if(cp[i] == '\\') {  /* Escaped characters */
156        i++;
157        if(cp[i] != '\'' && cp[i] != '\"' &&
158            cp[i] != '\\') {
159          goto fail;
160        }
161      }
162      (*path)[j++] = cp[i];
163    }
164
165    if(j == 0) {
166      goto fail;
167    }
168    *cpp = cp + i + strspn(cp + i, WHITESPACE);
169  }
170  else {
171    /* Read to end of filename - either to whitespace or terminator */
172    end = strpbrk(cp, WHITESPACE);
173    if(!end)
174      end = strchr(cp, '\0');
175    /* return pointer to second parameter if it exists */
176    *cpp = end + strspn(end, WHITESPACE);
177    pathLength = 0;
178    relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/');
179    /* Handling for relative path - prepend home directory */
180    if(relativePath) {
181      strcpy(*path, homedir);
182      pathLength = strlen(homedir);
183      (*path)[pathLength++] = '/';
184      (*path)[pathLength] = '\0';
185      cp += 3;
186    }
187    /* Copy path name up until first "whitespace" */
188    memcpy(&(*path)[pathLength], cp, (int)(end - cp));
189    pathLength += (int)(end - cp);
190    (*path)[pathLength] = '\0';
191  }
192  return CURLE_OK;
193
194fail:
195  Curl_safefree(*path);
196  return CURLE_QUOTE_ERROR;
197}
198
199#endif /* if SSH is used */
200