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() */
31static int
32hex2int( 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 */
55static int
56urldecode_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
124int
125smbc_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 */
148int
149smbc_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
221int
222SMBC_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//- JerryLin Modify
283#if 1
284    /* See if any options were specified */
285    if ((q = strrchr(p, '?')) != NULL ) {
286    	/* There are options.  Null terminate here and point to them */
287        *q++ = '\0';
288
289        DEBUG(4, ("Found options '%s'", q));
290
291		/* Copy the options */
292		if (pp_options && *pp_options != NULL) {
293			TALLOC_FREE(*pp_options);
294			*pp_options = talloc_strdup(ctx, q);
295		}
296	}
297#else
298	/* See if any options were specified */
299       if ((q = strrchr(p, '?')) != NULL ) {
300       	/* There are options.  Null terminate here and point to them */
301              *q++ = '\0';
302
303              DEBUG(4, ("Found options '%s'", q));
304
305		//- 20111221 Jerry add
306		char* option = talloc_strdup(ctx, q);
307		char * pch;
308		pch = strtok(option, "&");
309		while(pch!=NULL){
310			if(strncmp(pch, "len=", 4)==0){
311				char* r = strchr_m(pch, '/');
312				int index = r- pch;
313				//fprintf(stderr, "parser index = %d\n", index);
314				int len = strlen(pch)-4;
315
316				char *temp = talloc_strndup(ctx, pch+4, len);
317				userinfo_len = atoi(temp);
318				//fprintf(stderr, "parser len = %d\n", userinfo_len);
319			}
320
321			pch = strtok( NULL, "&" );
322		}
323
324		/* Copy the options */
325		if (pp_options && *pp_options != NULL) {
326			TALLOC_FREE(*pp_options);
327			*pp_options = talloc_strdup(ctx, q);
328		}
329	}
330#endif
331
332	if (*p == '\0') {
333		goto decoding;
334	}
335
336	if (*p == '/') {
337		int wl = strlen(smbc_getWorkgroup(context));
338
339		if (wl > 16) {
340			wl = 16;
341		}
342
343		*pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
344		if (!*pp_server) {
345			return -1;
346		}
347		(*pp_server)[wl] = '\0';
348		return 0;
349	}
350
351	/*
352	 * ok, its for us. Now parse out the server, share etc.
353	 *
354	 * However, we want to parse out [[domain;]user[:password]@] if it
355	 * exists ...
356	 */
357	 /* check that '@' occurs before '/', if '/' exists at all */
358#if 1
359
360	//- 20111221 Jerry add
361	int userinfo_len = -1;
362	int index = 0;
363
364	q = strchr_m(p, '@');
365	r = strchr_m(p, '/');
366
367	while (q&& (!r || q < r))
368	{
369		index = q-p+1;
370		//fprintf (stderr, "found at %d\n", index);
371	    q = strchr_m(q+1, '@');
372	}
373
374	if(index>0)
375		userinfo_len = index - 1;
376
377	 if(userinfo_len!=-1){
378	 	char *userinfo = NULL;
379		const char *u;
380
381		userinfo = talloc_strndup(ctx, p, userinfo_len);
382
383		if (!userinfo) {
384			return -1;
385		}
386
387		//fprintf(stderr, "Libsmb_path.c->SMBC_parse_path: userinfo=%s\n", userinfo);
388
389		u = userinfo;
390
391		if (strchr_m(u, ';')) {
392			next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
393			if (!workgroup) {
394				return -1;
395			}
396			if (pp_workgroup) {
397				*pp_workgroup = workgroup;
398			}
399		}
400
401		if (strchr_m(u, ':')) {
402			next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
403			if (!*pp_user) {
404				return -1;
405			}
406			*pp_password = talloc_strdup(ctx, u);
407			if (!*pp_password) {
408				return -1;
409			}
410		} else {
411			*pp_user = talloc_strdup(ctx, u);
412			if (!*pp_user) {
413				return -1;
414			}
415		}
416
417		p = p + userinfo_len + 1;
418
419		//fprintf(stderr, "pp_user=%s, pp_password=%s\n", *pp_user, *pp_password);
420	}
421
422#else
423
424	/* check that '@' occurs before '/', if '/' exists at all */
425	q = strchr_m(p, '@');
426	r = strchr_m(p, '/');
427
428	if (q && (!r || q < r)) {
429		char *userinfo = NULL;
430		const char *u;
431
432		next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
433		if (!userinfo) {
434			return -1;
435		}
436		u = userinfo;
437
438		if (strchr_m(u, ';')) {
439			next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
440			if (!workgroup) {
441				return -1;
442			}
443			if (pp_workgroup) {
444				*pp_workgroup = workgroup;
445			}
446		}
447
448		if (strchr_m(u, ':')) {
449			next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
450			if (!*pp_user) {
451				return -1;
452			}
453			*pp_password = talloc_strdup(ctx, u);
454			if (!*pp_password) {
455				return -1;
456			}
457		} else {
458			*pp_user = talloc_strdup(ctx, u);
459			if (!*pp_user) {
460				return -1;
461			}
462		}
463
464		fprintf(stderr, "pp_user=%s, pp_password=%s\n", *pp_user, *pp_password);
465
466	}
467#endif
468
469	if (!next_token_talloc(ctx, &p, pp_server, "/")) {
470		return -1;
471	}
472
473	if (*p == (char)0) {
474		goto decoding;  /* That's it ... */
475	}
476
477	if (!next_token_talloc(ctx, &p, pp_share, "/")) {
478		return -1;
479	}
480
481        /*
482         * Prepend a leading slash if there's a file path, as required by
483         * NetApp filers.
484         */
485        if (*p != '\0') {
486		*pp_path = talloc_asprintf(ctx,
487                                           "\\%s",
488                                           p);
489        } else {
490		*pp_path = talloc_strdup(ctx, "");
491	}
492	if (!*pp_path) {
493		return -1;
494	}
495	string_replace(*pp_path, '/', '\\');
496
497decoding:
498
499	(void) urldecode_talloc(ctx, pp_path, *pp_path);
500	(void) urldecode_talloc(ctx, pp_server, *pp_server);
501	(void) urldecode_talloc(ctx, pp_share, *pp_share);
502	(void) urldecode_talloc(ctx, pp_user, *pp_user);
503	(void) urldecode_talloc(ctx, pp_password, *pp_password);
504
505	if (!workgroup) {
506		workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
507	}
508	if (!workgroup) {
509		return -1;
510	}
511
512	/* set the credentials to make DFS work */
513	smbc_set_credentials_with_fallback(context,
514				    	   workgroup,
515				    	   *pp_user,
516				    	   *pp_password);
517
518	return 0;
519}
520
521