Continued revamping of libsmbclient.
[gd/samba/.git] / source / 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 static const char *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         int len;
237         
238         /* Ensure these returns are at least valid pointers. */
239         *pp_server = talloc_strdup(ctx, "");
240         *pp_share = talloc_strdup(ctx, "");
241         *pp_path = talloc_strdup(ctx, "");
242         *pp_user = talloc_strdup(ctx, "");
243         *pp_password = talloc_strdup(ctx, "");
244         
245         if (!*pp_server || !*pp_share || !*pp_path ||
246             !*pp_user || !*pp_password) {
247                 return -1;
248         }
249         
250         /*
251          * Assume we wont find an authentication domain to parse, so default
252          * to the workgroup in the provided context.
253          */
254         if (pp_workgroup != NULL) {
255                 *pp_workgroup =
256                         talloc_strdup(ctx, smbc_getWorkgroup(context));
257         }
258         
259         if (pp_options) {
260                 *pp_options = talloc_strdup(ctx, "");
261         }
262         s = talloc_strdup(ctx, fname);
263         
264         /* see if it has the right prefix */
265         len = strlen(smbc_prefix);
266         if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
267                 return -1; /* What about no smb: ? */
268         }
269         
270         p = s + len;
271         
272         /* Watch the test below, we are testing to see if we should exit */
273         
274         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
275                 DEBUG(1, ("Invalid path (does not begin with smb://"));
276                 return -1;
277         }
278         
279         p += 2;  /* Skip the double slash */
280         
281         /* See if any options were specified */
282         if ((q = strrchr(p, '?')) != NULL ) {
283                 /* There are options.  Null terminate here and point to them */
284                 *q++ = '\0';
285                 
286                 DEBUG(4, ("Found options '%s'", q));
287                 
288                 /* Copy the options */
289                 if (*pp_options != NULL) {
290                         TALLOC_FREE(*pp_options);
291                         *pp_options = talloc_strdup(ctx, q);
292                 }
293         }
294         
295         if (*p == '\0') {
296                 goto decoding;
297         }
298         
299         if (*p == '/') {
300                 int wl = strlen(smbc_getWorkgroup(context));
301                 
302                 if (wl > 16) {
303                         wl = 16;
304                 }
305                 
306                 *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
307                 if (!*pp_server) {
308                         return -1;
309                 }
310                 *pp_server[wl] = '\0';
311                 return 0;
312         }
313         
314         /*
315          * ok, its for us. Now parse out the server, share etc.
316          *
317          * However, we want to parse out [[domain;]user[:password]@] if it
318          * exists ...
319          */
320         
321         /* check that '@' occurs before '/', if '/' exists at all */
322         q = strchr_m(p, '@');
323         r = strchr_m(p, '/');
324         if (q && (!r || q < r)) {
325                 char *userinfo = NULL;
326                 const char *u;
327                 
328                 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
329                 if (!userinfo) {
330                         return -1;
331                 }
332                 u = userinfo;
333                 
334                 if (strchr_m(u, ';')) {
335                         char *workgroup;
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         
392         (void) urldecode_talloc(ctx, pp_path, *pp_path);
393         (void) urldecode_talloc(ctx, pp_server, *pp_server);
394         (void) urldecode_talloc(ctx, pp_share, *pp_share);
395         (void) urldecode_talloc(ctx, pp_user, *pp_user);
396         (void) urldecode_talloc(ctx, pp_password, *pp_password);
397         
398         return 0;
399 }
400