64a956d9ee4ee25e2dbb8a6dd984dbf61a0a024a
[sfrench/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         *dest++ = '\0';
177         max_dest_len--;
178
179         return max_dest_len;
180 }
181
182 /*
183  * Function to parse a path and turn it into components
184  *
185  * The general format of an SMB URI is explain in Christopher Hertel's CIFS
186  * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
187  * general format ("smb:" only; we do not look for "cifs:").
188  *
189  *
190  * We accept:
191  *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
192  *
193  * Meaning of URLs:
194  *
195  * smb://           Show all workgroups.
196  *
197  *                  The method of locating the list of workgroups varies
198  *                  depending upon the setting of the context variable
199  *                  context->options.browse_max_lmb_count.  This value
200  *                  determines the maximum number of local master browsers to
201  *                  query for the list of workgroups.  In order to ensure that
202  *                  a complete list of workgroups is obtained, all master
203  *                  browsers must be queried, but if there are many
204  *                  workgroups, the time spent querying can begin to add up.
205  *                  For small networks (not many workgroups), it is suggested
206  *                  that this variable be set to 0, indicating query all local
207  *                  master browsers.  When the network has many workgroups, a
208  *                  reasonable setting for this variable might be around 3.
209  *
210  * smb://name/      if name<1D> or name<1B> exists, list servers in
211  *                  workgroup, else, if name<20> exists, list all shares
212  *                  for server ...
213  *
214  * If "options" are provided, this function returns the entire option list as a
215  * string, for later parsing by the caller.  Note that currently, no options
216  * are supported.
217  */
218
219 #define SMBC_PREFIX "smb:"
220
221 int
222 SMBC_parse_path(TALLOC_CTX *ctx,
223                 SMBCCTX *context,
224                 const char *fname,
225                 char **pp_workgroup,
226                 char **pp_server,
227                 char **pp_share,
228                 char **pp_path,
229                 char **pp_user,
230                 char **pp_password,
231                 char **pp_options)
232 {
233         char *s;
234         const char *p;
235         char *q, *r;
236         char *workgroup = NULL;
237         int len;
238
239         /* Ensure these returns are at least valid pointers. */
240         *pp_server = talloc_strdup(ctx, "");
241         *pp_share = talloc_strdup(ctx, "");
242         *pp_path = talloc_strdup(ctx, "");
243         *pp_user = talloc_strdup(ctx, "");
244         *pp_password = talloc_strdup(ctx, "");
245
246         if (!*pp_server || !*pp_share || !*pp_path ||
247             !*pp_user || !*pp_password) {
248                 return -1;
249         }
250
251         /*
252          * Assume we wont find an authentication domain to parse, so default
253          * to the workgroup in the provided context.
254          */
255         if (pp_workgroup != NULL) {
256                 *pp_workgroup =
257                         talloc_strdup(ctx, smbc_getWorkgroup(context));
258         }
259
260         if (pp_options) {
261                 *pp_options = talloc_strdup(ctx, "");
262         }
263         s = talloc_strdup(ctx, fname);
264
265         /* see if it has the right prefix */
266         len = strlen(SMBC_PREFIX);
267         if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
268                 return -1; /* What about no smb: ? */
269         }
270
271         p = s + len;
272
273         /* Watch the test below, we are testing to see if we should exit */
274
275         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
276                 DEBUG(1, ("Invalid path (does not begin with smb://"));
277                 return -1;
278         }
279
280         p += 2;  /* Skip the double slash */
281
282         /* See if any options were specified */
283         if ((q = strrchr(p, '?')) != NULL ) {
284                 /* There are options.  Null terminate here and point to them */
285                 *q++ = '\0';
286
287                 DEBUG(4, ("Found options '%s'", q));
288
289                 /* Copy the options */
290                 if (pp_options && *pp_options != NULL) {
291                         TALLOC_FREE(*pp_options);
292                         *pp_options = talloc_strdup(ctx, q);
293                 }
294         }
295
296         if (*p == '\0') {
297                 goto decoding;
298         }
299
300         if (*p == '/') {
301                 int wl = strlen(smbc_getWorkgroup(context));
302
303                 if (wl > 16) {
304                         wl = 16;
305                 }
306
307                 *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
308                 if (!*pp_server) {
309                         return -1;
310                 }
311                 (*pp_server)[wl] = '\0';
312                 return 0;
313         }
314
315         /*
316          * ok, its for us. Now parse out the server, share etc.
317          *
318          * However, we want to parse out [[domain;]user[:password]@] if it
319          * exists ...
320          */
321
322         /* check that '@' occurs before '/', if '/' exists at all */
323         q = strchr_m(p, '@');
324         r = strchr_m(p, '/');
325         if (q && (!r || q < r)) {
326                 char *userinfo = NULL;
327                 const char *u;
328
329                 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
330                 if (!userinfo) {
331                         return -1;
332                 }
333                 u = userinfo;
334
335                 if (strchr_m(u, ';')) {
336                         next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
337                         if (!workgroup) {
338                                 return -1;
339                         }
340                         if (pp_workgroup) {
341                                 *pp_workgroup = workgroup;
342                         }
343                 }
344
345                 if (strchr_m(u, ':')) {
346                         next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
347                         if (!*pp_user) {
348                                 return -1;
349                         }
350                         *pp_password = talloc_strdup(ctx, u);
351                         if (!*pp_password) {
352                                 return -1;
353                         }
354                 } else {
355                         *pp_user = talloc_strdup(ctx, u);
356                         if (!*pp_user) {
357                                 return -1;
358                         }
359                 }
360         }
361
362         if (!next_token_talloc(ctx, &p, pp_server, "/")) {
363                 return -1;
364         }
365
366         if (*p == (char)0) {
367                 goto decoding;  /* That's it ... */
368         }
369
370         if (!next_token_talloc(ctx, &p, pp_share, "/")) {
371                 return -1;
372         }
373
374         /*
375          * Prepend a leading slash if there's a file path, as required by
376          * NetApp filers.
377          */
378         if (*p != '\0') {
379                 *pp_path = talloc_asprintf(ctx,
380                                            "\\%s",
381                                            p);
382         } else {
383                 *pp_path = talloc_strdup(ctx, "");
384         }
385         if (!*pp_path) {
386                 return -1;
387         }
388         string_replace(*pp_path, '/', '\\');
389
390 decoding:
391         (void) urldecode_talloc(ctx, pp_path, *pp_path);
392         (void) urldecode_talloc(ctx, pp_server, *pp_server);
393         (void) urldecode_talloc(ctx, pp_share, *pp_share);
394         (void) urldecode_talloc(ctx, pp_user, *pp_user);
395         (void) urldecode_talloc(ctx, pp_password, *pp_password);
396
397         if (!workgroup) {
398                 workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
399         }
400         if (!workgroup) {
401                 return -1;
402         }
403
404         /* set the credentials to make DFS work */
405         smbc_set_credentials_with_fallback(context,
406                                            workgroup,
407                                            *pp_user,
408                                            *pp_password);
409         return 0;
410 }
411