lib: remove unused function nttime_from_string()
[bbaumbach/samba-autobuild/.git] / source3 / libsmb / libsmb_path.c
1 /*
2    Unix SMB/Netbios implementation.
3    SMB client library implementation
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Richard Sharpe 2000, 2002
6    Copyright (C) John Terpstra 2000
7    Copyright (C) Tom Jansen (Ninja ISD) 2002
8    Copyright (C) Derrell Lipman 2003-2008
9    Copyright (C) Jeremy Allison 2007, 2008
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "libsmbclient.h"
27 #include "libsmb_internal.h"
28
29
30 /* Used by urldecode_talloc() */
31 static int
32 hex2int( unsigned int _char )
33 {
34         if ( _char >= 'A' && _char <='F')
35                 return _char - 'A' + 10;
36         if ( _char >= 'a' && _char <='f')
37                 return _char - 'a' + 10;
38         if ( _char >= '0' && _char <='9')
39                 return _char - '0';
40         return -1;
41 }
42
43 /*
44  * smbc_urldecode()
45  * and urldecode_talloc() (internal fn.)
46  *
47  * Convert strings of %xx to their single character equivalent.  Each 'x' must
48  * be a valid hexadecimal digit, or that % sequence is left undecoded.
49  *
50  * dest may, but need not be, the same pointer as src.
51  *
52  * Returns the number of % sequences which could not be converted due to lack
53  * of two following hexadecimal digits.
54  */
55 static int
56 urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
57 {
58         int old_length = strlen(src);
59         int i = 0;
60         int err_count = 0;
61         size_t newlen = 1;
62         char *p, *dest;
63
64         if (old_length == 0) {
65                 return 0;
66         }
67
68         *pp_dest = NULL;
69         for (i = 0; i < old_length; ) {
70                 unsigned char character = src[i++];
71
72                 if (character == '%') {
73                         int a = i+1 < old_length ? hex2int(src[i]) : -1;
74                         int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
75
76                         /* Replace valid sequence */
77                         if (a != -1 && b != -1) {
78                                 /* Replace valid %xx sequence with %dd */
79                                 character = (a * 16) + b;
80                                 if (character == '\0') {
81                                         break; /* Stop at %00 */
82                                 }
83                                 i += 2;
84                         } else {
85                                 err_count++;
86                         }
87                 }
88                 newlen++;
89         }
90
91         dest = talloc_array(ctx, char, newlen);
92         if (!dest) {
93                 return err_count;
94         }
95
96         err_count = 0;
97         for (p = dest, i = 0; i < old_length; ) {
98                 unsigned char character = src[i++];
99
100                 if (character == '%') {
101                         int a = i+1 < old_length ? hex2int(src[i]) : -1;
102                         int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
103
104                         /* Replace valid sequence */
105                         if (a != -1 && b != -1) {
106                                 /* Replace valid %xx sequence with %dd */
107                                 character = (a * 16) + b;
108                                 if (character == '\0') {
109                                         break; /* Stop at %00 */
110                                 }
111                                 i += 2;
112                         } else {
113                                 err_count++;
114                         }
115                 }
116                 *p++ = character;
117         }
118
119         *p = '\0';
120         *pp_dest = dest;
121         return err_count;
122 }
123
124 int
125 smbc_urldecode(char *dest,
126                char *src,
127                size_t max_dest_len)
128 {
129         TALLOC_CTX *frame = talloc_stackframe();
130         char *pdest;
131         int ret = urldecode_talloc(frame, &pdest, src);
132
133         if (pdest) {
134                 strlcpy(dest, pdest, max_dest_len);
135         }
136         TALLOC_FREE(frame);
137         return ret;
138 }
139
140 /*
141  * smbc_urlencode()
142  *
143  * Convert any characters not specifically allowed in a URL into their %xx
144  * equivalent.
145  *
146  * Returns the remaining buffer length.
147  */
148 int
149 smbc_urlencode(char *dest,
150                char *src,
151                int max_dest_len)
152 {
153         char hex[] = "0123456789ABCDEF";
154
155         for (; *src != '\0' && max_dest_len >= 3; src++) {
156
157                 if ((*src < '0' &&
158                      *src != '-' &&
159                      *src != '.') ||
160                     (*src > '9' &&
161                      *src < 'A') ||
162                     (*src > 'Z' &&
163                      *src < 'a' &&
164                      *src != '_') ||
165                     (*src > 'z')) {
166                         *dest++ = '%';
167                         *dest++ = hex[(*src >> 4) & 0x0f];
168                         *dest++ = hex[*src & 0x0f];
169                         max_dest_len -= 3;
170                 } else {
171                         *dest++ = *src;
172                         max_dest_len--;
173                 }
174         }
175
176         if (max_dest_len <= 0) {
177                 /* Ensure we return -1 if no null termination. */
178                 return -1;
179         }
180
181         *dest++ = '\0';
182         max_dest_len--;
183
184         return max_dest_len;
185 }
186
187 /*
188  * Function to parse a path and turn it into components
189  *
190  * The general format of an SMB URI is explain in Christopher Hertel's CIFS
191  * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
192  * general format ("smb:" only; we do not look for "cifs:").
193  *
194  *
195  * We accept:
196  *  smb://[[[domain;]user[:password]@]server[:port][/share[/path[/file]]]]
197  *                                                              [?options]
198  *
199  * Meaning of URLs:
200  *
201  * smb://           Show all workgroups.
202  *
203  *                  The method of locating the list of workgroups varies
204  *                  depending upon the setting of the context variable
205  *                  context->options.browse_max_lmb_count.  This value
206  *                  determines the maximum number of local master browsers to
207  *                  query for the list of workgroups.  In order to ensure that
208  *                  a complete list of workgroups is obtained, all master
209  *                  browsers must be queried, but if there are many
210  *                  workgroups, the time spent querying can begin to add up.
211  *                  For small networks (not many workgroups), it is suggested
212  *                  that this variable be set to 0, indicating query all local
213  *                  master browsers.  When the network has many workgroups, a
214  *                  reasonable setting for this variable might be around 3.
215  *
216  * smb://name/      if name<1D> or name<1B> exists, list servers in
217  *                  workgroup, else, if name<20> exists, list all shares
218  *                  for server ...
219  *
220  * If "options" are provided, this function returns the entire option list as a
221  * string, for later parsing by the caller.  Note that currently, no options
222  * are supported.
223  */
224
225 #define SMBC_PREFIX "smb:"
226
227 int
228 SMBC_parse_path(TALLOC_CTX *ctx,
229                 SMBCCTX *context,
230                 const char *fname,
231                 char **pp_workgroup,
232                 char **pp_server,
233                 uint16_t *p_port,
234                 char **pp_share,
235                 char **pp_path,
236                 char **pp_user,
237                 char **pp_password,
238                 char **pp_options)
239 {
240         char *s;
241         const char *p;
242         char *q, *r;
243         char *workgroup = NULL;
244         int len;
245
246         /* Ensure these returns are at least valid pointers. */
247         *pp_server = talloc_strdup(ctx, "");
248         *p_port = smbc_getPort(context);
249         *pp_share = talloc_strdup(ctx, "");
250         *pp_path = talloc_strdup(ctx, "");
251         *pp_user = talloc_strdup(ctx, "");
252         *pp_password = talloc_strdup(ctx, "");
253
254         if (!*pp_server || !*pp_share || !*pp_path ||
255             !*pp_user || !*pp_password) {
256                 return -1;
257         }
258
259         /*
260          * Assume we wont find an authentication domain to parse, so default
261          * to the workgroup in the provided context.
262          */
263         if (pp_workgroup != NULL) {
264                 *pp_workgroup =
265                         talloc_strdup(ctx, smbc_getWorkgroup(context));
266         }
267
268         if (pp_options) {
269                 *pp_options = talloc_strdup(ctx, "");
270         }
271         s = talloc_strdup(ctx, fname);
272
273         /* see if it has the right prefix */
274         len = strlen(SMBC_PREFIX);
275         if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
276                 return -1; /* What about no smb: ? */
277         }
278
279         p = s + len;
280
281         /* Watch the test below, we are testing to see if we should exit */
282
283         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
284                 DEBUG(1, ("Invalid path (does not begin with smb://"));
285                 return -1;
286         }
287
288         p += 2;  /* Skip the double slash */
289
290         /* See if any options were specified */
291         if ((q = strrchr(p, '?')) != NULL ) {
292                 /* There are options.  Null terminate here and point to them */
293                 *q++ = '\0';
294
295                 DEBUG(4, ("Found options '%s'", q));
296
297                 /* Copy the options */
298                 if (pp_options && *pp_options != NULL) {
299                         TALLOC_FREE(*pp_options);
300                         *pp_options = talloc_strdup(ctx, q);
301                 }
302         }
303
304         if (*p == '\0') {
305                 goto decoding;
306         }
307
308         if (*p == '/') {
309                 int wl = strlen(smbc_getWorkgroup(context));
310
311                 if (wl > 16) {
312                         wl = 16;
313                 }
314
315                 *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
316                 if (!*pp_server) {
317                         return -1;
318                 }
319                 (*pp_server)[wl] = '\0';
320                 return 0;
321         }
322
323         /*
324          * ok, its for us. Now parse out the server, share etc.
325          *
326          * However, we want to parse out [[domain;]user[:password]@] if it
327          * exists ...
328          */
329
330         /* check that '@' occurs before '/', if '/' exists at all */
331         q = strchr_m(p, '@');
332         r = strchr_m(p, '/');
333         if (q && (!r || q < r)) {
334                 char *userinfo = NULL;
335                 const char *u;
336
337                 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
338                 if (!userinfo) {
339                         return -1;
340                 }
341                 u = userinfo;
342
343                 if (strchr_m(u, ';')) {
344                         next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
345                         if (!workgroup) {
346                                 return -1;
347                         }
348                         if (pp_workgroup) {
349                                 *pp_workgroup = workgroup;
350                         }
351                 }
352
353                 if (strchr_m(u, ':')) {
354                         next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
355                         if (!*pp_user) {
356                                 return -1;
357                         }
358                         *pp_password = talloc_strdup(ctx, u);
359                         if (!*pp_password) {
360                                 return -1;
361                         }
362                 } else {
363                         *pp_user = talloc_strdup(ctx, u);
364                         if (!*pp_user) {
365                                 return -1;
366                         }
367                 }
368         }
369
370         if (!next_token_talloc(ctx, &p, pp_server, "/")) {
371                 return -1;
372         }
373
374         /*
375          * Does *pp_server contain a ':' ? If so
376          * this denotes the port.
377          */
378         q = strchr_m(*pp_server, ':');
379         if (q != NULL) {
380                 long int port;
381                 char *endptr = NULL;
382                 *q = '\0';
383                 q++;
384                 if (*q == '\0') {
385                         /* Bad port. */
386                         return -1;
387                 }
388                 port = strtol(q, &endptr, 10);
389                 if (*endptr != '\0') {
390                         /* Bad port. */
391                         return -1;
392                 }
393                 *p_port = (uint16_t)port;
394         }
395
396         if (*p == (char)0) {
397                 goto decoding;  /* That's it ... */
398         }
399
400         if (!next_token_talloc(ctx, &p, pp_share, "/")) {
401                 return -1;
402         }
403
404         /*
405          * Prepend a leading slash if there's a file path, as required by
406          * NetApp filers.
407          */
408         if (*p != '\0') {
409                 *pp_path = talloc_asprintf(ctx,
410                                            "\\%s",
411                                            p);
412         } else {
413                 *pp_path = talloc_strdup(ctx, "");
414         }
415         if (!*pp_path) {
416                 return -1;
417         }
418         string_replace(*pp_path, '/', '\\');
419
420 decoding:
421         (void) urldecode_talloc(ctx, pp_path, *pp_path);
422         (void) urldecode_talloc(ctx, pp_server, *pp_server);
423         (void) urldecode_talloc(ctx, pp_share, *pp_share);
424         (void) urldecode_talloc(ctx, pp_user, *pp_user);
425         (void) urldecode_talloc(ctx, pp_password, *pp_password);
426
427         if (!workgroup) {
428                 workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
429         }
430         if (!workgroup) {
431                 return -1;
432         }
433
434         /* set the credentials to make DFS work */
435         smbc_set_credentials_with_fallback(context,
436                                            workgroup,
437                                            *pp_user,
438                                            *pp_password);
439         return 0;
440 }
441