1/*
2 * Copyright (c) 2006 - 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <CoreFoundation/CoreFoundation.h>
25#include <CoreServices/CoreServicesPriv.h>
26#include <asl.h>
27#include <netsmb/smb_lib.h>
28#include <charsets.h>
29#include <parse_url.h>
30#include <netsmb/smb_conn.h>
31#include <NetFS/NetFS.h>
32#include <NetFS/NetFSUtilPrivate.h>
33#include <netsmb/netbios.h>
34#include <netsmb/nb_lib.h>
35
36#define CIFS_SCHEME_LEN 5
37#define SMB_SCHEME_LEN 4
38
39static void LogCFString(CFStringRef theString, const char *debugstr, const char * func, int lineNum)
40{
41	char prntstr[1024];
42
43	if (theString == NULL)
44		return;
45	CFStringGetCString(theString, prntstr, 1024, kCFStringEncodingUTF8);
46	smb_log_info("%s-line:%d %s = %s", ASL_LEVEL_DEBUG, func, lineNum, debugstr, prntstr);
47}
48
49#ifdef SMB_DEBUG
50#define DebugLogCFString LogCFString
51#else // SMB_DEBUG
52#define DebugLogCFString(theString, debugstr, func, lineNum)
53#endif // SMB_DEBUG
54
55/*
56 * Test to see if we have the same url string
57 */
58int isUrlStringEqual(CFURLRef url1, CFURLRef url2)
59{
60	if ((url1 == NULL) || (url2 == NULL)) {
61		return FALSE;	/* Never match null URLs */
62	}
63
64	if (CFStringCompare(CFURLGetString(url1), CFURLGetString(url1),
65						kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
66		return TRUE;
67	}
68	return FALSE;
69}
70
71/*
72 * Allocate a buffer and then use CFStringGetCString to copy the c-style string
73 * into the buffer. The calling routine needs to free the buffer when done.
74 * This routine needs a new name.
75 */
76char *CStringCreateWithCFString(CFStringRef inStr)
77{
78	CFIndex maxLen;
79	char *str;
80
81    if(inStr == NULL)
82        return NULL;
83	maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(inStr),
84											   kCFStringEncodingUTF8) + 1;
85	str = malloc(maxLen);
86	if (!str) {
87		smb_log_info("%s malloc failed, syserr = %s", ASL_LEVEL_ERR,
88					 __FUNCTION__,strerror(ENOMEM));
89		return NULL;
90	}
91	CFStringGetCString(inStr, str, maxLen, kCFStringEncodingUTF8);
92	return str;
93}
94
95/*
96 * See if this is a cifs or smb scheme
97 *
98 * RETURN VALUES:
99 *	0	- No Scheme, could still be our scheme
100 *	4	- SMB scheme, also the length of smb scheme field.
101 *	5	- CIFS scheme, also the length of cifs scheme field.
102 *	-1	- Unknown scheme, should be treated as an error.
103 */
104static int SMBSchemeLength(CFURLRef url)
105{
106	int len = 0;
107	CFStringRef scheme = CFURLCopyScheme (url);
108
109	if (scheme == NULL)
110		return 0;
111
112	if ( kCFCompareEqualTo == CFStringCompare (scheme, CFSTR("smb"), kCFCompareCaseInsensitive) )
113		len = SMB_SCHEME_LEN;	/* Length of "smb:" */
114	else if ( kCFCompareEqualTo == CFStringCompare (scheme, CFSTR("cifs"), kCFCompareCaseInsensitive) )
115		len = CIFS_SCHEME_LEN;	/* Length of "cifs:" */
116	else
117		len = -1;
118	CFRelease(scheme);
119	return len;
120}
121
122/*
123 * This routine will percent escape out the input string by making a copy. The CreateStringByReplacingPercentEscapesUTF8
124 * routine will either return a copy or take a retain on the original string.
125 *
126 * NOTE:  We always use kCFStringEncodingUTF8 and kCFAllocatorDefault.
127 */
128static void CreateStringByReplacingPercentEscapesUTF8(CFStringRef *inOutStr, CFStringRef LeaveEscaped)
129{
130	CFStringRef inStr = (inOutStr) ? *inOutStr : NULL;
131
132	if (!inStr)	/* Just a safety check */
133		return;
134
135	*inOutStr = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, inStr, LeaveEscaped, kCFStringEncodingUTF8);
136	CFRelease(inStr);	/* We always want to release the inStr */
137}
138
139/*
140 * This routine will  percent escape the input string by making a copy. The CreateStringByAddingPercentEscapesUTF8
141 * routine will either return a copy or take a retain on the original string. If doRelease is set then we do
142 * a release on the inStr.
143 *
144 * NOTE:  We always use kCFStringEncodingUTF8 and kCFAllocatorDefault.
145 */
146static void CreateStringByAddingPercentEscapesUTF8(CFStringRef *inOutStr, CFStringRef leaveUnescaped, CFStringRef toBeEscaped, Boolean doRelease)
147{
148	CFStringRef inStr = (inOutStr) ? *inOutStr : NULL;
149
150	if (!inStr)	/* Just a safety check */
151		return;
152
153	*inOutStr = CFURLCreateStringByAddingPercentEscapes(NULL, inStr, leaveUnescaped, toBeEscaped, kCFStringEncodingUTF8);
154	if (doRelease)
155		CFRelease(inStr);
156}
157
158static CFArrayRef CreateWrkgrpUserArrayFromCFStringRef(CFStringRef inString, CFStringRef separatorString)
159{
160	CFArrayRef	userArray = NULL;
161
162	userArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, inString, separatorString);
163	/*
164	 * If there are two array entries then we have a workgoup and username otherwise if we just have one item then its a
165	 * username. Any other number could be an error, but since we have no idea what they are trying to do we just treat
166	 * it as a username.
167	 */
168	if (userArray && (CFArrayGetCount(userArray) != 2)) {
169		CFRelease(userArray);
170		userArray = NULL;
171	}
172	return userArray;
173
174}
175
176/*
177 * Check to see if there is a workgroup/domain in the URL. If yes then return an array
178 * with the first element as the workgroup and the second element containing the
179 * username and server name. If no workgroup just return NULL.
180 */
181static CFArrayRef CreateWrkgrpUserArray(CFURLRef url)
182{
183	CFStringRef netlocation = NULL;
184	CFArrayRef	userArray = NULL;
185
186	netlocation = CFURLCopyNetLocation(url);
187	if (!netlocation)
188		return NULL;
189
190	userArray = CreateWrkgrpUserArrayFromCFStringRef(netlocation, CFSTR(";"));
191	CFRelease(netlocation);
192	return userArray;
193}
194
195
196/*
197 * Get the server name out of the URL. CFURLCopyHostName will escape out the
198 * server name for us. So just convert it to the correct code page encoding.
199 *
200 * Note: Currently we put the server name into a c-style string. In the future
201 * it would be nice to keep this as a CFString.
202 */
203static int SetServerFromURL(struct smb_ctx *ctx, CFURLRef url)
204{
205	CFIndex maxlen;
206	CFStringRef serverNameRef = CFURLCopyHostName(url);
207	char *ipV6Name = NULL;
208
209	ipV6Name = CStringCreateWithCFString(serverNameRef);
210	if (ipV6Name && isIPv6NumericName(ipV6Name)) {
211        /* CFURLCopyHostName removed the [] so put them back in */
212        CFMutableStringRef newServer = CFStringCreateMutableCopy(NULL, 1024, CFSTR("["));
213        if (newServer) {
214            CFStringAppend(newServer, serverNameRef);
215            CFStringAppend(newServer, CFSTR("]"));
216            CFRelease(serverNameRef);
217            serverNameRef = newServer;
218        }
219    }
220
221	/* Free up the buffer we allocated */
222	if (ipV6Name) {
223		free(ipV6Name);
224    }
225
226    /*
227	 * Every time we parse the URL we end up replacing the server name. In the
228	 * future we should skip replacing the server name if we already have one and
229	 * the one return CFURLCopyHostName matches it.
230	 *
231	 * Not going to make that big of a change in an update, so lets limit to the
232	 * case were we are dealing with using a domain controller.
233	 */
234	if (serverNameRef && ctx->serverNameRef && ctx->serverName &&
235		(ctx->serverIsDomainController) &&
236		(ctx->ct_flags & SMBCF_CONNECTED) &&
237		(CFStringCompare(serverNameRef, ctx->serverNameRef, 0) == kCFCompareEqualTo)) {
238		CFRelease(serverNameRef);
239		return 0; /* Same name nothing to do here */
240	}
241	if (ctx->serverNameRef) {
242		CFRelease(ctx->serverNameRef);
243	}
244		/* The serverNameRef should always contain the URL host name or the Bonjour Name */
245	ctx->serverNameRef = serverNameRef;
246	if (ctx->serverNameRef == NULL)
247		return EINVAL;
248	DebugLogCFString(ctx->serverNameRef, "Server", __FUNCTION__, __LINE__);
249
250	maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ctx->serverNameRef), kCFStringEncodingUTF8) + 1;
251	if (ctx->serverName)
252		free(ctx->serverName);
253	ctx->serverName = malloc(maxlen);
254	if (!ctx->serverName) {
255		CFRelease(ctx->serverNameRef);
256		ctx->serverNameRef = NULL;
257		return ENOMEM;
258	}
259	CFStringGetCString(ctx->serverNameRef, ctx->serverName, maxlen, kCFStringEncodingUTF8);
260	return 0;
261}
262
263/*
264 * Get the user and workgroup names and return them in CFStringRef.
265 * First get the CFURLCopyNetLocation because it will not escape out the string.
266 */
267static CFStringRef CopyUserAndWorkgroupFromURL(CFStringRef *outWorkGroup, CFURLRef url)
268{
269	CFURLRef net_url = NULL;
270	CFArrayRef	userArray = NULL;
271	CFStringRef userString = NULL;
272	CFMutableStringRef urlString = NULL;
273	CFStringRef wrkgrpString = NULL;
274
275	*outWorkGroup = NULL;	/* Always start like we didn't get one. */
276	/* This will return null if no workgroup in the URL */
277	userArray = CreateWrkgrpUserArray(url);
278	if (!userArray)	/* We just have a username name  */
279		return(CFURLCopyUserName(url));	/* This will escape out the character for us. */
280
281	/* Now for the hard part; netlocation contains one of the following:
282	 *
283	 * URL = "//workgroup;username:password@smb-win2003.apple.com"
284	 * URL = "//workgroup;username:@smb-win2003.apple.com"
285	 * URL = "//workgroup;username@smb-win2003.apple.com"
286	 * URL = "//workgroup;@smb-win2003.apple.com"
287	 */
288	/* Get the username first */
289	urlString = CFStringCreateMutableCopy(NULL, 1024, CFSTR("smb://"));
290	if (!urlString) {
291		CFRelease(userArray);
292		return(CFURLCopyUserName(url));	/* This will escape out the character for us. */
293	}
294	CFStringAppend(urlString, (CFStringRef)CFArrayGetValueAtIndex(userArray, 1));
295	net_url = CFURLCreateWithString(NULL, urlString, NULL);
296	CFRelease(urlString);
297	urlString = NULL;
298	/* Not sure what to do if we fail here */
299	if (!net_url) {
300		CFRelease(userArray);
301		return(CFURLCopyUserName(url));	/* This will escape out the character for us. */
302	}
303	/* We now have a URL without the workgroup name, just copy out the username. */
304	userString = CFURLCopyUserName(net_url);
305	CFRelease(net_url);
306
307	/* Now get the workgroup */
308	wrkgrpString = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 0));
309	CreateStringByReplacingPercentEscapesUTF8(&wrkgrpString, CFSTR(""));
310	if (wrkgrpString)
311		*outWorkGroup = wrkgrpString;	/* We have the workgroup return it to the calling routine */
312	CFRelease(userArray);
313	return(userString);
314}
315
316/*
317 * Get the workgroup and return a username CFStringRef if it exist.
318 * First get the CFURLCopyNetLocation because it will not escape out the string.
319 */
320static CFStringRef SetWorkgroupFromURL(struct smb_ctx *ctx, CFURLRef url)
321{
322	CFStringRef userString = NULL;
323	CFStringRef wrkgrpString = NULL;
324
325	userString = CopyUserAndWorkgroupFromURL(&wrkgrpString, url);
326
327	if (wrkgrpString) {
328		LogCFString(wrkgrpString, "Workgroup", __FUNCTION__, __LINE__);
329		if (CFStringGetLength(wrkgrpString) <= SMB_MAXNetBIOSNAMELEN) {
330			str_upper(ctx->ct_setup.ioc_domain, sizeof(ctx->ct_setup.ioc_domain), wrkgrpString);
331		}
332		CFRelease(wrkgrpString);
333	}
334	return(userString);
335}
336
337/*
338 * Need to call SetWorkgroupFromURL just in case we have a workgroup name. CFURL does not handle
339 * a CIFS style URL with a workgroup name.
340 */
341static int SetUserNameFromURL(struct smb_ctx *ctx, CFURLRef url)
342{
343	CFStringRef userNameRef = SetWorkgroupFromURL(ctx, url);
344	char username[SMB_MAXUSERNAMELEN+1];
345	int error;
346
347	/* No user name in the URL */
348	if (! userNameRef)
349		return 0;
350	LogCFString(userNameRef, "Username",__FUNCTION__, __LINE__);
351
352	/* Conversion failed or the data doesn't fit in the buffer */
353	if (CFStringGetCString(userNameRef, username, SMB_MAXUSERNAMELEN+1, kCFStringEncodingUTF8) == FALSE) {
354		error = ENAMETOOLONG; /* Not sure what else to return. */
355	} else {
356		error = smb_ctx_setuser(ctx, username);
357	}
358	CFRelease(userNameRef);
359	return error;
360}
361
362/*
363 * The URL may contain no password, an empty password, or a password. An empty password is a passowrd
364 * and should be treated the same as a password. This is need to make guest access work.
365 *
366 *	URL "smb://username:password@server/share" should set the password.
367 *	URL "smb://username:@server/" should set the password.
368 *	URL "smb://username@server/share" should not set the password.
369 *	URL "smb://server/share/path" should not set the password.
370 *
371 */
372static int SetPasswordFromURL(struct smb_ctx *ctx, CFURLRef url)
373{
374	CFStringRef passwd = CFURLCopyPassword(url);
375
376	/*  URL =" //username@smb-win2003.apple.com" or URL =" //smb-win2003.apple.com" */
377	if (! passwd)
378		return 0;
379
380	/* Password is too long return an error */
381	if (CFStringGetLength(passwd) >= SMB_MAXPASSWORDLEN) {
382		CFRelease(passwd);
383		return ENAMETOOLONG;
384	}
385	/*
386	 * Works for password and empty password
387	 *
388	 * URL = "//username:password@smb-win2003.apple.com"
389	 * URL = "//username:@smb-win2003.apple.com"
390	 */
391	CFStringGetCString(passwd, ctx->ct_setup.ioc_password, SMB_MAXPASSWORDLEN, kCFStringEncodingUTF8);
392	ctx->ct_flags |= SMBCF_EXPLICITPWD;
393	CFRelease(passwd);
394	return 0;
395}
396
397/*
398 * If URL contains a port then we should get it and set the correct flag.
399 *
400 *	URL "smb://username:password@server:445/share" set the port to 445.
401 *
402 */
403static void SetPortNumberFromURL(struct smb_ctx *ctx, CFURLRef url)
404{
405	SInt32 port = CFURLGetPortNumber(url);
406
407	/* No port defined in the URL */
408	if (port == -1)
409		return;
410	/* They supplied a port number use it and only it */
411	ctx->prefs.tcp_port = port;
412	ctx->prefs.tryBothPorts = FALSE;
413	smb_log_info("Setting port number to %d", ASL_LEVEL_DEBUG, ctx->prefs.tcp_port);
414}
415
416/*
417 * We need to separate the share name and any path component from the URL.
418 *	URL "smb://username:password@server" no share name or path.
419 *	URL "smb://username:password@server/"no share name or path.
420 *	URL "smb://username:password@server/share" just a share name.
421 *	URL "smb://username:password@server/share/path" share name and path.
422 *
423 * The Share name and Path name will not begin with a slash.
424 *		smb://server/ntfs  share = ntfs path = NULL
425 *		smb://ntfs/dir1/dir2  share = ntfs path = dir1/dir2
426 *		smb://server/OPEN%2fSPACE/dir1   share = OPEN%2fSPACE path = dir1
427 */
428static int GetShareAndPathFromURL(CFURLRef url, CFStringRef *out_share, CFStringRef *out_path)
429{
430	Boolean isAbsolute;
431	CFArrayRef userArray = NULL;
432	CFMutableArrayRef userArrayM = NULL;
433	CFStringRef share = CFURLCopyStrictPath(url, &isAbsolute);
434	CFStringRef path = NULL;
435
436	*out_share = NULL;
437	*out_path = NULL;
438	/* We have an empty share treat it like no share */
439	if (share && (CFStringGetLength(share) == 0)) {
440		CFRelease(share);
441		share = NULL;
442	}
443	/* Since there is no share name we have nothing left to do. */
444	if (!share)
445		return 0;
446
447	userArray = CFStringCreateArrayBySeparatingStrings(NULL, share, CFSTR("/"));
448	if (userArray && (CFArrayGetCount(userArray) > 1))
449		userArrayM = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(userArray), userArray);
450
451	if (userArray)
452		CFRelease(userArray);
453
454	if (userArrayM) {
455		CFMutableStringRef newshare;	/* Just in case something goes wrong */
456
457		newshare = CFStringCreateMutableCopy(NULL, 0, (CFStringRef)CFArrayGetValueAtIndex(userArrayM, 0));
458		if (newshare) {
459			CFStringTrim(newshare, CFSTR("/"));	/* Remove any trailing slashes */
460			CreateStringByReplacingPercentEscapesUTF8((CFStringRef *) &newshare, CFSTR("/"));
461		}
462		CFArrayRemoveValueAtIndex(userArrayM, 0);
463			/* Now remove any trailing slashes */
464		path = CFStringCreateByCombiningStrings(NULL, userArrayM, CFSTR("/"));
465		if (path && (CFStringGetLength(path) == 0)) {
466			CFRelease(path);	/* Empty path remove it */
467			path = NULL;
468		}
469		if (path) {
470			CFMutableStringRef newpath = CFStringCreateMutableCopy(NULL, 0, path);
471			if (newpath) {
472				CFStringTrim(newpath, CFSTR("/")); 	/* Remove any trailing slashes */
473				CFRelease(path);
474				path = newpath;
475			}
476		}
477		if (path) {
478			CreateStringByReplacingPercentEscapesUTF8(&path, CFSTR("/"));
479			LogCFString(path, "Path", __FUNCTION__, __LINE__);
480		}
481
482		CFRelease(userArrayM);
483		/* Something went wrong use the original value */
484		if (newshare) {
485			CFRelease(share);
486			share = newshare;
487		}
488	} else
489		CreateStringByReplacingPercentEscapesUTF8(&share, CFSTR("/"));
490
491	/*
492	 * The above routines will not un-precent escape out slashes. We only allow for the cases
493	 * where the share name is a single slash. Slashes are treated as delemiters in the path name.
494	 * So if the share name has a single 0x2f then make it a slash. This means you can't have
495	 * a share name whos name is 0x2f, not likley to happen.
496	 */
497	if (share && ( kCFCompareEqualTo == CFStringCompare (share, CFSTR("0x2f"), kCFCompareCaseInsensitive) )) {
498		CFRelease(share);
499		share = CFStringCreateCopy(NULL, CFSTR("/"));
500	}
501
502
503	if (share && (CFStringGetLength(share) >= SMB_MAXSHARENAMELEN)) {
504		CFRelease(share);
505		if (path)
506			CFRelease(path);
507		return ENAMETOOLONG;
508	}
509
510	*out_share = share;
511	*out_path = path;
512	return 0;
513}
514
515/*
516 * We need to separate the share name and any path component from the URL.
517 *	URL "smb://username:password@server" no share name or path.
518 *	URL "smb://username:password@server/"no share name or path.
519 *	URL "smb://username:password@server/share" just a share name.
520 *	URL "smb://username:password@server/share/path" share name and path.
521 *
522 * The Share name and Path name will not begin with a slash.
523 *		smb://server/ntfs  share = ntfs path = NULL
524 *		smb://ntfs/dir1/dir2  share = ntfs path = dir1/dir2
525 */
526static int SetShareAndPathFromURL(struct smb_ctx *ctx, CFURLRef url)
527{
528	CFStringRef share = NULL;
529	CFStringRef path = NULL;
530	CFIndex maxlen;
531	int error;
532
533	error = GetShareAndPathFromURL(url, &share, &path);
534	if (error) {
535		return error;
536    }
537
538	/* Since there is no share name we have nothing left to do. */
539	if (share == NULL) {
540        if (path != NULL) {
541            CFRelease(path);
542        }
543
544		return 0;
545    }
546
547	DebugLogCFString(share, "Share", __FUNCTION__, __LINE__);
548
549	CreateStringByReplacingPercentEscapesUTF8(&share, CFSTR(""));
550
551	if (ctx->ct_origshare) {
552		free(ctx->ct_origshare);
553    }
554
555	if (ctx->mountPath) {
556		CFRelease(ctx->mountPath);
557    }
558	ctx->mountPath = NULL;
559
560	maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(share), kCFStringEncodingUTF8) + 1;
561	ctx->ct_origshare = malloc(maxlen);
562	if (!ctx->ct_origshare) {
563		CFRelease(share);
564
565        if (path != NULL) {
566            CFRelease(path);
567        }
568
569		return ENOMEM;
570	}
571
572	CFStringGetCString(share, ctx->ct_origshare, maxlen, kCFStringEncodingUTF8);
573	str_upper(ctx->ct_sh.ioc_share, sizeof(ctx->ct_sh.ioc_share), share);
574	CFRelease(share);
575
576	ctx->mountPath = path;
577
578	return 0;
579}
580
581/*
582 * Here we expect something like
583 *   "//[workgroup;][user[:password]@]host[/share[/path]]"
584 * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
585 */
586int ParseSMBURL(struct smb_ctx *ctx)
587{
588	int error  = EINVAL;
589
590	/* Make sure its a good URL, better be at this point */
591	if ((!CFURLCanBeDecomposed(ctx->ct_url)) || (SMBSchemeLength(ctx->ct_url) < 0)) {
592		smb_log_info("This is an invalid URL, syserr = %s", ASL_LEVEL_ERR,
593					 strerror(error));
594		return error;
595	}
596
597	error = SetServerFromURL(ctx, ctx->ct_url);
598	if (error) {
599		smb_log_info("The URL has a bad server name, syserr = %s", ASL_LEVEL_ERR,
600					 strerror(error));
601		return error;
602	}
603	error = SetUserNameFromURL(ctx, ctx->ct_url);
604	if (error) {
605		smb_log_info("The URL has a bad user name, syserr = %s", ASL_LEVEL_ERR,
606					 strerror(error));
607		return error;
608	}
609	error = SetPasswordFromURL(ctx, ctx->ct_url);
610	if (error) {
611		smb_log_info("The URL has a bad password, syserr = %s", ASL_LEVEL_ERR,
612					 strerror(error));
613		return error;
614	}
615	SetPortNumberFromURL(ctx, ctx->ct_url);
616	error = SetShareAndPathFromURL(ctx, ctx->ct_url);
617	/* CFURLCopyQueryString to get ?WINS=msfilsys.apple.com;NODETYPE=H info */
618	return error;
619}
620
621/*
622 * Given a Dfs Referral string create a CFURL. Remember referral have a file
623 * syntax
624 *
625 * Example: "/smb-win2003.apple.com/DfsRoot/DfsLink1"
626 */
627CFURLRef CreateURLFromReferral(CFStringRef inStr)
628{
629	CFURLRef ct_url = NULL;
630	CFMutableStringRef urlString = CFStringCreateMutableCopy(NULL, 0, CFSTR("smb:/"));
631	CFStringRef escapeStr = inStr;
632
633    /*
634     * CreateStringByAddingPercentEscapesUTF8() will either create a new string
635     * or return the original with ref count incremented. Either way we have to
636     * call CFRelease on the returned string
637     */
638	CreateStringByAddingPercentEscapesUTF8(&escapeStr, NULL, NULL, FALSE);
639
640	if (urlString) {
641		CFStringAppend(urlString, escapeStr);
642		ct_url = CFURLCreateWithString(kCFAllocatorDefault, urlString, NULL);
643		CFRelease(urlString);	/* We create it now release it */
644	}
645
646	if (!ct_url) {
647		LogCFString(inStr, "creating url failed", __FUNCTION__, __LINE__);
648	}
649
650    if (escapeStr) {
651        CFRelease(escapeStr);
652    }
653
654	return ct_url;
655}
656
657/*
658 * Given a c-style string create a CFURL. We assume the c-style string is in
659 * URL or UNC format. Anything else will give unexpected behavior.
660 * NOTE: The library code doesn't care if the scheme exist or not in the URL,
661 * but we attempt to create a URL with a scheme, just for correctness sake.
662 *
663 * Note: If its a URL, then do not escape it out again since it should already
664 * be escaped out properly.
665 */
666CFURLRef CreateSMBURL(const char *url)
667{
668	CFURLRef ct_url = NULL;
669	CFStringRef urlString = CFStringCreateWithCString(NULL,
670                                                      url,
671                                                      kCFStringEncodingUTF8);
672    CFStringRef escapedUrlString;
673    int UNCformat = 0;
674
675    escapedUrlString = NULL;
676
677	/*
678	 * We have a UNC path that we need to convert into a SMB URL. Currently we
679	 * just replace the backslashes with slashes
680	 */
681   if (urlString && (*url == '\\') && (*(url + 1) == '\\')) {
682       CFArrayRef urlArray = CFStringCreateArrayBySeparatingStrings(NULL,
683                                                                    urlString,
684                                                                    CFSTR("\\"));
685
686	   CFRelease(urlString);
687	   urlString = NULL;
688	   if (urlArray) {
689		   urlString = CFStringCreateByCombiningStrings(NULL,
690                                                        urlArray,
691                                                        CFSTR("/"));
692		   CFRelease(urlArray);
693	   }
694       UNCformat = 1;
695    }
696
697	/* Something failed just get out */
698	if (!urlString) {
699		return NULL;
700    }
701
702	DebugLogCFString(urlString, "urlString ", __FUNCTION__, __LINE__);
703
704	/*
705	 * No scheme, add one if we can, but not required by the library code.
706	 * NOTE: If no scheme, then expect the string to start with double slashes.
707	 */
708	if ((!CFStringHasPrefix(urlString, CFSTR("smb://"))) &&
709		(!CFStringHasPrefix(urlString, CFSTR("cifs://")))) {
710		CFMutableStringRef urlStringM = CFStringCreateMutableCopy(NULL,
711                                                                  1024,
712                                                                  CFSTR("smb:"));
713
714		if (urlStringM) {
715			CFStringAppend(urlStringM, urlString);
716			CFRelease(urlString);
717			urlString = urlStringM;
718		}
719	}
720
721    /* Something failed just get out */
722    if (urlString == NULL) {
723        return (NULL);
724    }
725
726    if (UNCformat == 1) {
727        /* For UNC format strings, escape out any non-URL characters */
728        escapedUrlString = CFURLCreateStringByAddingPercentEscapes(NULL,
729                                                                   urlString,
730                                                                   NULL,
731                                                                   NULL,
732                                                                   kCFStringEncodingUTF8);
733        CFRelease(urlString);	/* Can release it now */
734
735        /* Something failed just get out */
736        if (escapedUrlString == NULL) {
737            return (NULL);
738        }
739
740        /* now create the URL */
741        ct_url = CFURLCreateWithString(kCFAllocatorDefault,
742                                       escapedUrlString,
743                                       NULL);
744
745        CFRelease(escapedUrlString);	/* We create it now release it */
746    }
747    else {
748        /*
749         * For URL format strings, it should already be escaped.
750         * Just create the URL.
751         */
752        ct_url = CFURLCreateWithString(kCFAllocatorDefault,
753                                       urlString,
754                                       NULL);
755
756        CFRelease(urlString);	/* Can release it now */
757    }
758
759	return ct_url;
760}
761
762/*
763 * Given a url parse it and place the component in a dictionary we create.
764 */
765int smb_url_to_dictionary(CFURLRef url, CFDictionaryRef *dict)
766{
767	CFMutableDictionaryRef mutableDict = NULL;
768	int error  = 0;
769	CFStringRef Server = NULL;
770	CFStringRef Username = NULL;
771	CFStringRef DomainWrkgrp = NULL;
772	CFStringRef Password = NULL;
773	CFStringRef Share = NULL;
774	CFStringRef Path = NULL;
775	CFStringRef Port = NULL;
776
777	/* Make sure its a good URL, better be at this point */
778	if ((!CFURLCanBeDecomposed(url)) || (SMBSchemeLength(url) < 0)) {
779		smb_log_info("%s: Invalid URL, syserr = %s", ASL_LEVEL_ERR,
780					 __FUNCTION__, strerror(EINVAL));
781		goto ErrorOut;
782	}
783
784	/* create and return the server parameters dictionary */
785	mutableDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
786												&kCFTypeDictionaryValueCallBacks);
787	if (mutableDict == NULL) {
788		error = errno;
789		smb_log_info("%s: CFDictionaryCreateMutable failed, syserr = %s",
790					 ASL_LEVEL_ERR, __FUNCTION__, strerror(error));
791		goto ErrorOut;
792	}
793
794	/*
795	 * SMB can have two different scheme's cifs or smb. When we made SMBSchemeLength call at the
796	 * start of this routine it made sure we had one or the other scheme. Always default here to
797	 * the SMB scheme.
798	 */
799	CFDictionarySetValue (mutableDict, kNetFSSchemeKey, CFSTR(SMB_SCHEME_STRING));
800
801    error = NetFSCopyHostAndPort(url, &Server, &Port);
802	if ((Server == NULL) || (error != noErr)) {
803        if (Port != NULL) {
804            CFRelease(Port);
805        }
806		goto ErrorOut; /* Server name is required */
807    }
808
809	LogCFString(Server, "Server String", __FUNCTION__, __LINE__);
810	CFDictionarySetValue (mutableDict, kNetFSHostKey, Server);
811	CFRelease(Server);
812	Server = NULL;
813
814    if (Port != NULL) {
815        CFDictionarySetValue (mutableDict, kNetFSAlternatePortKey, Port);
816        CFRelease(Port);
817        Port = NULL;
818    }
819
820	Username = CopyUserAndWorkgroupFromURL(&DomainWrkgrp, url);
821	LogCFString(Username, "Username String", __FUNCTION__, __LINE__);
822	LogCFString(DomainWrkgrp, "DomainWrkgrp String", __FUNCTION__, __LINE__);
823	error = 0;
824	if ((Username) && (CFStringGetLength(Username) >= SMB_MAXUSERNAMELEN))
825		error = ENAMETOOLONG;
826
827	if ((DomainWrkgrp) && (CFStringGetLength(DomainWrkgrp) > SMB_MAXNetBIOSNAMELEN))
828		error = ENAMETOOLONG;
829
830	if (error) {
831		if (Username)
832			CFRelease(Username);
833		if (DomainWrkgrp)
834			CFRelease(DomainWrkgrp);
835		goto ErrorOut; /* Username or Domain name is too long */
836	}
837
838	/*
839	 * We have a domain name so combined it with the user name so we can it
840	 * display to the user. We now test to make sure we have a username. Having
841	 * a domain without a username makes no sense, so don't return either.
842	 */
843	if (DomainWrkgrp && Username && CFStringGetLength(Username)) {
844		CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, DomainWrkgrp);
845
846		if (tempString) {
847			CFStringAppend(tempString, CFSTR("\\"));
848			CFStringAppend(tempString, Username);
849			CFRelease(Username);
850			Username = tempString;
851		}
852	}
853
854	if (Username)
855	{
856		CFDictionarySetValue (mutableDict, kNetFSUserNameKey, Username);
857		CFRelease(Username);
858	}
859
860    if (DomainWrkgrp) {
861		CFRelease(DomainWrkgrp);
862    }
863
864	Password = CFURLCopyPassword(url);
865	if (Password) {
866		if (CFStringGetLength(Password) >= SMB_MAXPASSWORDLEN) {
867			error = ENAMETOOLONG;
868			CFRelease(Password);
869			goto ErrorOut; /* Password is too long */
870		}
871		CFDictionarySetValue (mutableDict, kNetFSPasswordKey, Password);
872		CFRelease(Password);
873	}
874
875	/*
876	 * We used to keep the share and path as two different elements in the dictionary. This was
877	 * changed to satisfy NetFS and other plugins. We still need to check and make sure the
878	 * share and path are correct. So now split them apart and then put them put them back together.
879	 */
880	error = GetShareAndPathFromURL(url, &Share, &Path);
881	if (error)
882		goto ErrorOut; /* Share name is too long */
883
884	LogCFString(Share, "Share String", __FUNCTION__, __LINE__);
885	LogCFString(Path, "Path String", __FUNCTION__, __LINE__);
886
887	if (Share && Path) {
888		/*
889		 * We have a share and path, but there is nothing in the
890		 * share, then return an error
891		 */
892	    if (CFStringGetLength(Share) == 0) {
893			CFRelease(Path);
894			CFRelease(Share);
895			Share = Path = NULL;
896			error = EINVAL;
897			smb_log_info("%s: No share name found, syserr = %s",
898						 ASL_LEVEL_ERR, __FUNCTION__, strerror(error));
899			goto ErrorOut;
900		}
901		if (CFStringGetLength(Path)) {
902			CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, Share);
903			if (tempString) {
904				CFStringAppend(tempString, CFSTR("/"));
905				CFStringAppend(tempString, Path);
906				CFDictionarySetValue (mutableDict, kNetFSPathKey, tempString);
907				CFRelease(tempString);
908				CFRelease(Share);
909				Share = NULL;
910			}
911		}
912	}
913	/* Ignore any empty share at this point */
914	if (Share && CFStringGetLength(Share))
915		CFDictionarySetValue (mutableDict, kNetFSPathKey, Share);
916
917	if (Share)
918		CFRelease(Share);
919
920	if (Path)
921		CFRelease(Path);
922
923	*dict = mutableDict;
924	return 0;
925
926ErrorOut:
927
928	*dict = NULL;
929	if (mutableDict)
930		CFRelease(mutableDict);
931	if (!error)	/* No error set it to the default error */
932		error = EINVAL;
933	return error;
934
935}
936
937/*
938 * Given a dictionary create a url string. We assume that the dictionary has any characters that need to
939 * be escaped out escaped out.
940 */
941static int smb_dictionary_to_urlstring(CFDictionaryRef dict, CFMutableStringRef *urlReturnString)
942{
943	int error = 0;
944	CFMutableStringRef urlStringM = NULL;
945	CFStringRef DomainWrkgrp = NULL;
946	CFStringRef Username = NULL;
947	CFStringRef Password = NULL;
948	CFStringRef Server = NULL;
949	CFStringRef PortNumber = NULL;
950	CFStringRef Path = NULL;
951	Boolean	releaseUsername = FALSE;
952	char *ipV6Name = NULL;
953
954	urlStringM = CFStringCreateMutableCopy(NULL, 1024, CFSTR("smb://"));
955	if (urlStringM == NULL) {
956		error = errno;
957		smb_log_info("%s: couldn't allocate the url string, syserr = %s",
958					 ASL_LEVEL_ERR, __FUNCTION__, strerror(error));
959		goto WeAreDone;
960	}
961
962	/* Get the server name, required value */
963	Server = CFDictionaryGetValue(dict, kNetFSHostKey);
964	if (Server == NULL) {
965		error = EINVAL;
966		smb_log_info("%s: no server name, syserr = %s", ASL_LEVEL_ERR,
967					 __FUNCTION__, strerror(error));
968		goto WeAreDone;
969	}
970
971	/*
972	 * So we have three basic server names to cover here.
973	 *
974	 * 1. Bonjour Name requires these /@:,?=;&+$ extra characters to be percent
975	 *	  escape out.
976	 *
977	 * 2. NetBIOS Name requires these ~!@#$%^&;'.(){} extra characters to be
978	 *	  percent escape out.
979	 *
980	 * 3. DNS Names requires the DOT IPv6 Notification address to be enclosed in
981	 *	  square brackets and that the colon not to be escaped out.
982	 *
983	 * Note that CFURLCopyHostName will remove the square brackets from the DOT
984	 * IPv6 Notification address. smb_url_to_dictionary will put the brackets
985     * back into the IPv6 address in the dictionary. We expect this behavior so
986     * anyone changing the dictionary server field will need to make sure
987	 * it does have square brackets.
988	 */
989	ipV6Name = CStringCreateWithCFString(Server);
990
991	/* Is this an IPv6 numeric name, then leave the square brackets around it */
992	if (ipV6Name && isIPv6NumericName(ipV6Name)) {
993        CreateStringByAddingPercentEscapesUTF8(&Server, CFSTR("[]"), NULL, FALSE);
994    }
995    else {
996		/* Some other name make sure we percent escape all the characters needed */
997		CreateStringByAddingPercentEscapesUTF8(&Server, NULL, CFSTR("~!'()/@:,?=;&+$"), FALSE);
998	}
999
1000	/* Free up the buffer we allocated */
1001	if (ipV6Name) {
1002		free(ipV6Name);
1003    }
1004
1005	if (Server == NULL) {
1006		error = EINVAL;
1007		smb_log_info("%s: still no server name, syserr = %s", ASL_LEVEL_ERR,
1008					 __FUNCTION__, strerror(error));
1009		goto WeAreDone;
1010	}
1011
1012	/* Now get all the other parts of the url. */
1013	Username = CFDictionaryGetValue(dict, kNetFSUserNameKey);
1014	/* We have a user name see if they entered a domain also. */
1015	if (Username) {
1016		CFArrayRef	userArray = NULL;
1017		/*
1018		 * Remember that on windows a back slash is illegal, so if someone wants to use one
1019		 * in the username they will need to escape percent it out.
1020		 */
1021		userArray = CreateWrkgrpUserArrayFromCFStringRef(Username, CFSTR("\\"));
1022		/* If we have an array then we have a domain\username in the Username String */
1023		if (userArray) {
1024			DomainWrkgrp = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 0));
1025			Username = CFStringCreateCopy(NULL, (CFStringRef)CFArrayGetValueAtIndex(userArray, 1));
1026			CFRelease(userArray);
1027			releaseUsername = TRUE;
1028		}
1029	}
1030
1031	Password = CFDictionaryGetValue(dict, kNetFSPasswordKey);
1032	Path = CFDictionaryGetValue(dict, kNetFSPathKey);
1033	PortNumber = CFDictionaryGetValue(dict, kNetFSAlternatePortKey);
1034
1035	/*
1036	 * Percent escape out any URL special characters, for the username, password, Domain/Workgroup,
1037	 * path, and port. Not sure the port is required, but AFP does it so why not.
1038	 * The CreateStringByAddingPercentEscapesUTF8 will return either NULL or a value that must be
1039	 * released.
1040	 */
1041	CreateStringByAddingPercentEscapesUTF8(&DomainWrkgrp, NULL, CFSTR("@:;/?"), TRUE);
1042	CreateStringByAddingPercentEscapesUTF8(&Username, NULL, CFSTR("@:;/?"), releaseUsername);
1043	CreateStringByAddingPercentEscapesUTF8(&Password, NULL, CFSTR("@:;/?"), FALSE);
1044	CreateStringByAddingPercentEscapesUTF8(&Path, CFSTR("%%"), CFSTR("?#"), FALSE);
1045	CreateStringByAddingPercentEscapesUTF8(&PortNumber, NULL, NULL, FALSE);
1046
1047	LogCFString(Username, "Username String", __FUNCTION__, __LINE__);
1048	LogCFString(DomainWrkgrp, "Domain String", __FUNCTION__, __LINE__);
1049	LogCFString(Path, "Path String", __FUNCTION__, __LINE__);
1050	LogCFString(PortNumber, "PortNumber String", __FUNCTION__, __LINE__);
1051
1052	/* Add the Domain/Workgroup */
1053	if (DomainWrkgrp) {
1054		CFStringAppend(urlStringM, DomainWrkgrp);
1055		CFStringAppend(urlStringM, CFSTR(";"));
1056	}
1057	/* Add the username and password */
1058	if (Username || Password) {
1059		if (Username)
1060			CFStringAppend(urlStringM, Username);
1061		if (Password) {
1062			CFStringAppend(urlStringM, CFSTR(":"));
1063			CFStringAppend(urlStringM, Password);
1064		}
1065		CFStringAppend(urlStringM, CFSTR("@"));
1066	}
1067
1068	/* Add the server */
1069	CFStringAppend(urlStringM, Server);
1070
1071	/* Add the port number */
1072	if (PortNumber) {
1073		CFStringAppend(urlStringM, CFSTR(":"));
1074		CFStringAppend(urlStringM, PortNumber);
1075	}
1076
1077	/* Add the share and path */
1078	if (Path) {
1079		CFStringAppend(urlStringM, CFSTR("/"));
1080		/* If the share name is a slash percent escape it out */
1081		if ( kCFCompareEqualTo == CFStringCompare (Path, CFSTR("/"), kCFCompareCaseInsensitive) )
1082			CFStringAppend(urlStringM, CFSTR("0x2f"));
1083		else
1084			CFStringAppend(urlStringM, Path);
1085	}
1086
1087	DebugLogCFString(urlStringM, "URL String", __FUNCTION__, __LINE__);
1088
1089WeAreDone:
1090	if (Username)
1091		CFRelease(Username);
1092	if (Password)
1093		CFRelease(Password);
1094	if (DomainWrkgrp)
1095		CFRelease(DomainWrkgrp);
1096	if (Path)
1097		CFRelease(Path);
1098	if (PortNumber)
1099		CFRelease(PortNumber);
1100	if (Server)
1101		CFRelease(Server);
1102
1103	if (error == 0)
1104		*urlReturnString = urlStringM;
1105	else if (urlStringM)
1106		CFRelease(urlStringM);
1107
1108	return error;
1109}
1110
1111
1112/*
1113 * Given a dictionary create a url. We assume that the dictionary has any characters that need to
1114 * be escaped out escaped out.
1115 */
1116int smb_dictionary_to_url(CFDictionaryRef dict, CFURLRef *url)
1117{
1118	int error;
1119	CFMutableStringRef urlStringM = NULL;
1120
1121	error = smb_dictionary_to_urlstring(dict, &urlStringM);
1122	/* Ok we have everything we need for the URL now create it. */
1123	if ((error == 0) && urlStringM) {
1124		*url = CFURLCreateWithString(NULL, urlStringM, NULL);
1125		if (*url == NULL)
1126			error = errno;
1127	}
1128
1129	if (urlStringM)
1130		CFRelease(urlStringM);
1131	if (error)
1132		smb_log_info("%s: creating the url failed, syserr = %s", ASL_LEVEL_ERR,
1133					 __FUNCTION__, strerror(error));
1134
1135	return error;
1136
1137}
1138
1139CFStringRef CreateURLCFString(CFStringRef Domain, CFStringRef Username,
1140                              CFStringRef Password, CFStringRef ServerName,
1141                              CFStringRef Path, CFStringRef PortNumber)
1142{
1143	CFMutableDictionaryRef mutableDict = NULL;
1144	int error;
1145	CFMutableStringRef urlString = NULL;
1146
1147	mutableDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
1148											&kCFTypeDictionaryValueCallBacks);
1149	if (mutableDict == NULL) {
1150		smb_log_info("%s: CFDictionaryCreateMutable failed, syserr = %s",
1151					 ASL_LEVEL_ERR, __FUNCTION__, strerror(errno));
1152		return NULL;
1153	}
1154
1155	if (Domain && Username) {
1156		CFMutableStringRef tempString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, Domain);
1157
1158		if (tempString) {
1159			CFStringAppend(tempString, CFSTR("\\"));
1160			CFStringAppend(tempString, Username);
1161			Username = tempString;
1162		}
1163        else {
1164			CFRetain(Username);
1165		}
1166	} else if (Username) {
1167		CFRetain(Username);
1168	}
1169
1170	if (Username) {
1171		CFDictionarySetValue(mutableDict, kNetFSUserNameKey, Username);
1172		CFRelease(Username);
1173	}
1174
1175	if (Password) {
1176		CFDictionarySetValue(mutableDict, kNetFSPasswordKey, Password);
1177	}
1178
1179	if (ServerName) {
1180		CFDictionarySetValue(mutableDict, kNetFSHostKey, ServerName);
1181	}
1182
1183	if (Path) {
1184		CFDictionarySetValue (mutableDict, kNetFSPathKey, Path);
1185	}
1186
1187	if (PortNumber) {
1188		CFDictionarySetValue (mutableDict, kNetFSAlternatePortKey, PortNumber);
1189	}
1190
1191	error = smb_dictionary_to_urlstring(mutableDict, &urlString);
1192	CFRelease(mutableDict);
1193
1194	if (error) {
1195		errno = error;
1196	}
1197
1198	return urlString;
1199}
1200
1201/*
1202 * Check to make sure we have the correct user name and share. If we already have
1203 * both a username and share name then we are done, otherwise get the latest stuff.
1204 */
1205static void UpdateDictionaryWithUserAndShare(struct smb_ctx *ctx, CFMutableDictionaryRef mutableDict)
1206{
1207	CFStringRef DomainWrkgrp = NULL;
1208	CFStringRef Username = NULL;
1209	CFStringRef share = NULL;
1210	CFMutableStringRef path = NULL;
1211
1212	Username = CFDictionaryGetValue(mutableDict, kNetFSUserNameKey);
1213	share = CFDictionaryGetValue(mutableDict, kNetFSPathKey);
1214
1215	/* Everything we need is in the dictionary */
1216	if (share && Username)
1217		return;
1218
1219	/* Add the user name, if we have one */
1220	if (ctx->ct_setup.ioc_user[0]) {
1221		Username = CFStringCreateWithCString(NULL, ctx->ct_setup.ioc_user, kCFStringEncodingUTF8);
1222		if (ctx->ct_setup.ioc_domain[0])	/* They gave us a domain add it */
1223			DomainWrkgrp = CFStringCreateWithCString(NULL, ctx->ct_setup.ioc_domain, kCFStringEncodingUTF8);
1224
1225		/*
1226		 * We have a domain name so combined it with the user name. We now test
1227		 * to make sure we have a username. Having a domain without a username
1228		 * makes no sense, so don't return either.
1229		 */
1230		if (DomainWrkgrp && Username && CFStringGetLength(Username)) {
1231			CFMutableStringRef tempString = CFStringCreateMutableCopy(NULL, 0, DomainWrkgrp);
1232
1233			if (tempString) {
1234				CFStringAppend(tempString, CFSTR("\\"));
1235				CFStringAppend(tempString, Username);
1236				CFRelease(Username);
1237				Username = tempString;
1238			}
1239		}
1240		if (DomainWrkgrp) {
1241			CFRelease(DomainWrkgrp);
1242		}
1243		if (Username)
1244		{
1245			CFDictionarySetValue (mutableDict, kNetFSUserNameKey, Username);
1246			CFRelease(Username);
1247		}
1248	}
1249	/* if we have a share then we are done */
1250	if (share || !ctx->ct_origshare)
1251		return;
1252
1253	path = CFStringCreateMutable(NULL, 1024);
1254	/* Should never happen, but just to be safe */
1255	if (!path)
1256		return;
1257
1258	CFStringAppendCString(path, ctx->ct_origshare, kCFStringEncodingUTF8);
1259	/* Add the path if we have one */
1260	if (ctx->mountPath) {
1261		CFStringAppend(path, CFSTR("/"));
1262		CFStringAppend(path, ctx->mountPath);
1263	}
1264	CFDictionarySetValue (mutableDict, kNetFSPathKey, path);
1265	CFRelease(path);
1266}
1267
1268/*
1269 * We need to create the from name. The from name is just a URL without the scheme. We
1270 * never put the password in the from name, but if they have an empty password then we
1271 * need to make sure that it's included.
1272 *
1273 * Examples:
1274 *	URL "smb://username:@server/share" - Empty password, just remove the scheme.
1275 *	URL "smb://username:password@server/share" - Need to remove the password and the scheme.
1276 *	URL "smb://username@server" - Need to add the share and remove the scheme.
1277 *	URL "smb://server" - Need to add the username and share and remove the scheme.
1278 *	URL "smb://server/share/path" - Need to add the usernameand remove the scheme.
1279 */
1280void CreateSMBFromName(struct smb_ctx *ctx, char *fromname, int maxlen)
1281{
1282	CFMutableStringRef urlStringM = NULL;
1283	CFMutableStringRef newUrlStringM = NULL;
1284	CFMutableDictionaryRef mutableDict = NULL;
1285	CFStringRef Password = NULL;
1286	int SchemeLength = 0;
1287	int error = 0;;
1288
1289	/* Always start with the original url and a cleaned out the from name */
1290	bzero(fromname, maxlen);
1291	SchemeLength = SMBSchemeLength(ctx->ct_url);
1292	urlStringM = CFStringCreateMutableCopy(NULL, 0, CFURLGetString(ctx->ct_url));
1293	if (urlStringM == NULL) {
1294		smb_log_info("Failed creating URL string, syserr = %s", ASL_LEVEL_ERR, strerror(errno));
1295		return;
1296	}
1297
1298	error = smb_url_to_dictionary(ctx->ct_url, (CFDictionaryRef *)&mutableDict);
1299	if (error || (mutableDict == NULL)) {
1300		smb_log_info("Failed parsing URL, syserr = %s", ASL_LEVEL_DEBUG, strerror(error));
1301		goto WeAreDone;
1302	}
1303	UpdateDictionaryWithUserAndShare(ctx, mutableDict);
1304
1305	Password = CFDictionaryGetValue(mutableDict, kNetFSPasswordKey);
1306	/*
1307	 * If there is a password and its not an empty password then remove it. Never
1308	 * show the password in the mount from name.
1309	 */
1310	if (Password && (CFStringGetLength(Password) > 0)) {
1311		CFDictionaryRemoveValue(mutableDict, kNetFSPasswordKey);
1312	}
1313	/* Guest access has an empty password. */
1314	if (ctx->ct_setup.ioc_userflags & SMBV_GUEST_ACCESS)
1315		CFDictionarySetValue (mutableDict, kNetFSPasswordKey, CFSTR(""));
1316
1317	/*
1318	 * Recreate the URL from our new dictionary. The old code would not escape
1319	 * out the share/path, which can cause us issue. Now the from name can look
1320	 * pretty goofy, but we will always work with alias. Now this was originally
1321	 * done because carbon would use the last part of the mount from name as the
1322	 * volume name. We now return the volume name, so this is no longer an issue.
1323	 */
1324	error = smb_dictionary_to_urlstring(mutableDict, &newUrlStringM);
1325	if (error || (newUrlStringM == NULL)) {
1326		smb_log_info("Failed parsing dictionary, syserr = %s", ASL_LEVEL_DEBUG,
1327					 strerror(error));
1328		goto WeAreDone;
1329	}
1330	if (urlStringM)
1331		CFRelease(urlStringM);
1332	urlStringM = newUrlStringM;
1333	newUrlStringM = NULL;
1334	/* smb_dictionary_to_urlstring always uses the SMB scheme */
1335	SchemeLength = SMB_SCHEME_LEN;
1336	if (CFStringGetLength(urlStringM) < (maxlen+SchemeLength))
1337		goto WeAreDone;
1338
1339	/*
1340	 * At this point the URL is too big to fit in the mount from name. See if
1341	 * removing the username will make it fit.
1342	 */
1343	CFDictionaryRemoveValue(mutableDict, kNetFSUserNameKey);
1344	CFDictionaryRemoveValue(mutableDict, kNetFSPasswordKey);
1345
1346
1347	/*
1348	 * Recreate the URL from our new dictionary. The old code would not escape
1349	 * out the share/path, which can cause us issue. Now the from name can look
1350	 * pretty goofy, but we will always work with alias. Now this was originally
1351	 * done because carbon would use the last part of the mount from name as the
1352	 * volume name. We now return the volume name, so this is no longer an issue.
1353	 */
1354	error = smb_dictionary_to_urlstring(mutableDict, &newUrlStringM);
1355	if (error || (newUrlStringM == NULL)) {
1356		smb_log_info("Removing username failed parsing dictionary, syserr = %s",
1357					 ASL_LEVEL_DEBUG, strerror(error));
1358		goto WeAreDone;
1359	}
1360	if (urlStringM)
1361		CFRelease(urlStringM);
1362	urlStringM = newUrlStringM;
1363	newUrlStringM = NULL;
1364
1365WeAreDone:
1366	if (urlStringM && (SchemeLength > 0)) {
1367		/* Remove the scheme, start at the begining */
1368		CFRange range1 = CFRangeMake(0, SchemeLength);
1369		CFStringDelete(urlStringM, range1);
1370	}
1371	if (urlStringM)
1372		CFStringGetCString(urlStringM, fromname, maxlen, kCFStringEncodingUTF8);
1373	if (error)
1374		smb_log_info("Mount from name is %s, syserr = %s", ASL_LEVEL_ERR,
1375					 fromname, strerror(error));
1376	else
1377		smb_log_info("Mount from name is %s", ASL_LEVEL_DEBUG, fromname);
1378
1379	if (urlStringM)
1380		CFRelease(urlStringM);
1381	if (mutableDict)
1382		CFRelease(mutableDict);
1383}
1384
1385/*
1386 * See if this is a BTMM address
1387 */
1388int isBTMMAddress(CFStringRef serverNameRef)
1389{
1390    boolean_t	foundBTMM;
1391    CFStringRef btmmRef;
1392    CFStringRef serverNameQual;
1393    CFIndex serverNameLen, btmmCount, serverCount, btmmIndex, srvIndex;
1394    CFArrayRef btmmArrRef, serverArrRef;
1395    CFStringRef btmmTmpRef, serverTmpRef;
1396    CFComparisonResult res;
1397
1398    foundBTMM = TRUE;
1399    btmmRef = NULL;
1400    serverNameQual = NULL;
1401    btmmArrRef = NULL;
1402    serverArrRef = NULL;
1403
1404	if (serverNameRef == NULL) {
1405		smb_log_info("%s: serverNameRef is NULL!", ASL_LEVEL_DEBUG, __FUNCTION__);
1406		foundBTMM = FALSE;
1407        goto out;
1408	}
1409
1410    serverNameLen = CFStringGetLength(serverNameRef);
1411	if (serverNameLen == 0) {
1412		smb_log_info("%s: serverNameRef len is 0!", ASL_LEVEL_DEBUG, __FUNCTION__);
1413		foundBTMM = FALSE;
1414        goto out;
1415	}
1416
1417    /*  Create a copy of the server name, add a trailing '.' */
1418    /*  if it doesn't already have one. */
1419    if (CFStringGetCharacterAtIndex(serverNameRef, serverNameLen - 1) == (UniChar)'.') {
1420        serverNameQual = CFStringCreateCopy(kCFAllocatorDefault, serverNameRef);
1421    } else {
1422        serverNameQual = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@."), serverNameRef);
1423    }
1424
1425    if (serverNameQual == NULL) {
1426		foundBTMM = FALSE;
1427        goto out;
1428    }
1429
1430    /* Fetch BTMM domain from DynamicStore */
1431    btmmRef = _CSBackToMyMacCopyDomain();
1432    if (btmmRef == NULL) {
1433		foundBTMM = FALSE;
1434        goto out;
1435    }
1436
1437    /* Split them into component string arrays */
1438    btmmArrRef = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, btmmRef, CFSTR("."));
1439    btmmCount = CFArrayGetCount(btmmArrRef);
1440
1441    serverArrRef = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, serverNameQual, CFSTR("."));
1442    serverCount = CFArrayGetCount(serverArrRef);
1443
1444    if (btmmCount == 0 || serverCount == 0) {
1445		foundBTMM = FALSE;
1446        goto out;
1447    }
1448
1449    if (btmmCount > serverCount) {
1450        /* Not a BTMM domain */
1451		foundBTMM = FALSE;
1452        goto out;
1453    }
1454
1455    for (btmmIndex = btmmCount - 1, srvIndex = serverCount - 1; btmmIndex >= 0; btmmIndex--, srvIndex--) {
1456        btmmTmpRef = CFArrayGetValueAtIndex(btmmArrRef, btmmIndex);
1457        serverTmpRef = CFArrayGetValueAtIndex(serverArrRef, srvIndex);
1458
1459        res = CFStringCompare(btmmTmpRef, serverTmpRef, kCFCompareCaseInsensitive);
1460        if (res != kCFCompareEqualTo) {
1461            /* Not a BTMM domain */
1462            foundBTMM = FALSE;
1463            break;
1464        }
1465    }
1466
1467	if (foundBTMM == TRUE) {
1468        smb_log_info("%s: found a btmm address", ASL_LEVEL_DEBUG, __FUNCTION__);
1469	}
1470
1471out:
1472
1473    // Clean up mem
1474    if (btmmArrRef != NULL) {
1475        CFRelease(btmmArrRef);
1476    }
1477    if (serverArrRef != NULL) {
1478        CFRelease(serverArrRef);
1479    }
1480    if (btmmRef != NULL) {
1481        CFRelease(btmmRef);
1482    }
1483    if (serverNameQual !=NULL) {
1484        CFRelease(serverNameQual);
1485    }
1486
1487	return (foundBTMM);
1488}
1489