1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35#include <sys/param.h>
36#include <sys/errno.h>
37#include <sys/stat.h>
38#include <err.h>
39#include <stdio.h>
40#include <unistd.h>
41#include <strings.h>
42#include <stdlib.h>
43#include <sysexits.h>
44
45#include <smbclient/smbclient.h>
46#include <smbclient/ntstatus.h>
47
48#include "common.h"
49#include "netshareenum.h"
50
51/*
52 * Allocate a buffer and then use CFStringGetCString to copy the c-style string
53 * into the buffer. The calling routine needs to free the buffer when done.
54 */
55static char *CStringCreateWithCFString(CFStringRef inStr)
56{
57	CFIndex maxLen;
58	char *str;
59
60	if (inStr == NULL) {
61		return NULL;
62	}
63	maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(inStr),
64											   kCFStringEncodingUTF8) + 1;
65	str = malloc(maxLen);
66	if (!str) {
67		return NULL;
68	}
69	CFStringGetCString(inStr, str, maxLen, kCFStringEncodingUTF8);
70	return str;
71}
72
73/*
74 * Given a share dictionary create an array that contains the share entries in
75 * the dictionary.
76 */
77CFArrayRef createShareArrayFromShareDictionary(CFDictionaryRef shareDict)
78{
79	CFIndex count = 0;
80	CFArrayRef keyArray = NULL;
81
82    if (!shareDict)
83        return NULL;
84    count = CFDictionaryGetCount(shareDict);
85
86    void *shareKeys = CFAllocatorAllocate(kCFAllocatorDefault, count * sizeof(CFStringRef), 0);
87	if (shareKeys) {
88		CFDictionaryGetKeysAndValues(shareDict, (const void **)shareKeys, NULL);
89		keyArray = CFArrayCreate(kCFAllocatorDefault, (const void **)shareKeys,
90                                 count, &kCFTypeArrayCallBacks);
91		CFAllocatorDeallocate(kCFAllocatorDefault, shareKeys);
92	}
93
94	return keyArray;
95}
96
97int
98cmd_view(int argc, char *argv[])
99{
100	const char *url = NULL;
101	int			opt;
102	SMBHANDLE	serverConnection = NULL;
103	uint64_t	options = 0;
104	NTSTATUS	status;
105	int			error;
106	CFDictionaryRef shareDict= NULL;
107
108	while ((opt = getopt(argc, argv, "ANGgaf")) != EOF) {
109		switch(opt){
110			case 'A':
111				options |= kSMBOptionSessionOnly;
112				break;
113			case 'N':
114				options |= kSMBOptionNoPrompt;
115				break;
116			case 'G':
117				options |= kSMBOptionAllowGuestAuth;
118				break;
119			case 'g':
120				if (options & kSMBOptionOnlyAuthMask)
121					view_usage();
122				options |= kSMBOptionUseGuestOnlyAuth;
123				options |= kSMBOptionNoPrompt;
124				break;
125			case 'a':
126				if (options & kSMBOptionOnlyAuthMask)
127					view_usage();
128				options |= kSMBOptionUseAnonymousOnlyAuth;
129				options |= kSMBOptionNoPrompt;
130				break;
131			case 'f':
132				options |= kSMBOptionForceNewSession;
133				break;
134			default:
135				view_usage();
136				/*NOTREACHED*/
137		}
138	}
139
140	if (optind >= argc)
141		view_usage();
142	url = argv[optind];
143	argc -= optind;
144	/* One more check to make sure we have the correct number of arguments */
145	if (argc != 1)
146		view_usage();
147
148	status = SMBOpenServerEx(url, &serverConnection, options);
149	/*
150	 * SMBOpenServerEx now sets errno, so err will work correctly. We change
151	 * the string based on the NTSTATUS Error.
152	 */
153	if (!NT_SUCCESS(status)) {
154		/* This routine will exit the program */
155		ntstatus_to_err(status);
156	}
157	if (options  & kSMBOptionSessionOnly) {
158		fprintf(stdout, "Authenticate successfully with %s\n", url);
159		goto done;
160	}
161	fprintf(stdout, "%-48s%-8s%s\n", "Share", "Type", "Comments");
162	fprintf(stdout, "-------------------------------\n");
163
164	error = smb_netshareenum(serverConnection, &shareDict, FALSE);
165	if (error) {
166		errno = error;
167		SMBReleaseServer(serverConnection);
168		err(EX_IOERR, "unable to list resources");
169	} else {
170		CFArrayRef shareArray = createShareArrayFromShareDictionary(shareDict);
171		CFStringRef shareStr, shareTypeStr, commentStr;
172		CFDictionaryRef theDict;
173		CFIndex ii;
174		char *share, *sharetype, *comments;
175
176		for (ii=0; shareArray && (ii < CFArrayGetCount(shareArray)); ii++) {
177			shareStr = CFArrayGetValueAtIndex(shareArray, ii);
178			/* Should never happen, but just to be safe */
179			if (shareStr == NULL) {
180				continue;
181			}
182			theDict = CFDictionaryGetValue(shareDict, shareStr);
183			/* Should never happen, but just to be safe */
184			if (theDict == NULL) {
185				continue;
186			}
187			shareTypeStr = CFDictionaryGetValue(theDict, kNetShareTypeStrKey);
188			commentStr = CFDictionaryGetValue(theDict, kNetCommentStrKey);
189
190			share = CStringCreateWithCFString(shareStr);
191			sharetype = CStringCreateWithCFString(shareTypeStr);
192			comments = CStringCreateWithCFString(commentStr);
193			fprintf(stdout, "%-48s%-8s%s\n", share ? share : "",
194					sharetype ? sharetype : "", comments ? comments : "");
195			free(share);
196			free(sharetype);
197			free(comments);
198		}
199		if (shareArray) {
200			fprintf(stdout, "\n%ld shares listed\n", CFArrayGetCount(shareArray));
201			CFRelease(shareArray);
202		} else {
203			fprintf(stdout, "\n0 shares listed\n");
204		}
205		if (shareDict) {
206			CFRelease(shareDict);
207		}
208	}
209done:
210	SMBReleaseServer(serverConnection);
211	return 0;
212}
213
214
215void
216view_usage(void)
217{
218	fprintf(stderr, "usage: smbutil view [connection options] //"
219		"[domain;][user[:password]@]"
220	"server\n");
221
222	fprintf(stderr, "where options are:\n"
223					"    -A    authorize only\n"
224					"    -N    don't prompt for a password\n"
225					"    -G    allow guest access\n"
226					"    -g    authorize with guest only\n"
227					"    -a    authorize with anonymous only\n"
228					"    -f    don't share session\n");
229	exit(1);
230}