Initial revamp of the libsmbclient interface.
[samba.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->browse_max_lmb_count.  This value determines
200  *                  the maximum number of local master browsers to query
201  *                  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 = talloc_strdup(ctx, context->workgroup);
256         }
257
258         if (pp_options) {
259                 *pp_options = talloc_strdup(ctx, "");
260         }
261         s = talloc_strdup(ctx, fname);
262
263         /* see if it has the right prefix */
264         len = strlen(smbc_prefix);
265         if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
266                 return -1; /* What about no smb: ? */
267         }
268
269         p = s + len;
270
271         /* Watch the test below, we are testing to see if we should exit */
272
273         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
274                 DEBUG(1, ("Invalid path (does not begin with smb://"));
275                 return -1;
276         }
277
278         p += 2;  /* Skip the double slash */
279
280         /* See if any options were specified */
281         if ((q = strrchr(p, '?')) != NULL ) {
282                 /* There are options.  Null terminate here and point to them */
283                 *q++ = '\0';
284
285                 DEBUG(4, ("Found options '%s'", q));
286
287                 /* Copy the options */
288                 if (*pp_options != NULL) {
289                         TALLOC_FREE(*pp_options);
290                         *pp_options = talloc_strdup(ctx, q);
291                 }
292         }
293
294         if (*p == '\0') {
295                 goto decoding;
296         }
297
298         if (*p == '/') {
299                 int wl = strlen(context->workgroup);
300
301                 if (wl > 16) {
302                         wl = 16;
303                 }
304
305                 *pp_server = talloc_strdup(ctx, context->workgroup);
306                 if (!*pp_server) {
307                         return -1;
308                 }
309                 *pp_server[wl] = '\0';
310                 return 0;
311         }
312
313         /*
314          * ok, its for us. Now parse out the server, share etc.
315          *
316          * However, we want to parse out [[domain;]user[:password]@] if it
317          * exists ...
318          */
319
320         /* check that '@' occurs before '/', if '/' exists at all */
321         q = strchr_m(p, '@');
322         r = strchr_m(p, '/');
323         if (q && (!r || q < r)) {
324                 char *userinfo = NULL;
325                 const char *u;
326
327                 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
328                 if (!userinfo) {
329                         return -1;
330                 }
331                 u = userinfo;
332
333                 if (strchr_m(u, ';')) {
334                         char *workgroup;
335                         next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
336                         if (!workgroup) {
337                                 return -1;
338                         }
339                         if (pp_workgroup) {
340                                 *pp_workgroup = workgroup;
341                         }
342                 }
343
344                 if (strchr_m(u, ':')) {
345                         next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
346                         if (!*pp_user) {
347                                 return -1;
348                         }
349                         *pp_password = talloc_strdup(ctx, u);
350                         if (!*pp_password) {
351                                 return -1;
352                         }
353                 } else {
354                         *pp_user = talloc_strdup(ctx, u);
355                         if (!*pp_user) {
356                                 return -1;
357                         }
358                 }
359         }
360
361         if (!next_token_talloc(ctx, &p, pp_server, "/")) {
362                 return -1;
363         }
364
365         if (*p == (char)0) {
366                 goto decoding;  /* That's it ... */
367         }
368
369         if (!next_token_talloc(ctx, &p, pp_share, "/")) {
370                 return -1;
371         }
372
373         /*
374          * Prepend a leading slash if there's a file path, as required by
375          * NetApp filers.
376          */
377         if (*p != '\0') {
378                 *pp_path = talloc_asprintf(ctx,
379                                         "\\%s",
380                                         p);
381         } else {
382                 *pp_path = talloc_strdup(ctx, "");
383         }
384         if (!*pp_path) {
385                 return -1;
386         }
387         string_replace(*pp_path, '/', '\\');
388
389  decoding:
390
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         return 0;
398 }
399