root/source3/libsmb/libsmb_path.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hex2int
  2. urldecode_talloc
  3. smbc_urldecode
  4. smbc_urlencode
  5. SMBC_parse_path

   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 )
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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         
 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         if (!workgroup) {
 399                 workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
 400         }
 401         if (!workgroup) {
 402                 return -1;
 403         }
 404 
 405         /* set the credentials to make DFS work */
 406         smbc_set_credentials_with_fallback(context,
 407                                            workgroup,
 408                                            *pp_user,
 409                                            *pp_password);
 410         
 411         return 0;
 412 }
 413 

/* [<][>][^][v][top][bottom][index][help] */