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, 2004
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 2 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25#include "includes.h"
26
27#include "include/libsmb_internal.h"
28
29
30/*
31 * DOS Attribute values (used internally)
32 */
33typedef struct DOS_ATTR_DESC
34{
35    int mode;
36    unsigned long long size;
37    time_t a_time;
38    time_t c_time;
39    time_t m_time;
40    unsigned long long inode;
41} DOS_ATTR_DESC;
42
43
44/*
45 * Internal flags for extended attributes
46 */
47
48/* internal mode values */
49#define SMBC_XATTR_MODE_ADD          1
50#define SMBC_XATTR_MODE_REMOVE       2
51#define SMBC_XATTR_MODE_REMOVE_ALL   3
52#define SMBC_XATTR_MODE_SET          4
53#define SMBC_XATTR_MODE_CHOWN        5
54#define SMBC_XATTR_MODE_CHGRP        6
55
56#define CREATE_ACCESS_READ      READ_CONTROL_ACCESS
57
58/*We should test for this in configure ... */
59#ifndef ENOTSUP
60#define ENOTSUP EOPNOTSUPP
61#endif
62
63/*
64 * Functions exported by libsmb_cache.c that we need here
65 */
66int smbc_default_cache_functions(SMBCCTX *context);
67
68/*
69 * check if an element is part of the list.
70 * FIXME: Does not belong here !
71 * Can anyone put this in a macro in dlinklist.h ?
72 * -- Tom
73 */
74static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
75	if (!p || !list) return False;
76	do {
77		if (p == list) return True;
78		list = list->next;
79	} while (list);
80	return False;
81}
82
83extern BOOL in_client;
84
85/*
86 * Is the logging working / configfile read ?
87 */
88static int smbc_initialized = 0;
89
90static int
91hex2int( unsigned int _char )
92{
93    if ( _char >= 'A' && _char <='F')
94	return _char - 'A' + 10;
95    if ( _char >= 'a' && _char <='f')
96	return _char - 'a' + 10;
97    if ( _char >= '0' && _char <='9')
98	return _char - '0';
99    return -1;
100}
101
102/*
103 * smbc_urldecode()
104 *
105 * Convert strings of %xx to their single character equivalent.  Each 'x' must
106 * be a valid hexadecimal digit, or that % sequence is left undecoded.
107 *
108 * dest may, but need not be, the same pointer as src.
109 *
110 * Returns the number of % sequences which could not be converted due to lack
111 * of two following hexadecimal digits.
112 */
113int
114smbc_urldecode(char *dest, char * src, size_t max_dest_len)
115{
116        int old_length = strlen(src);
117        int i = 0;
118        int err_count = 0;
119        pstring temp;
120        char * p;
121
122        if ( old_length == 0 ) {
123                return 0;
124        }
125
126        p = temp;
127        while ( i < old_length ) {
128                unsigned char character = src[ i++ ];
129
130                if (character == '%') {
131                        int a = i+1 < old_length ? hex2int( src[i] ) : -1;
132                        int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
133
134                        /* Replace valid sequence */
135                        if (a != -1 && b != -1) {
136
137                                /* Replace valid %xx sequence with %dd */
138                                character = (a * 16) + b;
139
140                                if (character == '\0') {
141                                        break; /* Stop at %00 */
142                                }
143
144                                i += 2;
145                        } else {
146
147                                err_count++;
148                        }
149                }
150
151                *p++ = character;
152        }
153
154        *p = '\0';
155
156        strncpy(dest, temp, max_dest_len);
157
158        return err_count;
159}
160
161/*
162 * smbc_urlencode()
163 *
164 * Convert any characters not specifically allowed in a URL into their %xx
165 * equivalent.
166 *
167 * Returns the remaining buffer length.
168 */
169int
170smbc_urlencode(char * dest, char * src, int max_dest_len)
171{
172        char hex[] = "0123456789ABCDEF";
173
174        for (; *src != '\0' && max_dest_len >= 3; src++) {
175
176                if ((*src < '0' &&
177                     *src != '-' &&
178                     *src != '.') ||
179                    (*src > '9' &&
180                     *src < 'A') ||
181                    (*src > 'Z' &&
182                     *src < 'a' &&
183                     *src != '_') ||
184                    (*src > 'z')) {
185                        *dest++ = '%';
186                        *dest++ = hex[(*src >> 4) & 0x0f];
187                        *dest++ = hex[*src & 0x0f];
188                        max_dest_len -= 3;
189                } else {
190                        *dest++ = *src;
191                        max_dest_len--;
192                }
193        }
194
195        *dest++ = '\0';
196        max_dest_len--;
197
198        return max_dest_len;
199}
200
201/*
202 * Function to parse a path and turn it into components
203 *
204 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
205 * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
206 * general format ("smb:" only; we do not look for "cifs:").
207 *
208 *
209 * We accept:
210 *  smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]][?options]
211 *
212 * Meaning of URLs:
213 *
214 * smb://           Show all workgroups.
215 *
216 *                  The method of locating the list of workgroups varies
217 *                  depending upon the setting of the context variable
218 *                  context->options.browse_max_lmb_count.  This value
219 *                  determine the maximum number of local master browsers to
220 *                  query for the list of workgroups.  In order to ensure that
221 *                  a complete list of workgroups is obtained, all master
222 *                  browsers must be queried, but if there are many
223 *                  workgroups, the time spent querying can begin to add up.
224 *                  For small networks (not many workgroups), it is suggested
225 *                  that this variable be set to 0, indicating query all local
226 *                  master browsers.  When the network has many workgroups, a
227 *                  reasonable setting for this variable might be around 3.
228 *
229 * smb://name/      if name<1D> or name<1B> exists, list servers in
230 *                  workgroup, else, if name<20> exists, list all shares
231 *                  for server ...
232 *
233 * If "options" are provided, this function returns the entire option list as a
234 * string, for later parsing by the caller.  Note that currently, no options
235 * are supported.
236 */
237
238static const char *smbc_prefix = "smb:";
239
240static int
241smbc_parse_path(SMBCCTX *context,
242                const char *fname,
243                char *server, int server_len,
244                char *share, int share_len,
245                char *path, int path_len,
246		char *user, int user_len,
247                char *password, int password_len,
248                char *options, int options_len)
249{
250	static pstring s;
251	pstring userinfo;
252	const char *p;
253	char *q, *r;
254	int len;
255
256	server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
257        if (options != NULL && options_len > 0) {
258                options[0] = (char)0;
259        }
260	pstrcpy(s, fname);
261
262	/* see if it has the right prefix */
263	len = strlen(smbc_prefix);
264	if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
265                return -1; /* What about no smb: ? */
266        }
267
268	p = s + len;
269
270	/* Watch the test below, we are testing to see if we should exit */
271
272	if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
273
274                DEBUG(1, ("Invalid path (does not begin with smb://"));
275		return -1;
276
277	}
278
279	p += 2;  /* Skip the double slash */
280
281        /* See if any options were specified */
282        if ((q = strrchr(p, '?')) != NULL ) {
283                /* There are options.  Null terminate here and point to them */
284                *q++ = '\0';
285
286                DEBUG(4, ("Found options '%s'", q));
287
288                /* Copy the options */
289                if (options != NULL && options_len > 0) {
290                        safe_strcpy(options, q, options_len - 1);
291                }
292        }
293
294	if (*p == (char)0)
295	    goto decoding;
296
297	if (*p == '/') {
298
299		strncpy(server, context->workgroup,
300			(strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
301		return 0;
302
303	}
304
305	/*
306	 * ok, its for us. Now parse out the server, share etc.
307	 *
308	 * However, we want to parse out [[domain;]user[:password]@] if it
309	 * exists ...
310	 */
311
312	/* check that '@' occurs before '/', if '/' exists at all */
313	q = strchr_m(p, '@');
314	r = strchr_m(p, '/');
315	if (q && (!r || q < r)) {
316		pstring username, passwd, domain;
317		const char *u = userinfo;
318
319		next_token(&p, userinfo, "@", sizeof(fstring));
320
321		username[0] = passwd[0] = domain[0] = 0;
322
323		if (strchr_m(u, ';')) {
324
325			next_token(&u, domain, ";", sizeof(fstring));
326
327		}
328
329		if (strchr_m(u, ':')) {
330
331			next_token(&u, username, ":", sizeof(fstring));
332
333			pstrcpy(passwd, u);
334
335		}
336		else {
337
338			pstrcpy(username, u);
339
340		}
341
342		if (username[0])
343			strncpy(user, username, user_len);  /* FIXME, domain */
344
345		if (passwd[0])
346			strncpy(password, passwd, password_len);
347
348	}
349
350	if (!next_token(&p, server, "/", sizeof(fstring))) {
351
352		return -1;
353
354	}
355
356	if (*p == (char)0) goto decoding;  /* That's it ... */
357
358	if (!next_token(&p, share, "/", sizeof(fstring))) {
359
360		return -1;
361
362	}
363
364        safe_strcpy(path, p, path_len - 1);
365
366	all_string_sub(path, "/", "\\", 0);
367
368 decoding:
369	(void) smbc_urldecode(path, path, path_len);
370	(void) smbc_urldecode(server, server, server_len);
371	(void) smbc_urldecode(share, share, share_len);
372	(void) smbc_urldecode(user, user, user_len);
373	(void) smbc_urldecode(password, password, password_len);
374
375	return 0;
376}
377
378/*
379 * Verify that the options specified in a URL are valid
380 */
381static int smbc_check_options(char *server, char *share, char *path, char *options)
382{
383        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' path='%s' options='%s'\n", server, share, path, options));
384
385        /* No options at all is always ok */
386        if (! *options) return 0;
387
388        /* Currently, we don't support any options. */
389        return -1;
390}
391
392/*
393 * Convert an SMB error into a UNIX error ...
394 */
395static int smbc_errno(SMBCCTX *context, struct cli_state *c)
396{
397	int ret = cli_errno(c);
398
399        if (cli_is_dos_error(c)) {
400                uint8 eclass;
401                uint32 ecode;
402
403                cli_dos_error(c, &eclass, &ecode);
404
405                DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
406                         (int)eclass, (int)ecode, (int)ecode, ret));
407        } else {
408                NTSTATUS status;
409
410                status = cli_nt_error(c);
411
412                DEBUG(3,("smbc errno %s -> %d\n",
413                         nt_errstr(status), ret));
414        }
415
416	return ret;
417}
418
419/*
420 * Check a server_fd.
421 * returns 0 if the server is in shape. Returns 1 on error
422 *
423 * Also useable outside libsmbclient to enable external cache
424 * to do some checks too.
425 */
426int smbc_check_server(SMBCCTX * context, SMBCSRV * server)
427{
428	if ( send_keepalive(server->cli.fd) == False )
429		return 1;
430
431	/* connection is ok */
432	return 0;
433}
434
435/*
436 * Remove a server from the cached server list it's unused.
437 * On success, 0 is returned. 1 is returned if the server could not be removed.
438 *
439 * Also useable outside libsmbclient
440 */
441int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
442{
443	SMBCFILE * file;
444
445	/* are we being fooled ? */
446	if (!context || !context->internal ||
447	    !context->internal->_initialized || !srv) return 1;
448
449
450	/* Check all open files/directories for a relation with this server */
451	for (file = context->internal->_files; file; file=file->next) {
452		if (file->srv == srv) {
453			/* Still used */
454			DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n",
455				  srv, file));
456			return 1;
457		}
458	}
459
460	DLIST_REMOVE(context->internal->_servers, srv);
461
462	cli_shutdown(&srv->cli);
463
464	DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
465
466	context->callbacks.remove_cached_srv_fn(context, srv);
467
468	return 0;
469}
470
471SMBCSRV *find_server(SMBCCTX *context,
472                     const char *server,
473                     const char *share,
474                     fstring workgroup,
475                     fstring username,
476                     fstring password)
477{
478        SMBCSRV *srv;
479        int auth_called = 0;
480
481 check_server_cache:
482
483	srv = context->callbacks.get_cached_srv_fn(context, server, share,
484						   workgroup, username);
485
486	if (!auth_called && !srv && (!username[0] || !password[0])) {
487		context->callbacks.auth_fn(server, share,
488                                           workgroup, sizeof(fstring),
489                                           username, sizeof(fstring),
490                                           password, sizeof(fstring));
491		/*
492                 * However, smbc_auth_fn may have picked up info relating to
493                 * an existing connection, so try for an existing connection
494                 * again ...
495                 */
496		auth_called = 1;
497		goto check_server_cache;
498
499	}
500
501	if (srv) {
502		if (context->callbacks.check_server_fn(context, srv)) {
503			/*
504                         * This server is no good anymore
505                         * Try to remove it and check for more possible
506                         * servers in the cache
507                         */
508			if (context->callbacks.remove_unused_server_fn(context,
509                                                                       srv)) {
510                                /*
511                                 * We could not remove the server completely,
512                                 * remove it from the cache so we will not get
513                                 * it again. It will be removed when the last
514                                 * file/dir is closed.
515                                 */
516				context->callbacks.remove_cached_srv_fn(context,
517                                                                        srv);
518			}
519
520			/*
521                         * Maybe there are more cached connections to this
522                         * server
523                         */
524			goto check_server_cache;
525		}
526
527		return srv;
528 	}
529
530        return NULL;
531}
532
533/*
534 * Connect to a server, possibly on an existing connection
535 *
536 * Here, what we want to do is: If the server and username
537 * match an existing connection, reuse that, otherwise, establish a
538 * new connection.
539 *
540 * If we have to create a new connection, call the auth_fn to get the
541 * info we need, unless the username and password were passed in.
542 */
543
544SMBCSRV *smbc_server(SMBCCTX *context,
545		     const char *server, const char *share,
546		     fstring workgroup, fstring username,
547		     fstring password)
548{
549	SMBCSRV *srv=NULL;
550	struct cli_state c;
551	struct nmb_name called, calling;
552	const char *server_n = server;
553	pstring ipenv;
554	struct in_addr ip;
555	int tried_reverse = 0;
556        int port_try_first;
557        int port_try_next;
558
559	zero_ip(&ip);
560	ZERO_STRUCT(c);
561
562	if (server[0] == 0) {
563		errno = EPERM;
564		return NULL;
565	}
566
567        srv = find_server(context, server, share,
568                          workgroup, username, password);
569
570        /*
571         * If we found a connection and we're only allowed one share per
572         * server...
573         */
574        if (srv && *share != '\0' && context->options.one_share_per_server) {
575
576                /*
577                 * ... then if there's no current connection to the share,
578                 * connect to it.  find_server(), or rather the function
579                 * pointed to by context->callbacks.get_cached_srv_fn which
580                 * was called by find_server(), will have issued a tree
581                 * disconnect if the requested share is not the same as the
582                 * one that was already connected.
583                 */
584                if (srv->cli.cnum == (uint16) -1) {
585                        /* Ensure we have accurate auth info */
586                        context->callbacks.auth_fn(server, share,
587                                                   workgroup, sizeof(fstring),
588                                                   username, sizeof(fstring),
589                                                   password, sizeof(fstring));
590
591                        if (! cli_send_tconX(&srv->cli, share, "?????",
592                                             password, strlen(password)+1)) {
593
594                                errno = smbc_errno(context, &srv->cli);
595                                cli_shutdown(&srv->cli);
596                                context->callbacks.remove_cached_srv_fn(context, srv);
597                                srv = NULL;
598                        }
599
600                        /* Regenerate the dev value since it's based on both server and share */
601                        if (srv) {
602                                srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
603                        }
604                }
605        }
606
607        /* If we have a connection... */
608        if (srv) {
609
610                /* ... then we're done here.  Give 'em what they came for. */
611                return srv;
612        }
613
614	make_nmb_name(&calling, context->netbios_name, 0x0);
615	make_nmb_name(&called , server, 0x20);
616
617	DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
618
619	DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
620
621 again:
622	slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
623
624	zero_ip(&ip);
625
626	/* have to open a new connection */
627	if (!cli_initialise(&c)) {
628		errno = ENOMEM;
629		return NULL;
630	}
631
632	if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
633		c.use_kerberos = True;
634	}
635	if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
636		c.fallback_after_kerberos = True;
637	}
638
639	c.timeout = context->timeout;
640
641        /*
642         * Force use of port 139 for first try if share is $IPC, empty, or
643         * null, so browse lists can work
644         */
645        if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0)
646        {
647                port_try_first = 139;
648                port_try_next = 445;
649        }
650        else
651        {
652                port_try_first = 445;
653                port_try_next = 139;
654        }
655
656        c.port = port_try_first;
657
658	if (!cli_connect(&c, server_n, &ip)) {
659
660                /* First connection attempt failed.  Try alternate port. */
661                c.port = port_try_next;
662
663                if (!cli_connect(&c, server_n, &ip)) {
664                        cli_shutdown(&c);
665                        errno = ETIMEDOUT;
666                        return NULL;
667                }
668 	}
669
670	if (!cli_session_request(&c, &calling, &called)) {
671		cli_shutdown(&c);
672		if (strcmp(called.name, "*SMBSERVER")) {
673			make_nmb_name(&called , "*SMBSERVER", 0x20);
674			goto again;
675		}
676		else {  /* Try one more time, but ensure we don't loop */
677
678		  /* Only try this if server is an IP address ... */
679
680		  if (is_ipaddress(server) && !tried_reverse) {
681		    fstring remote_name;
682		    struct in_addr rem_ip;
683
684		    if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
685		      DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
686		      errno = ETIMEDOUT;
687		      return NULL;
688		    }
689
690		    tried_reverse++; /* Yuck */
691
692		    if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
693		      make_nmb_name(&called, remote_name, 0x20);
694		      goto again;
695		    }
696
697
698		  }
699		}
700		errno = ETIMEDOUT;
701		return NULL;
702	}
703
704	DEBUG(4,(" session request ok\n"));
705
706	if (!cli_negprot(&c)) {
707		cli_shutdown(&c);
708		errno = ETIMEDOUT;
709		return NULL;
710	}
711
712	if (!cli_session_setup(&c, username,
713			       password, strlen(password),
714			       password, strlen(password),
715			       workgroup) &&
716			/* Try an anonymous login if it failed and this was allowed by flags. */
717			((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
718			!cli_session_setup(&c, "", "", 1,"", 0, workgroup))) {
719		cli_shutdown(&c);
720		errno = EPERM;
721		return NULL;
722	}
723
724	DEBUG(4,(" session setup ok\n"));
725
726	if (!cli_send_tconX(&c, share, "?????",
727			    password, strlen(password)+1)) {
728		errno = smbc_errno(context, &c);
729		cli_shutdown(&c);
730		return NULL;
731	}
732
733	DEBUG(4,(" tconx ok\n"));
734
735	/*
736	 * Ok, we have got a nice connection
737	 * Let's find a free server_fd
738	 */
739
740	srv = SMB_MALLOC_P(SMBCSRV);
741	if (!srv) {
742		errno = ENOMEM;
743		goto failed;
744	}
745
746	ZERO_STRUCTP(srv);
747	srv->cli = c;
748	srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
749
750	/* now add it to the cache (internal or external)  */
751	/* Let the cache function set errno if it wants to */
752	errno = 0;
753	if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
754		int saved_errno = errno;
755		DEBUG(3, (" Failed to add server to cache\n"));
756		errno = saved_errno;
757		if (errno == 0) {
758			errno = ENOMEM;
759		}
760		goto failed;
761	}
762
763	DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
764		  server, share, srv));
765
766	DLIST_ADD(context->internal->_servers, srv);
767	return srv;
768
769 failed:
770	cli_shutdown(&c);
771	if (!srv) return NULL;
772
773	SAFE_FREE(srv);
774	return NULL;
775}
776
777/*
778 * Connect to a server for getting/setting attributes, possibly on an existing
779 * connection.  This works similarly to smbc_server().
780 */
781SMBCSRV *smbc_attr_server(SMBCCTX *context,
782                          const char *server, const char *share,
783                          fstring workgroup,
784                          fstring username, fstring password,
785                          POLICY_HND *pol)
786{
787        struct in_addr ip;
788	struct cli_state *ipc_cli;
789        NTSTATUS nt_status;
790	SMBCSRV *ipc_srv=NULL;
791
792        /*
793         * See if we've already created this special connection.  Reference our
794         * "special" share name '*IPC$', which is an impossible real share name
795         * due to the leading asterisk.
796         */
797        ipc_srv = find_server(context, server, "*IPC$",
798                              workgroup, username, password);
799        if (!ipc_srv) {
800
801                /* We didn't find a cached connection.  Get the password */
802                if (*password == '\0') {
803                        /* ... then retrieve it now. */
804                        context->callbacks.auth_fn(server, share,
805                                                   workgroup, sizeof(fstring),
806                                                   username, sizeof(fstring),
807                                                   password, sizeof(fstring));
808                }
809
810                zero_ip(&ip);
811                nt_status = cli_full_connection(&ipc_cli,
812                                                global_myname(), server,
813                                                &ip, 0, "IPC$", "?????",
814                                                username, workgroup,
815                                                password, 0,
816                                                Undefined, NULL);
817                if (! NT_STATUS_IS_OK(nt_status)) {
818                        DEBUG(1,("cli_full_connection failed! (%s)\n",
819                                 nt_errstr(nt_status)));
820                        errno = ENOTSUP;
821                        return NULL;
822                }
823
824                if (!cli_nt_session_open(ipc_cli, PI_LSARPC)) {
825                        DEBUG(1, ("cli_nt_session_open fail!\n"));
826                        errno = ENOTSUP;
827                        cli_shutdown(ipc_cli);
828                        return NULL;
829                }
830
831                /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED,
832                   but NT sends 0x2000000 so we might as well do it too. */
833
834                nt_status = cli_lsa_open_policy(ipc_cli,
835                                                ipc_cli->mem_ctx,
836                                                True,
837                                                GENERIC_EXECUTE_ACCESS,
838                                                pol);
839
840                if (!NT_STATUS_IS_OK(nt_status)) {
841                        errno = smbc_errno(context, ipc_cli);
842                        cli_shutdown(ipc_cli);
843                        return NULL;
844                }
845
846                ipc_srv = SMB_MALLOC_P(SMBCSRV);
847                if (!ipc_srv) {
848                        errno = ENOMEM;
849                        cli_shutdown(ipc_cli);
850                        return NULL;
851                }
852
853                ZERO_STRUCTP(ipc_srv);
854                ipc_srv->cli = *ipc_cli;
855
856                free(ipc_cli);
857
858                /* now add it to the cache (internal or external) */
859
860                errno = 0;      /* let cache function set errno if it likes */
861                if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
862                                                         server,
863                                                         "*IPC$",
864                                                         workgroup,
865                                                         username)) {
866                        DEBUG(3, (" Failed to add server to cache\n"));
867                        if (errno == 0) {
868                                errno = ENOMEM;
869                        }
870                        cli_shutdown(&ipc_srv->cli);
871                        free(ipc_srv);
872                        return NULL;
873                }
874
875                DLIST_ADD(context->internal->_servers, ipc_srv);
876        }
877
878        return ipc_srv;
879}
880
881/*
882 * Routine to open() a file ...
883 */
884
885static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
886{
887	fstring server, share, user, password, workgroup;
888	pstring path;
889	SMBCSRV *srv   = NULL;
890	SMBCFILE *file = NULL;
891	int fd;
892
893	if (!context || !context->internal ||
894	    !context->internal->_initialized) {
895
896		errno = EINVAL;  /* Best I can think of ... */
897		return NULL;
898
899	}
900
901	if (!fname) {
902
903		errno = EINVAL;
904		return NULL;
905
906	}
907
908	if (smbc_parse_path(context, fname,
909                            server, sizeof(server),
910                            share, sizeof(share),
911                            path, sizeof(path),
912                            user, sizeof(user),
913                            password, sizeof(password),
914                            NULL, 0)) {
915                errno = EINVAL;
916                return NULL;
917        }
918
919	if (user[0] == (char)0) fstrcpy(user, context->user);
920
921	fstrcpy(workgroup, context->workgroup);
922
923	srv = smbc_server(context, server, share, workgroup, user, password);
924
925	if (!srv) {
926
927		if (errno == EPERM) errno = EACCES;
928		return NULL;  /* smbc_server sets errno */
929
930	}
931
932	/* Hmmm, the test for a directory is suspect here ... FIXME */
933
934	if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
935
936		fd = -1;
937
938	}
939	else {
940
941		file = SMB_MALLOC_P(SMBCFILE);
942
943		if (!file) {
944
945			errno = ENOMEM;
946			return NULL;
947
948		}
949
950		ZERO_STRUCTP(file);
951
952		if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
953
954			/* Handle the error ... */
955
956			SAFE_FREE(file);
957			errno = smbc_errno(context, &srv->cli);
958			return NULL;
959
960		}
961
962		/* Fill in file struct */
963
964		file->cli_fd  = fd;
965		file->fname   = SMB_STRDUP(fname);
966		file->srv     = srv;
967		file->offset  = 0;
968		file->file    = True;
969
970		DLIST_ADD(context->internal->_files, file);
971		return file;
972
973	}
974
975	/* Check if opendir needed ... */
976
977	if (fd == -1) {
978		int eno = 0;
979
980		eno = smbc_errno(context, &srv->cli);
981		file = context->opendir(context, fname);
982		if (!file) errno = eno;
983		return file;
984
985	}
986
987	errno = EINVAL; /* FIXME, correct errno ? */
988	return NULL;
989
990}
991
992/*
993 * Routine to create a file
994 */
995
996static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
997
998static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
999{
1000
1001	if (!context || !context->internal ||
1002	    !context->internal->_initialized) {
1003
1004		errno = EINVAL;
1005		return NULL;
1006
1007	}
1008
1009	return smbc_open_ctx(context, path, creat_bits, mode);
1010}
1011
1012/*
1013 * Routine to read() a file ...
1014 */
1015
1016static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
1017{
1018	int ret;
1019
1020	if (!context || !context->internal ||
1021	    !context->internal->_initialized) {
1022
1023		errno = EINVAL;
1024		return -1;
1025
1026	}
1027
1028	DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1029
1030	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1031
1032		errno = EBADF;
1033		return -1;
1034
1035	}
1036
1037	/* Check that the buffer exists ... */
1038
1039	if (buf == NULL) {
1040
1041		errno = EINVAL;
1042		return -1;
1043
1044	}
1045
1046	ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
1047
1048	if (ret < 0) {
1049
1050		errno = smbc_errno(context, &file->srv->cli);
1051		return -1;
1052
1053	}
1054
1055	file->offset += ret;
1056
1057	DEBUG(4, ("  --> %d\n", ret));
1058
1059	return ret;  /* Success, ret bytes of data ... */
1060
1061}
1062
1063/*
1064 * Routine to write() a file ...
1065 */
1066
1067static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
1068{
1069	int ret;
1070
1071	if (!context || !context->internal ||
1072	    !context->internal->_initialized) {
1073
1074		errno = EINVAL;
1075		return -1;
1076
1077	}
1078
1079	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1080
1081		errno = EBADF;
1082		return -1;
1083
1084	}
1085
1086	/* Check that the buffer exists ... */
1087
1088	if (buf == NULL) {
1089
1090		errno = EINVAL;
1091		return -1;
1092
1093	}
1094
1095	ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
1096
1097	if (ret <= 0) {
1098
1099		errno = smbc_errno(context, &file->srv->cli);
1100		return -1;
1101
1102	}
1103
1104	file->offset += ret;
1105
1106	return ret;  /* Success, 0 bytes of data ... */
1107}
1108
1109/*
1110 * Routine to close() a file ...
1111 */
1112
1113static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
1114{
1115        SMBCSRV *srv;
1116
1117	if (!context || !context->internal ||
1118	    !context->internal->_initialized) {
1119
1120		errno = EINVAL;
1121		return -1;
1122
1123	}
1124
1125	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1126
1127		errno = EBADF;
1128		return -1;
1129
1130	}
1131
1132	/* IS a dir ... */
1133	if (!file->file) {
1134
1135		return context->closedir(context, file);
1136
1137	}
1138
1139	if (!cli_close(&file->srv->cli, file->cli_fd)) {
1140
1141		DEBUG(3, ("cli_close failed on %s. purging server.\n",
1142			  file->fname));
1143		/* Deallocate slot and remove the server
1144		 * from the server cache if unused */
1145		errno = smbc_errno(context, &file->srv->cli);
1146		srv = file->srv;
1147		DLIST_REMOVE(context->internal->_files, file);
1148		SAFE_FREE(file->fname);
1149		SAFE_FREE(file);
1150		context->callbacks.remove_unused_server_fn(context, srv);
1151
1152		return -1;
1153
1154	}
1155
1156	DLIST_REMOVE(context->internal->_files, file);
1157	SAFE_FREE(file->fname);
1158	SAFE_FREE(file);
1159
1160	return 0;
1161}
1162
1163/*
1164 * Get info from an SMB server on a file. Use a qpathinfo call first
1165 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1166 */
1167static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1168		 uint16 *mode, size_t *size,
1169		 time_t *c_time, time_t *a_time, time_t *m_time,
1170		 SMB_INO_T *ino)
1171{
1172
1173	if (!context || !context->internal ||
1174	    !context->internal->_initialized) {
1175
1176		errno = EINVAL;
1177 		return -1;
1178
1179 	}
1180
1181	DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1182
1183	if (!srv->no_pathinfo2 &&
1184	    cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
1185			   size, mode, ino)) return True;
1186
1187	/* if this is NT then don't bother with the getatr */
1188	if (srv->cli.capabilities & CAP_NT_SMBS) {
1189                errno = EPERM;
1190                return False;
1191        }
1192
1193	if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
1194		a_time = c_time = m_time;
1195		srv->no_pathinfo2 = True;
1196		return True;
1197	}
1198
1199        errno = EPERM;
1200	return False;
1201
1202}
1203
1204/*
1205 * Routine to unlink() a file
1206 */
1207
1208static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
1209{
1210	fstring server, share, user, password, workgroup;
1211	pstring path;
1212	SMBCSRV *srv = NULL;
1213
1214	if (!context || !context->internal ||
1215	    !context->internal->_initialized) {
1216
1217		errno = EINVAL;  /* Best I can think of ... */
1218		return -1;
1219
1220	}
1221
1222	if (!fname) {
1223
1224		errno = EINVAL;
1225		return -1;
1226
1227	}
1228
1229	if (smbc_parse_path(context, fname,
1230                            server, sizeof(server),
1231                            share, sizeof(share),
1232                            path, sizeof(path),
1233                            user, sizeof(user),
1234                            password, sizeof(password),
1235                            NULL, 0)) {
1236                errno = EINVAL;
1237                return -1;
1238        }
1239
1240	if (user[0] == (char)0) fstrcpy(user, context->user);
1241
1242	fstrcpy(workgroup, context->workgroup);
1243
1244	srv = smbc_server(context, server, share, workgroup, user, password);
1245
1246	if (!srv) {
1247
1248		return -1;  /* smbc_server sets errno */
1249
1250	}
1251
1252	/*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1253
1254    int job = smbc_stat_printjob(srv, path, NULL, NULL);
1255    if (job == -1) {
1256
1257      return -1;
1258
1259    }
1260    if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
1261
1262
1263      return -1;
1264
1265    }
1266    } else */
1267
1268	if (!cli_unlink(&srv->cli, path)) {
1269
1270		errno = smbc_errno(context, &srv->cli);
1271
1272		if (errno == EACCES) { /* Check if the file is a directory */
1273
1274			int saverr = errno;
1275			size_t size = 0;
1276			uint16 mode = 0;
1277			time_t m_time = 0, a_time = 0, c_time = 0;
1278			SMB_INO_T ino = 0;
1279
1280			if (!smbc_getatr(context, srv, path, &mode, &size,
1281					 &c_time, &a_time, &m_time, &ino)) {
1282
1283				/* Hmmm, bad error ... What? */
1284
1285				errno = smbc_errno(context, &srv->cli);
1286				return -1;
1287
1288			}
1289			else {
1290
1291				if (IS_DOS_DIR(mode))
1292					errno = EISDIR;
1293				else
1294					errno = saverr;  /* Restore this */
1295
1296			}
1297		}
1298
1299		return -1;
1300
1301	}
1302
1303	return 0;  /* Success ... */
1304
1305}
1306
1307/*
1308 * Routine to rename() a file
1309 */
1310
1311static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
1312			   SMBCCTX *ncontext, const char *nname)
1313{
1314	fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
1315	pstring path1, path2;
1316	SMBCSRV *srv = NULL;
1317
1318	if (!ocontext || !ncontext ||
1319	    !ocontext->internal || !ncontext->internal ||
1320	    !ocontext->internal->_initialized ||
1321	    !ncontext->internal->_initialized) {
1322
1323		errno = EINVAL;  /* Best I can think of ... */
1324		return -1;
1325
1326	}
1327
1328	if (!oname || !nname) {
1329
1330		errno = EINVAL;
1331		return -1;
1332
1333	}
1334
1335	DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1336
1337	smbc_parse_path(ocontext, oname,
1338                        server1, sizeof(server1),
1339                        share1, sizeof(share1),
1340                        path1, sizeof(path1),
1341                        user1, sizeof(user1),
1342                        password1, sizeof(password1),
1343                        NULL, 0);
1344
1345	if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1346
1347	smbc_parse_path(ncontext, nname,
1348                        server2, sizeof(server2),
1349                        share2, sizeof(share2),
1350                        path2, sizeof(path2),
1351                        user2, sizeof(user2),
1352                        password2, sizeof(password2),
1353                        NULL, 0);
1354
1355	if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1356
1357	if (strcmp(server1, server2) || strcmp(share1, share2) ||
1358	    strcmp(user1, user2)) {
1359
1360		/* Can't rename across file systems, or users?? */
1361
1362		errno = EXDEV;
1363		return -1;
1364
1365	}
1366
1367	fstrcpy(workgroup, ocontext->workgroup);
1368	/* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */
1369	srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
1370	if (!srv) {
1371
1372		return -1;
1373
1374	}
1375
1376	if (!cli_rename(&srv->cli, path1, path2)) {
1377		int eno = smbc_errno(ocontext, &srv->cli);
1378
1379		if (eno != EEXIST ||
1380		    !cli_unlink(&srv->cli, path2) ||
1381		    !cli_rename(&srv->cli, path1, path2)) {
1382
1383			errno = eno;
1384			return -1;
1385
1386		}
1387	}
1388
1389	return 0; /* Success */
1390
1391}
1392
1393/*
1394 * A routine to lseek() a file
1395 */
1396
1397static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
1398{
1399	size_t size;
1400
1401	if (!context || !context->internal ||
1402	    !context->internal->_initialized) {
1403
1404		errno = EINVAL;
1405		return -1;
1406
1407	}
1408
1409	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1410
1411		errno = EBADF;
1412		return -1;
1413
1414	}
1415
1416	if (!file->file) {
1417
1418		errno = EINVAL;
1419		return -1;      /* Can't lseek a dir ... */
1420
1421	}
1422
1423	switch (whence) {
1424	case SEEK_SET:
1425		file->offset = offset;
1426		break;
1427
1428	case SEEK_CUR:
1429		file->offset += offset;
1430		break;
1431
1432	case SEEK_END:
1433		if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
1434				   NULL, NULL, NULL))
1435		{
1436		    SMB_BIG_UINT b_size = size;
1437		    if (!cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &b_size, NULL, NULL,
1438				      NULL))
1439		    {
1440			errno = EINVAL;
1441			return -1;
1442		    } else
1443			size = b_size;
1444		}
1445		file->offset = size + offset;
1446		break;
1447
1448	default:
1449		errno = EINVAL;
1450		break;
1451
1452	}
1453
1454	return file->offset;
1455
1456}
1457
1458/*
1459 * Generate an inode number from file name for those things that need it
1460 */
1461
1462static
1463ino_t smbc_inode(SMBCCTX *context, const char *name)
1464{
1465
1466	if (!context || !context->internal ||
1467	    !context->internal->_initialized) {
1468
1469		errno = EINVAL;
1470		return -1;
1471
1472	}
1473
1474	if (!*name) return 2; /* FIXME, why 2 ??? */
1475	return (ino_t)str_checksum(name);
1476
1477}
1478
1479/*
1480 * Routine to put basic stat info into a stat structure ... Used by stat and
1481 * fstat below.
1482 */
1483
1484static
1485int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
1486{
1487
1488	st->st_mode = 0;
1489
1490	if (IS_DOS_DIR(mode)) {
1491		st->st_mode = SMBC_DIR_MODE;
1492	} else {
1493		st->st_mode = SMBC_FILE_MODE;
1494	}
1495
1496	if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
1497	if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
1498	if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
1499	if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
1500
1501	st->st_size = size;
1502#ifdef HAVE_STAT_ST_BLKSIZE
1503	st->st_blksize = 512;
1504#endif
1505#ifdef HAVE_STAT_ST_BLOCKS
1506	st->st_blocks = (size+511)/512;
1507#endif
1508	st->st_uid = getuid();
1509	st->st_gid = getgid();
1510
1511	if (IS_DOS_DIR(mode)) {
1512		st->st_nlink = 2;
1513	} else {
1514		st->st_nlink = 1;
1515	}
1516
1517	if (st->st_ino == 0) {
1518		st->st_ino = smbc_inode(context, fname);
1519	}
1520
1521	return True;  /* FIXME: Is this needed ? */
1522
1523}
1524
1525/*
1526 * Routine to stat a file given a name
1527 */
1528
1529static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
1530{
1531	SMBCSRV *srv;
1532	fstring server, share, user, password, workgroup;
1533	pstring path;
1534	time_t m_time = 0, a_time = 0, c_time = 0;
1535	size_t size = 0;
1536	uint16 mode = 0;
1537	SMB_INO_T ino = 0;
1538
1539	if (!context || !context->internal ||
1540	    !context->internal->_initialized) {
1541
1542		errno = EINVAL;  /* Best I can think of ... */
1543		return -1;
1544
1545	}
1546
1547	if (!fname) {
1548
1549		errno = EINVAL;
1550		return -1;
1551
1552	}
1553
1554	DEBUG(4, ("smbc_stat(%s)\n", fname));
1555
1556	if (smbc_parse_path(context, fname,
1557                            server, sizeof(server),
1558                            share, sizeof(share),
1559                            path, sizeof(path),
1560                            user, sizeof(user),
1561                            password, sizeof(password),
1562                            NULL, 0)) {
1563                errno = EINVAL;
1564                return -1;
1565        }
1566
1567	if (user[0] == (char)0) fstrcpy(user, context->user);
1568
1569	fstrcpy(workgroup, context->workgroup);
1570
1571	srv = smbc_server(context, server, share, workgroup, user, password);
1572
1573	if (!srv) {
1574		return -1;  /* errno set by smbc_server */
1575	}
1576
1577	if (!smbc_getatr(context, srv, path, &mode, &size,
1578			 &c_time, &a_time, &m_time, &ino)) {
1579
1580		errno = smbc_errno(context, &srv->cli);
1581		return -1;
1582
1583	}
1584
1585	st->st_ino = ino;
1586
1587	smbc_setup_stat(context, st, path, size, mode);
1588
1589	st->st_atime = a_time;
1590	st->st_ctime = c_time;
1591	st->st_mtime = m_time;
1592	st->st_dev   = srv->dev;
1593
1594	return 0;
1595
1596}
1597
1598/*
1599 * Routine to stat a file given an fd
1600 */
1601
1602static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
1603{
1604	time_t c_time, a_time, m_time;
1605	size_t size;
1606	uint16 mode;
1607	SMB_INO_T ino = 0;
1608
1609	if (!context || !context->internal ||
1610	    !context->internal->_initialized) {
1611
1612		errno = EINVAL;
1613		return -1;
1614
1615	}
1616
1617	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1618
1619		errno = EBADF;
1620		return -1;
1621
1622	}
1623
1624	if (!file->file) {
1625
1626		return context->fstatdir(context, file, st);
1627
1628	}
1629
1630	if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
1631			   &mode, &size, &c_time, &a_time, &m_time, NULL, &ino)) {
1632	    SMB_BIG_UINT b_size = size;
1633	    if (!cli_getattrE(&file->srv->cli, file->cli_fd,
1634			  &mode, &b_size, &c_time, &a_time, &m_time)) {
1635
1636		errno = EINVAL;
1637		return -1;
1638	    } else
1639		size = b_size;
1640
1641	}
1642
1643	st->st_ino = ino;
1644
1645	smbc_setup_stat(context, st, file->fname, size, mode);
1646
1647	st->st_atime = a_time;
1648	st->st_ctime = c_time;
1649	st->st_mtime = m_time;
1650	st->st_dev = file->srv->dev;
1651
1652	return 0;
1653
1654}
1655
1656/*
1657 * Routine to open a directory
1658 * We accept the URL syntax explained in smbc_parse_path(), above.
1659 */
1660
1661static void smbc_remove_dir(SMBCFILE *dir)
1662{
1663	struct smbc_dir_list *d,*f;
1664
1665	d = dir->dir_list;
1666	while (d) {
1667
1668		f = d; d = d->next;
1669
1670		SAFE_FREE(f->dirent);
1671		SAFE_FREE(f);
1672
1673	}
1674
1675	dir->dir_list = dir->dir_end = dir->dir_next = NULL;
1676
1677}
1678
1679static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
1680{
1681	struct smbc_dirent *dirent;
1682	int size;
1683        int name_length = (name == NULL ? 0 : strlen(name));
1684        int comment_len = (comment == NULL ? 0 : strlen(comment));
1685
1686	/*
1687	 * Allocate space for the dirent, which must be increased by the
1688	 * size of the name and the comment and 1 each for the null terminator.
1689	 */
1690
1691	size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
1692
1693	dirent = SMB_MALLOC(size);
1694
1695	if (!dirent) {
1696
1697		dir->dir_error = ENOMEM;
1698		return -1;
1699
1700	}
1701
1702	ZERO_STRUCTP(dirent);
1703
1704	if (dir->dir_list == NULL) {
1705
1706		dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
1707		if (!dir->dir_list) {
1708
1709			SAFE_FREE(dirent);
1710			dir->dir_error = ENOMEM;
1711			return -1;
1712
1713		}
1714		ZERO_STRUCTP(dir->dir_list);
1715
1716		dir->dir_end = dir->dir_next = dir->dir_list;
1717	}
1718	else {
1719
1720		dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
1721
1722		if (!dir->dir_end->next) {
1723
1724			SAFE_FREE(dirent);
1725			dir->dir_error = ENOMEM;
1726			return -1;
1727
1728		}
1729		ZERO_STRUCTP(dir->dir_end->next);
1730
1731		dir->dir_end = dir->dir_end->next;
1732	}
1733
1734	dir->dir_end->next = NULL;
1735	dir->dir_end->dirent = dirent;
1736
1737	dirent->smbc_type = type;
1738	dirent->namelen = name_length;
1739	dirent->commentlen = comment_len;
1740	dirent->dirlen = size;
1741
1742	strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
1743
1744	dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
1745	strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
1746
1747	return 0;
1748
1749}
1750
1751static void
1752list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *state)
1753{
1754	SMBCFILE *dir = (SMBCFILE *)state;
1755        struct smbc_dir_list *dir_list;
1756        struct smbc_dirent *dirent;
1757	int dirent_type;
1758        int do_remove = 0;
1759
1760	dirent_type = dir->dir_type;
1761
1762	if (add_dirent(dir, name, comment, dirent_type) < 0) {
1763
1764		/* An error occurred, what do we do? */
1765		/* FIXME: Add some code here */
1766	}
1767
1768        /* Point to the one just added */
1769        dirent = dir->dir_end->dirent;
1770
1771        /* See if this was a duplicate */
1772        for (dir_list = dir->dir_list;
1773             dir_list != dir->dir_end;
1774             dir_list = dir_list->next) {
1775                if (! do_remove &&
1776                    strcmp(dir_list->dirent->name, dirent->name) == 0) {
1777                        /* Duplicate.  End end of list need to be removed. */
1778                        do_remove = 1;
1779                }
1780
1781                if (do_remove && dir_list->next == dir->dir_end) {
1782                        /* Found the end of the list.  Remove it. */
1783                        dir->dir_end = dir_list;
1784                        free(dir_list->next);
1785                        dir_list->next = NULL;
1786                        break;
1787                }
1788        }
1789}
1790
1791static void
1792list_fn(const char *name, uint32 type, const char *comment, void *state)
1793{
1794	SMBCFILE *dir = (SMBCFILE *)state;
1795	int dirent_type;
1796
1797	/* We need to process the type a little ... */
1798
1799	if (dir->dir_type == SMBC_FILE_SHARE) {
1800
1801		switch (type) {
1802		case 0: /* Directory tree */
1803			dirent_type = SMBC_FILE_SHARE;
1804			break;
1805
1806		case 1:
1807			dirent_type = SMBC_PRINTER_SHARE;
1808			break;
1809
1810		case 2:
1811			dirent_type = SMBC_COMMS_SHARE;
1812			break;
1813
1814		case 3:
1815			dirent_type = SMBC_IPC_SHARE;
1816			break;
1817
1818		default:
1819			dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
1820			break;
1821		}
1822	}
1823	else dirent_type = dir->dir_type;
1824
1825	if (add_dirent(dir, name, comment, dirent_type) < 0) {
1826
1827		/* An error occurred, what do we do? */
1828		/* FIXME: Add some code here */
1829
1830	}
1831}
1832
1833static void
1834dir_list_fn(const char *mnt, file_info *finfo, const char *mask, void *state)
1835{
1836
1837	if (add_dirent((SMBCFILE *)state, finfo->name, "",
1838		       (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
1839
1840		/* Handle an error ... */
1841
1842		/* FIXME: Add some code ... */
1843
1844	}
1845
1846}
1847
1848static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
1849{
1850	fstring server, share, user, password, options;
1851	pstring workgroup;
1852	pstring path;
1853	SMBCSRV *srv  = NULL;
1854	SMBCFILE *dir = NULL;
1855	struct in_addr rem_ip;
1856
1857	if (!context || !context->internal ||
1858	    !context->internal->_initialized) {
1859	        DEBUG(4, ("no valid context\n"));
1860		errno = EINVAL + 8192;
1861		return NULL;
1862
1863	}
1864
1865	if (!fname) {
1866		DEBUG(4, ("no valid fname\n"));
1867		errno = EINVAL + 8193;
1868		return NULL;
1869	}
1870
1871	if (smbc_parse_path(context, fname,
1872                            server, sizeof(server),
1873                            share, sizeof(share),
1874                            path, sizeof(path),
1875                            user, sizeof(user),
1876                            password, sizeof(password),
1877                            options, sizeof(options))) {
1878	        DEBUG(4, ("no valid path\n"));
1879		errno = EINVAL + 8194;
1880		return NULL;
1881	}
1882
1883	DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s' options='%s'\n", fname, server, share, path, options));
1884
1885        /* Ensure the options are valid */
1886        if (smbc_check_options(server, share, path, options)) {
1887                DEBUG(4, ("unacceptable options (%s)\n", options));
1888                errno = EINVAL + 8195;
1889                return NULL;
1890        }
1891
1892	if (user[0] == (char)0) fstrcpy(user, context->user);
1893
1894	pstrcpy(workgroup, context->workgroup);
1895
1896	dir = SMB_MALLOC_P(SMBCFILE);
1897
1898	if (!dir) {
1899
1900		errno = ENOMEM;
1901		return NULL;
1902
1903	}
1904
1905	ZERO_STRUCTP(dir);
1906
1907	dir->cli_fd   = 0;
1908	dir->fname    = SMB_STRDUP(fname);
1909	dir->srv      = NULL;
1910	dir->offset   = 0;
1911	dir->file     = False;
1912	dir->dir_list = dir->dir_next = dir->dir_end = NULL;
1913
1914	if (server[0] == (char)0) {
1915
1916                int i;
1917                int count;
1918                int max_lmb_count;
1919                struct ip_service *ip_list;
1920                struct ip_service server_addr;
1921                struct user_auth_info u_info;
1922                struct cli_state *cli;
1923
1924		if (share[0] != (char)0 || path[0] != (char)0) {
1925
1926			errno = EINVAL + 8196;
1927			if (dir) {
1928				SAFE_FREE(dir->fname);
1929				SAFE_FREE(dir);
1930			}
1931			return NULL;
1932		}
1933
1934                /* Determine how many local master browsers to query */
1935                max_lmb_count = (context->options.browse_max_lmb_count == 0
1936                                 ? INT_MAX
1937                                 : context->options.browse_max_lmb_count);
1938
1939                pstrcpy(u_info.username, user);
1940                pstrcpy(u_info.password, password);
1941
1942		/*
1943                 * We have server and share and path empty but options
1944                 * requesting that we scan all master browsers for their list
1945                 * of workgroups/domains.  This implies that we must first try
1946                 * broadcast queries to find all master browsers, and if that
1947                 * doesn't work, then try our other methods which return only
1948                 * a single master browser.
1949                 */
1950
1951                if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
1952                        if (!find_master_ip(workgroup, &server_addr.ip)) {
1953
1954                                errno = ENOENT;
1955                                return NULL;
1956                        }
1957
1958                        ip_list = &server_addr;
1959                        count = 1;
1960                }
1961
1962                for (i = 0; i < count && i < max_lmb_count; i++) {
1963                        DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
1964
1965                        cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info);
1966			/* cli == NULL is the master browser refused to talk or
1967			   could not be found */
1968			if ( !cli )
1969				continue;
1970
1971                        fstrcpy(server, cli->desthost);
1972                        cli_shutdown(cli);
1973
1974                        DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
1975
1976                        /*
1977                         * For each returned master browser IP address, get a
1978                         * connection to IPC$ on the server if we do not
1979                         * already have one, and determine the
1980                         * workgroups/domains that it knows about.
1981                         */
1982
1983                        srv = smbc_server(context, server,
1984                                          "IPC$", workgroup, user, password);
1985                        if (!srv) {
1986
1987                                if (dir) {
1988                                        SAFE_FREE(dir->fname);
1989                                        SAFE_FREE(dir);
1990                                }
1991                                return NULL;
1992                        }
1993
1994                        dir->srv = srv;
1995                        dir->dir_type = SMBC_WORKGROUP;
1996
1997                        /* Now, list the stuff ... */
1998
1999                        if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_unique_wg_fn,
2000                                               (void *)dir)) {
2001
2002                                if (dir) {
2003                                        SAFE_FREE(dir->fname);
2004                                        SAFE_FREE(dir);
2005                                }
2006
2007                                return NULL;
2008
2009                        }
2010                }
2011        } else {
2012                /*
2013                 * Server not an empty string ... Check the rest and see what
2014                 * gives
2015                 */
2016		if (share[0] == (char)0) {
2017
2018			if (path[0] != (char)0) { /* Should not have empty share with path */
2019
2020				errno = EINVAL + 8197;
2021				if (dir) {
2022					SAFE_FREE(dir->fname);
2023					SAFE_FREE(dir);
2024				}
2025				return NULL;
2026
2027			}
2028
2029			/* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
2030			/* However, we check to see if <server> is an IP address first */
2031
2032			if (!is_ipaddress(server) &&  /* Not an IP addr so check next */
2033			    (resolve_name(server, &rem_ip, 0x1d) ||   /* Found LMB */
2034                                    resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
2035				fstring buserver;
2036
2037				dir->dir_type = SMBC_SERVER;
2038
2039				/*
2040				 * Get the backup list ...
2041				 */
2042
2043
2044				if (!name_status_find(server, 0, 0, rem_ip, buserver)) {
2045
2046					DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
2047					errno = EPERM;  /* FIXME, is this correct */
2048					return NULL;
2049
2050				}
2051
2052				/*
2053				 * Get a connection to IPC$ on the server if we do not already have one
2054				 */
2055
2056				srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
2057
2058				if (!srv) {
2059				        DEBUG(0, ("got no contact to IPC$\n"));
2060					if (dir) {
2061						SAFE_FREE(dir->fname);
2062						SAFE_FREE(dir);
2063					}
2064					return NULL;
2065
2066				}
2067
2068				dir->srv = srv;
2069
2070				/* Now, list the servers ... */
2071
2072				if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
2073						       (void *)dir)) {
2074
2075					if (dir) {
2076						SAFE_FREE(dir->fname);
2077						SAFE_FREE(dir);
2078					}
2079					return NULL;
2080
2081				}
2082			}
2083			else {
2084
2085				if (resolve_name(server, &rem_ip, 0x20)) {
2086
2087					/* Now, list the shares ... */
2088
2089					dir->dir_type = SMBC_FILE_SHARE;
2090
2091					srv = smbc_server(context, server, "IPC$", workgroup, user, password);
2092
2093					if (!srv) {
2094
2095						if (dir) {
2096							SAFE_FREE(dir->fname);
2097							SAFE_FREE(dir);
2098						}
2099						return NULL;
2100
2101					}
2102
2103					dir->srv = srv;
2104
2105					/* Now, list the servers ... */
2106
2107					if (cli_RNetShareEnum(&srv->cli, list_fn,
2108							      (void *)dir) < 0) {
2109
2110						errno = cli_errno(&srv->cli);
2111						if (dir) {
2112							SAFE_FREE(dir->fname);
2113							SAFE_FREE(dir);
2114						}
2115						return NULL;
2116
2117					}
2118
2119				}
2120				else {
2121
2122					errno = ECONNREFUSED;   /* Neither the workgroup nor server exists */
2123					if (dir) {
2124						SAFE_FREE(dir->fname);
2125						SAFE_FREE(dir);
2126					}
2127					return NULL;
2128
2129				}
2130
2131			}
2132
2133		}
2134		else { /* The server and share are specified ... work from there ... */
2135
2136			/* Well, we connect to the server and list the directory */
2137
2138			dir->dir_type = SMBC_FILE_SHARE;
2139
2140			srv = smbc_server(context, server, share, workgroup, user, password);
2141
2142			if (!srv) {
2143
2144				if (dir) {
2145					SAFE_FREE(dir->fname);
2146					SAFE_FREE(dir);
2147				}
2148				return NULL;
2149
2150			}
2151
2152			dir->srv = srv;
2153
2154			/* Now, list the files ... */
2155
2156			pstrcat(path, "\\*");
2157
2158			if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn,
2159				     (void *)dir) < 0) {
2160
2161				if (dir) {
2162					SAFE_FREE(dir->fname);
2163					SAFE_FREE(dir);
2164				}
2165				errno = smbc_errno(context, &srv->cli);
2166				return NULL;
2167
2168			}
2169		}
2170
2171	}
2172
2173	DLIST_ADD(context->internal->_files, dir);
2174	return dir;
2175
2176}
2177
2178/*
2179 * Routine to close a directory
2180 */
2181
2182static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
2183{
2184
2185        if (!context || !context->internal ||
2186	    !context->internal->_initialized) {
2187
2188		errno = EINVAL;
2189		return -1;
2190
2191	}
2192
2193	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2194
2195		errno = EBADF;
2196		return -1;
2197
2198	}
2199
2200	smbc_remove_dir(dir); /* Clean it up */
2201
2202	DLIST_REMOVE(context->internal->_files, dir);
2203
2204	if (dir) {
2205
2206		SAFE_FREE(dir->fname);
2207		SAFE_FREE(dir);    /* Free the space too */
2208	}
2209
2210	return 0;
2211
2212}
2213
2214static void smbc_readdir_internal(SMBCCTX * context,
2215                                  struct smbc_dirent *dest,
2216                                  struct smbc_dirent *src,
2217                                  int max_namebuf_len)
2218{
2219        if (context->options.urlencode_readdir_entries) {
2220
2221                /* url-encode the name.  get back remaining buffer space */
2222                max_namebuf_len =
2223                        smbc_urlencode(dest->name, src->name, max_namebuf_len);
2224
2225                /* We now know the name length */
2226                dest->namelen = strlen(dest->name);
2227
2228                /* Save the pointer to the beginning of the comment */
2229                dest->comment = dest->name + dest->namelen + 1;
2230
2231                /* Copy the comment */
2232                strncpy(dest->comment, src->comment, max_namebuf_len);
2233
2234                /* Ensure the comment is null terminated */
2235                if (max_namebuf_len > src->commentlen) {
2236                        dest->comment[src->commentlen] = '\0';
2237                } else {
2238                        dest->comment[max_namebuf_len - 1] = '\0';
2239                }
2240
2241                /* Save other fields */
2242                dest->smbc_type = src->smbc_type;
2243                dest->commentlen = strlen(dest->comment);
2244                dest->dirlen = ((dest->comment + dest->commentlen + 1) -
2245                                (char *) dest);
2246        } else {
2247
2248                /* No encoding.  Just copy the entry as is. */
2249                memcpy(dest, src, src->dirlen);
2250                dest->comment = (char *)(&dest->name + src->namelen + 1);
2251        }
2252
2253}
2254
2255/*
2256 * Routine to get a directory entry
2257 */
2258
2259struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
2260{
2261        int maxlen;
2262	struct smbc_dirent *dirp, *dirent;
2263
2264	/* Check that all is ok first ... */
2265
2266	if (!context || !context->internal ||
2267	    !context->internal->_initialized) {
2268
2269		errno = EINVAL;
2270                DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
2271		return NULL;
2272
2273	}
2274
2275	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2276
2277		errno = EBADF;
2278                DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
2279		return NULL;
2280
2281	}
2282
2283	if (dir->file != False) { /* FIXME, should be dir, perhaps */
2284
2285		errno = ENOTDIR;
2286                DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
2287		return NULL;
2288
2289	}
2290
2291	if (!dir->dir_next) {
2292		return NULL;
2293        }
2294
2295        dirent = dir->dir_next->dirent;
2296        if (!dirent) {
2297
2298                errno = ENOENT;
2299                return NULL;
2300
2301        }
2302
2303        dirp = (struct smbc_dirent *)context->internal->_dirent;
2304        maxlen = (sizeof(context->internal->_dirent) -
2305                  sizeof(struct smbc_dirent));
2306
2307        smbc_readdir_internal(context, dirp, dirent, maxlen);
2308
2309        dir->dir_next = dir->dir_next->next;
2310
2311        return dirp;
2312}
2313
2314/*
2315 * Routine to get directory entries
2316 */
2317
2318static int smbc_getdents_ctx(SMBCCTX *context,
2319                             SMBCFILE *dir,
2320                             struct smbc_dirent *dirp,
2321                             int count)
2322{
2323	int rem = count;
2324        int reqd;
2325        int maxlen;
2326	char *ndir = (char *)dirp;
2327	struct smbc_dir_list *dirlist;
2328
2329	/* Check that all is ok first ... */
2330
2331	if (!context || !context->internal ||
2332	    !context->internal->_initialized) {
2333
2334		errno = EINVAL;
2335		return -1;
2336
2337	}
2338
2339	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2340
2341		errno = EBADF;
2342		return -1;
2343
2344	}
2345
2346	if (dir->file != False) { /* FIXME, should be dir, perhaps */
2347
2348		errno = ENOTDIR;
2349		return -1;
2350
2351	}
2352
2353	/*
2354	 * Now, retrieve the number of entries that will fit in what was passed
2355	 * We have to figure out if the info is in the list, or we need to
2356	 * send a request to the server to get the info.
2357	 */
2358
2359	while ((dirlist = dir->dir_next)) {
2360		struct smbc_dirent *dirent;
2361
2362		if (!dirlist->dirent) {
2363
2364			errno = ENOENT;  /* Bad error */
2365			return -1;
2366
2367		}
2368
2369                /* Do urlencoding of next entry, if so selected */
2370                dirent = (struct smbc_dirent *)context->internal->_dirent;
2371                maxlen = (sizeof(context->internal->_dirent) -
2372                          sizeof(struct smbc_dirent));
2373                smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
2374
2375                reqd = dirent->dirlen;
2376
2377		if (rem < reqd) {
2378
2379			if (rem < count) { /* We managed to copy something */
2380
2381				errno = 0;
2382				return count - rem;
2383
2384			}
2385			else { /* Nothing copied ... */
2386
2387				errno = EINVAL;  /* Not enough space ... */
2388				return -1;
2389
2390			}
2391
2392		}
2393
2394		memcpy(ndir, dirent, reqd); /* Copy the data in ... */
2395
2396		((struct smbc_dirent *)ndir)->comment =
2397			(char *)(&((struct smbc_dirent *)ndir)->name +
2398                                 dirent->namelen +
2399                                 1);
2400
2401		ndir += reqd;
2402
2403		rem -= reqd;
2404
2405		dir->dir_next = dirlist = dirlist -> next;
2406	}
2407
2408	if (rem == count)
2409		return 0;
2410	else
2411		return count - rem;
2412
2413}
2414
2415/*
2416 * Routine to create a directory ...
2417 */
2418
2419static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
2420{
2421	SMBCSRV *srv;
2422	fstring server, share, user, password, workgroup;
2423	pstring path;
2424
2425	if (!context || !context->internal ||
2426	    !context->internal->_initialized) {
2427
2428		errno = EINVAL;
2429		return -1;
2430
2431	}
2432
2433	if (!fname) {
2434
2435		errno = EINVAL;
2436		return -1;
2437
2438	}
2439
2440	DEBUG(4, ("smbc_mkdir(%s)\n", fname));
2441
2442	if (smbc_parse_path(context, fname,
2443                            server, sizeof(server),
2444                            share, sizeof(share),
2445                            path, sizeof(path),
2446                            user, sizeof(user),
2447                            password, sizeof(password),
2448                            NULL, 0)) {
2449                errno = EINVAL;
2450                return -1;
2451        }
2452
2453	if (user[0] == (char)0) fstrcpy(user, context->user);
2454
2455	fstrcpy(workgroup, context->workgroup);
2456
2457	srv = smbc_server(context, server, share, workgroup, user, password);
2458
2459	if (!srv) {
2460
2461		return -1;  /* errno set by smbc_server */
2462
2463	}
2464
2465	if (!cli_mkdir(&srv->cli, path)) {
2466
2467		errno = smbc_errno(context, &srv->cli);
2468		return -1;
2469
2470	}
2471
2472	return 0;
2473
2474}
2475
2476/*
2477 * Our list function simply checks to see if a directory is not empty
2478 */
2479
2480static int smbc_rmdir_dirempty = True;
2481
2482static void rmdir_list_fn(const char *mnt, file_info *finfo, const char *mask, void *state)
2483{
2484
2485	if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
2486		smbc_rmdir_dirempty = False;
2487
2488}
2489
2490/*
2491 * Routine to remove a directory
2492 */
2493
2494static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
2495{
2496	SMBCSRV *srv;
2497	fstring server, share, user, password, workgroup;
2498	pstring path;
2499
2500	if (!context || !context->internal ||
2501	    !context->internal->_initialized) {
2502
2503		errno = EINVAL;
2504		return -1;
2505
2506	}
2507
2508	if (!fname) {
2509
2510		errno = EINVAL;
2511		return -1;
2512
2513	}
2514
2515	DEBUG(4, ("smbc_rmdir(%s)\n", fname));
2516
2517	if (smbc_parse_path(context, fname,
2518                            server, sizeof(server),
2519                            share, sizeof(share),
2520                            path, sizeof(path),
2521                            user, sizeof(user),
2522                            password, sizeof(password),
2523                            NULL, 0))
2524        {
2525                errno = EINVAL;
2526                return -1;
2527        }
2528
2529	if (user[0] == (char)0) fstrcpy(user, context->user);
2530
2531	fstrcpy(workgroup, context->workgroup);
2532
2533	srv = smbc_server(context, server, share, workgroup, user, password);
2534
2535	if (!srv) {
2536
2537		return -1;  /* errno set by smbc_server */
2538
2539	}
2540
2541	/* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
2542
2543	   mode = aDIR | aRONLY;
2544
2545	   }
2546	   else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
2547
2548	   if (strcmp(path, "\\") == 0) {
2549
2550	   mode = aDIR | aRONLY;
2551
2552	   }
2553	   else {
2554
2555	   mode = aRONLY;
2556	   smbc_stat_printjob(srv, path, &size, &m_time);
2557	   c_time = a_time = m_time;
2558
2559	   }
2560	   else { */
2561
2562	if (!cli_rmdir(&srv->cli, path)) {
2563
2564		errno = smbc_errno(context, &srv->cli);
2565
2566		if (errno == EACCES) {  /* Check if the dir empty or not */
2567
2568			pstring lpath; /* Local storage to avoid buffer overflows */
2569
2570			smbc_rmdir_dirempty = True;  /* Make this so ... */
2571
2572			pstrcpy(lpath, path);
2573			pstrcat(lpath, "\\*");
2574
2575			if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
2576				     NULL) < 0) {
2577
2578				/* Fix errno to ignore latest error ... */
2579
2580				DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n",
2581					  smbc_errno(context, &srv->cli)));
2582				errno = EACCES;
2583
2584			}
2585
2586			if (smbc_rmdir_dirempty)
2587				errno = EACCES;
2588			else
2589				errno = ENOTEMPTY;
2590
2591		}
2592
2593		return -1;
2594
2595	}
2596
2597	return 0;
2598
2599}
2600
2601/*
2602 * Routine to return the current directory position
2603 */
2604
2605static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
2606{
2607	off_t ret_val; /* Squash warnings about cast */
2608
2609	if (!context || !context->internal ||
2610	    !context->internal->_initialized) {
2611
2612		errno = EINVAL;
2613		return -1;
2614
2615	}
2616
2617	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
2618
2619		errno = EBADF;
2620		return -1;
2621
2622	}
2623
2624	if (dir->file != False) { /* FIXME, should be dir, perhaps */
2625
2626		errno = ENOTDIR;
2627		return -1;
2628
2629	}
2630
2631	/*
2632	 * We return the pointer here as the offset
2633	 */
2634	ret_val = (int)dir->dir_next;
2635	return ret_val;
2636
2637}
2638
2639/*
2640 * A routine to run down the list and see if the entry is OK
2641 */
2642
2643struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
2644					 struct smbc_dirent *dirent)
2645{
2646
2647	/* Run down the list looking for what we want */
2648
2649	if (dirent) {
2650
2651		struct smbc_dir_list *tmp = list;
2652
2653		while (tmp) {
2654
2655			if (tmp->dirent == dirent)
2656				return tmp;
2657
2658			tmp = tmp->next;
2659
2660		}
2661
2662	}
2663
2664	return NULL;  /* Not found, or an error */
2665
2666}
2667
2668
2669/*
2670 * Routine to seek on a directory
2671 */
2672
2673static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
2674{
2675	long int l_offset = offset;  /* Handle problems of size */
2676	struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
2677	struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
2678
2679	if (!context || !context->internal ||
2680	    !context->internal->_initialized) {
2681
2682		errno = EINVAL;
2683		return -1;
2684
2685	}
2686
2687	if (dir->file != False) { /* FIXME, should be dir, perhaps */
2688
2689		errno = ENOTDIR;
2690		return -1;
2691
2692	}
2693
2694	/* Now, check what we were passed and see if it is OK ... */
2695
2696	if (dirent == NULL) {  /* Seek to the begining of the list */
2697
2698		dir->dir_next = dir->dir_list;
2699		return 0;
2700
2701	}
2702
2703	/* Now, run down the list and make sure that the entry is OK       */
2704	/* This may need to be changed if we change the format of the list */
2705
2706	if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
2707
2708		errno = EINVAL;   /* Bad entry */
2709		return -1;
2710
2711	}
2712
2713	dir->dir_next = list_ent;
2714
2715	return 0;
2716
2717}
2718
2719/*
2720 * Routine to fstat a dir
2721 */
2722
2723static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
2724{
2725
2726	if (!context || !context->internal ||
2727	    !context->internal->_initialized) {
2728
2729		errno = EINVAL;
2730		return -1;
2731
2732	}
2733
2734	/* No code yet ... */
2735
2736	return 0;
2737
2738}
2739
2740int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
2741{
2742        SMBCSRV *srv;
2743	fstring server, share, user, password, workgroup;
2744	pstring path;
2745	uint16 mode;
2746
2747	if (!context || !context->internal ||
2748	    !context->internal->_initialized) {
2749
2750		errno = EINVAL;  /* Best I can think of ... */
2751		return -1;
2752
2753	}
2754
2755	if (!fname) {
2756
2757		errno = EINVAL;
2758		return -1;
2759
2760	}
2761
2762	DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
2763
2764	if (smbc_parse_path(context, fname,
2765                            server, sizeof(server),
2766                            share, sizeof(share),
2767                            path, sizeof(path),
2768                            user, sizeof(user),
2769                            password, sizeof(password),
2770                            NULL, 0)) {
2771                errno = EINVAL;
2772                return -1;
2773        }
2774
2775	if (user[0] == (char)0) fstrcpy(user, context->user);
2776
2777	fstrcpy(workgroup, context->workgroup);
2778
2779	srv = smbc_server(context, server, share, workgroup, user, password);
2780
2781	if (!srv) {
2782		return -1;  /* errno set by smbc_server */
2783	}
2784
2785	mode = 0;
2786
2787	if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
2788	if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
2789	if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
2790	if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
2791
2792	if (!cli_setatr(&srv->cli, path, mode, 0)) {
2793		errno = smbc_errno(context, &srv->cli);
2794		return -1;
2795	}
2796
2797        return 0;
2798}
2799
2800int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
2801{
2802        SMBCSRV *srv;
2803	fstring server, share, user, password, workgroup;
2804	pstring path;
2805	uint16 mode;
2806        time_t t = (tbuf == NULL ? time(NULL) : tbuf->tv_sec);
2807
2808	if (!context || !context->internal ||
2809	    !context->internal->_initialized) {
2810
2811		errno = EINVAL;  /* Best I can think of ... */
2812		return -1;
2813
2814	}
2815
2816	if (!fname) {
2817
2818		errno = EINVAL;
2819		return -1;
2820
2821	}
2822
2823	DEBUG(4, ("smbc_utimes(%s, [%s])\n", fname, ctime(&t)));
2824
2825	if (smbc_parse_path(context, fname,
2826                            server, sizeof(server),
2827                            share, sizeof(share),
2828                            path, sizeof(path),
2829                            user, sizeof(user),
2830                            password, sizeof(password),
2831                            NULL, 0)) {
2832                errno = EINVAL;
2833                return -1;
2834        }
2835
2836	if (user[0] == (char)0) fstrcpy(user, context->user);
2837
2838	fstrcpy(workgroup, context->workgroup);
2839
2840	srv = smbc_server(context, server, share, workgroup, user, password);
2841
2842	if (!srv) {
2843		return -1;  /* errno set by smbc_server */
2844	}
2845
2846	if (!smbc_getatr(context, srv, path,
2847                         &mode, NULL,
2848                         NULL, NULL, NULL,
2849                         NULL)) {
2850                return -1;
2851	}
2852
2853	if (!cli_setatr(&srv->cli, path, mode, t)) {
2854		/* some servers always refuse directory changes */
2855		if (!(mode & aDIR)) {
2856			errno = smbc_errno(context, &srv->cli);
2857                        return -1;
2858		}
2859	}
2860
2861	return 0;
2862}
2863
2864
2865/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
2866   However NT4 gives a "The information may have been modified by a
2867   computer running Windows NT 5.0" if denied ACEs do not appear before
2868   allowed ACEs. */
2869
2870static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
2871{
2872	if (sec_ace_equal(ace1, ace2))
2873		return 0;
2874
2875	if (ace1->type != ace2->type)
2876		return ace2->type - ace1->type;
2877
2878	if (sid_compare(&ace1->trustee, &ace2->trustee))
2879		return sid_compare(&ace1->trustee, &ace2->trustee);
2880
2881	if (ace1->flags != ace2->flags)
2882		return ace1->flags - ace2->flags;
2883
2884	if (ace1->info.mask != ace2->info.mask)
2885		return ace1->info.mask - ace2->info.mask;
2886
2887	if (ace1->size != ace2->size)
2888		return ace1->size - ace2->size;
2889
2890	return memcmp(ace1, ace2, sizeof(SEC_ACE));
2891}
2892
2893
2894static void sort_acl(SEC_ACL *the_acl)
2895{
2896	uint32 i;
2897	if (!the_acl) return;
2898
2899	qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare);
2900
2901	for (i=1;i<the_acl->num_aces;) {
2902		if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) {
2903			int j;
2904			for (j=i; j<the_acl->num_aces-1; j++) {
2905				the_acl->ace[j] = the_acl->ace[j+1];
2906			}
2907			the_acl->num_aces--;
2908		} else {
2909			i++;
2910		}
2911	}
2912}
2913
2914/* convert a SID to a string, either numeric or username/group */
2915static void convert_sid_to_string(struct cli_state *ipc_cli,
2916                                  POLICY_HND *pol,
2917                                  fstring str,
2918                                  BOOL numeric,
2919                                  DOM_SID *sid)
2920{
2921	char **domains = NULL;
2922	char **names = NULL;
2923	uint32 *types = NULL;
2924
2925	sid_to_string(str, sid);
2926
2927        if (numeric) return;     /* no lookup desired */
2928
2929	/* Ask LSA to convert the sid to a name */
2930
2931	if (!NT_STATUS_IS_OK(cli_lsa_lookup_sids(ipc_cli, ipc_cli->mem_ctx,
2932						 pol, 1, sid, &domains,
2933						 &names, &types)) ||
2934	    !domains || !domains[0] || !names || !names[0]) {
2935		return;
2936	}
2937
2938	/* Converted OK */
2939
2940	slprintf(str, sizeof(fstring) - 1, "%s%s%s",
2941		 domains[0], lp_winbind_separator(),
2942		 names[0]);
2943}
2944
2945/* convert a string to a SID, either numeric or username/group */
2946static BOOL convert_string_to_sid(struct cli_state *ipc_cli,
2947                                  POLICY_HND *pol,
2948                                  BOOL numeric,
2949                                  DOM_SID *sid,
2950                                  const char *str)
2951{
2952	uint32 *types = NULL;
2953	DOM_SID *sids = NULL;
2954	BOOL result = True;
2955
2956        if (numeric) {
2957                if (strncmp(str, "S-", 2) == 0) {
2958                        return string_to_sid(sid, str);
2959                }
2960
2961                result = False;
2962                goto done;
2963        }
2964
2965	if (!NT_STATUS_IS_OK(cli_lsa_lookup_names(ipc_cli, ipc_cli->mem_ctx,
2966						  pol, 1, &str, &sids,
2967						  &types))) {
2968		result = False;
2969		goto done;
2970	}
2971
2972	sid_copy(sid, &sids[0]);
2973 done:
2974
2975	return result;
2976}
2977
2978
2979/* parse an ACE in the same format as print_ace() */
2980static BOOL parse_ace(struct cli_state *ipc_cli,
2981                      POLICY_HND *pol,
2982                      SEC_ACE *ace,
2983                      BOOL numeric,
2984                      char *str)
2985{
2986	char *p;
2987	const char *cp;
2988	fstring tok;
2989	unsigned atype, aflags, amask;
2990	DOM_SID sid;
2991	SEC_ACCESS mask;
2992	const struct perm_value *v;
2993        struct perm_value {
2994                const char *perm;
2995                uint32 mask;
2996        };
2997
2998        /* These values discovered by inspection */
2999        static const struct perm_value special_values[] = {
3000                { "R", 0x00120089 },
3001                { "W", 0x00120116 },
3002                { "X", 0x001200a0 },
3003                { "D", 0x00010000 },
3004                { "P", 0x00040000 },
3005                { "O", 0x00080000 },
3006                { NULL, 0 },
3007        };
3008
3009        static const struct perm_value standard_values[] = {
3010                { "READ",   0x001200a9 },
3011                { "CHANGE", 0x001301bf },
3012                { "FULL",   0x001f01ff },
3013                { NULL, 0 },
3014        };
3015
3016
3017	ZERO_STRUCTP(ace);
3018	p = strchr_m(str,':');
3019	if (!p) return False;
3020	*p = '\0';
3021	p++;
3022	/* Try to parse numeric form */
3023
3024	if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3025	    convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3026		goto done;
3027	}
3028
3029	/* Try to parse text form */
3030
3031	if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3032		return False;
3033	}
3034
3035	cp = p;
3036	if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3037		return False;
3038	}
3039
3040	if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3041		atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3042	} else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3043		atype = SEC_ACE_TYPE_ACCESS_DENIED;
3044	} else {
3045		return False;
3046	}
3047
3048	/* Only numeric form accepted for flags at present */
3049
3050	if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3051	      sscanf(tok, "%i", &aflags))) {
3052		return False;
3053	}
3054
3055	if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3056		return False;
3057	}
3058
3059	if (strncmp(tok, "0x", 2) == 0) {
3060		if (sscanf(tok, "%i", &amask) != 1) {
3061			return False;
3062		}
3063		goto done;
3064	}
3065
3066	for (v = standard_values; v->perm; v++) {
3067		if (strcmp(tok, v->perm) == 0) {
3068			amask = v->mask;
3069			goto done;
3070		}
3071	}
3072
3073	p = tok;
3074
3075	while(*p) {
3076		BOOL found = False;
3077
3078		for (v = special_values; v->perm; v++) {
3079			if (v->perm[0] == *p) {
3080				amask |= v->mask;
3081				found = True;
3082			}
3083		}
3084
3085		if (!found) return False;
3086		p++;
3087	}
3088
3089	if (*p) {
3090		return False;
3091	}
3092
3093 done:
3094	mask.mask = amask;
3095	init_sec_ace(ace, &sid, atype, mask, aflags);
3096	return True;
3097}
3098
3099/* add an ACE to a list of ACEs in a SEC_ACL */
3100static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace, TALLOC_CTX *ctx)
3101{
3102	SEC_ACL *new;
3103	SEC_ACE *aces;
3104	if (! *the_acl) {
3105		(*the_acl) = make_sec_acl(ctx, 3, 1, ace);
3106		return True;
3107	}
3108
3109	aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces);
3110	memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE));
3111	memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
3112	new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
3113	SAFE_FREE(aces);
3114	(*the_acl) = new;
3115	return True;
3116}
3117
3118
3119/* parse a ascii version of a security descriptor */
3120static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx,
3121                                struct cli_state *ipc_cli,
3122                                POLICY_HND *pol,
3123                                BOOL numeric,
3124                                char *str)
3125{
3126	const char *p = str;
3127	fstring tok;
3128	SEC_DESC *ret;
3129	size_t sd_size;
3130	DOM_SID *grp_sid=NULL, *owner_sid=NULL;
3131	SEC_ACL *dacl=NULL;
3132	int revision=1;
3133
3134	while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
3135
3136		if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
3137			revision = strtol(tok+9, NULL, 16);
3138			continue;
3139		}
3140
3141		if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
3142			owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3143			if (!owner_sid ||
3144			    !convert_string_to_sid(ipc_cli, pol,
3145                                                   numeric,
3146                                                   owner_sid, tok+6)) {
3147				DEBUG(5, ("Failed to parse owner sid\n"));
3148				return NULL;
3149			}
3150			continue;
3151		}
3152
3153		if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
3154			owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3155			if (!owner_sid ||
3156			    !convert_string_to_sid(ipc_cli, pol,
3157                                                   False,
3158                                                   owner_sid, tok+7)) {
3159				DEBUG(5, ("Failed to parse owner sid\n"));
3160				return NULL;
3161			}
3162			continue;
3163		}
3164
3165		if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
3166			grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3167			if (!grp_sid ||
3168			    !convert_string_to_sid(ipc_cli, pol,
3169                                                   numeric,
3170                                                   grp_sid, tok+6)) {
3171				DEBUG(5, ("Failed to parse group sid\n"));
3172				return NULL;
3173			}
3174			continue;
3175		}
3176
3177		if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
3178			grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
3179			if (!grp_sid ||
3180			    !convert_string_to_sid(ipc_cli, pol,
3181                                                   False,
3182                                                   grp_sid, tok+6)) {
3183				DEBUG(5, ("Failed to parse group sid\n"));
3184				return NULL;
3185			}
3186			continue;
3187		}
3188
3189		if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
3190			SEC_ACE ace;
3191			if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
3192				DEBUG(5, ("Failed to parse ACL %s\n", tok));
3193				return NULL;
3194			}
3195			if(!add_ace(&dacl, &ace, ctx)) {
3196				DEBUG(5, ("Failed to add ACL %s\n", tok));
3197				return NULL;
3198			}
3199			continue;
3200		}
3201
3202		if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
3203			SEC_ACE ace;
3204			if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
3205				DEBUG(5, ("Failed to parse ACL %s\n", tok));
3206				return NULL;
3207			}
3208			if(!add_ace(&dacl, &ace, ctx)) {
3209				DEBUG(5, ("Failed to add ACL %s\n", tok));
3210				return NULL;
3211			}
3212			continue;
3213		}
3214
3215		DEBUG(5, ("Failed to parse security descriptor\n"));
3216		return NULL;
3217	}
3218
3219	ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
3220			    owner_sid, grp_sid, NULL, dacl, &sd_size);
3221
3222	SAFE_FREE(grp_sid);
3223	SAFE_FREE(owner_sid);
3224
3225	return ret;
3226}
3227
3228
3229/* Obtain the current dos attributes */
3230static DOS_ATTR_DESC *dos_attr_query(SMBCCTX *context,
3231                                     TALLOC_CTX *ctx,
3232                                     const char *filename,
3233                                     SMBCSRV *srv)
3234{
3235        time_t m_time = 0, a_time = 0, c_time = 0;
3236        size_t size = 0;
3237        uint16 mode = 0;
3238	SMB_INO_T inode = 0;
3239        DOS_ATTR_DESC *ret;
3240
3241        ret = talloc(ctx, sizeof(DOS_ATTR_DESC));
3242        if (!ret) {
3243                errno = ENOMEM;
3244                return NULL;
3245        }
3246
3247        /* Obtain the DOS attributes */
3248        if (!smbc_getatr(context, srv, filename, &mode, &size,
3249                         &c_time, &a_time, &m_time, &inode)) {
3250
3251                errno = smbc_errno(context, &srv->cli);
3252                DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
3253                return NULL;
3254
3255        }
3256
3257        ret->mode = mode;
3258        ret->size = size;
3259        ret->a_time = a_time;
3260        ret->c_time = c_time;
3261        ret->m_time = m_time;
3262        ret->inode = inode;
3263
3264        return ret;
3265}
3266
3267
3268/* parse a ascii version of a security descriptor */
3269static void dos_attr_parse(SMBCCTX *context,
3270                           DOS_ATTR_DESC *dad,
3271                           SMBCSRV *srv,
3272                           char *str)
3273{
3274	const char *p = str;
3275	fstring tok;
3276
3277	while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
3278
3279		if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
3280			dad->mode = strtol(tok+5, NULL, 16);
3281			continue;
3282		}
3283
3284		if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
3285                        dad->size = strtoll(tok+5, NULL, 10);
3286			continue;
3287		}
3288
3289		if (StrnCaseCmp(tok, "A_TIME:", 7) == 0) {
3290                        dad->a_time = strtoll(tok+7, NULL, 10);
3291			continue;
3292		}
3293
3294		if (StrnCaseCmp(tok, "C_TIME:", 7) == 0) {
3295                        dad->c_time = strtoll(tok+7, NULL, 10);
3296			continue;
3297		}
3298
3299		if (StrnCaseCmp(tok, "M_TIME:", 7) == 0) {
3300                        dad->m_time = strtoll(tok+7, NULL, 10);
3301			continue;
3302		}
3303
3304		if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
3305                        dad->inode = strtoll(tok+6, NULL, 10);
3306			continue;
3307		}
3308	}
3309}
3310
3311
3312/*****************************************************
3313retrieve the acls for a file
3314*******************************************************/
3315static int cacl_get(SMBCCTX *context, TALLOC_CTX *ctx, SMBCSRV *srv,
3316                    struct cli_state *ipc_cli, POLICY_HND *pol,
3317                    char *filename, char *name, char *buf, int bufsize)
3318{
3319	uint32 i;
3320        int n = 0;
3321        int n_used;
3322        BOOL all;
3323        BOOL all_nt;
3324        BOOL all_dos;
3325        BOOL some_nt;
3326        BOOL some_dos;
3327        BOOL numeric = True;
3328        BOOL determine_size = (bufsize == 0);
3329	int fnum = -1;
3330	SEC_DESC *sd;
3331	fstring sidstr;
3332        char *p;
3333	time_t m_time = 0, a_time = 0, c_time = 0;
3334	size_t size = 0;
3335	uint16 mode = 0;
3336	SMB_INO_T ino = 0;
3337        struct cli_state *cli = &srv->cli;
3338
3339        all = (StrnCaseCmp(name, "system.*", 8) == 0);
3340        all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
3341        all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
3342        some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
3343        some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
3344        numeric = (* (name + strlen(name) - 1) != '+');
3345
3346        n_used = 0;
3347
3348        /*
3349         * If we are (possibly) talking to an NT or new system and some NT
3350         * attributes have been requested...
3351         */
3352        if (ipc_cli && (all || some_nt)) {
3353                /* Point to the portion after "system.nt_sec_desc." */
3354                name += 19;     /* if (all) this will be invalid but unused */
3355
3356                /* ... then obtain any NT attributes which were requested */
3357                fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
3358
3359                if (fnum == -1) {
3360                        DEBUG(5, ("cacl_get failed to open %s: %s\n",
3361                                  filename, cli_errstr(cli)));
3362                        errno = 0;
3363                        return -1;
3364                }
3365
3366                sd = cli_query_secdesc(cli, fnum, ctx);
3367
3368                if (!sd) {
3369                        DEBUG(5,
3370                              ("cacl_get Failed to query old descriptor\n"));
3371                        errno = 0;
3372                        return -1;
3373                }
3374
3375                cli_close(cli, fnum);
3376
3377                if (all || all_nt) {
3378                        if (determine_size) {
3379                                p = talloc_asprintf(ctx,
3380                                                    "REVISION:%d",
3381                                                    sd->revision);
3382                                if (!p) {
3383                                        errno = ENOMEM;
3384                                        return -1;
3385                                }
3386                                n = strlen(p);
3387                        } else {
3388                                n = snprintf(buf, bufsize,
3389                                             "REVISION:%d", sd->revision);
3390                        }
3391                } else if (StrCaseCmp(name, "revision") == 0) {
3392                        if (determine_size) {
3393                                p = talloc_asprintf(ctx, "%d", sd->revision);
3394                                if (!p) {
3395                                        errno = ENOMEM;
3396                                        return -1;
3397                                }
3398                                n = strlen(p);
3399                        } else {
3400                                n = snprintf(buf, bufsize, "%d", sd->revision);
3401                        }
3402                }
3403
3404                if (!determine_size && n > bufsize) {
3405                        errno = ERANGE;
3406                        return -1;
3407                }
3408                buf += n;
3409                n_used += n;
3410                bufsize -= n;
3411
3412                /* Get owner and group sid */
3413
3414                if (sd->owner_sid) {
3415                        convert_sid_to_string(ipc_cli, pol,
3416                                              sidstr, numeric, sd->owner_sid);
3417                } else {
3418                        fstrcpy(sidstr, "");
3419                }
3420
3421                if (all || all_nt) {
3422                        if (determine_size) {
3423                                p = talloc_asprintf(ctx, ",OWNER:%s", sidstr);
3424                                if (!p) {
3425                                        errno = ENOMEM;
3426                                        return -1;
3427                                }
3428                                n = strlen(p);
3429                        } else {
3430                                n = snprintf(buf, bufsize,
3431                                             ",OWNER:%s", sidstr);
3432                        }
3433                } else if (StrnCaseCmp(name, "owner", 5) == 0) {
3434                        if (determine_size) {
3435                                p = talloc_asprintf(ctx, "%s", sidstr);
3436                                if (!p) {
3437                                        errno = ENOMEM;
3438                                        return -1;
3439                                }
3440                                n = strlen(p);
3441                        } else {
3442                                n = snprintf(buf, bufsize, "%s", sidstr);
3443                        }
3444                }
3445
3446                if (!determine_size && n > bufsize) {
3447                        errno = ERANGE;
3448                        return -1;
3449                }
3450                buf += n;
3451                n_used += n;
3452                bufsize -= n;
3453
3454                if (sd->grp_sid) {
3455                        convert_sid_to_string(ipc_cli, pol,
3456                                              sidstr, numeric, sd->grp_sid);
3457                } else {
3458                        fstrcpy(sidstr, "");
3459                }
3460
3461                if (all || all_nt) {
3462                        if (determine_size) {
3463                                p = talloc_asprintf(ctx, ",GROUP:%s", sidstr);
3464                                if (!p) {
3465                                        errno = ENOMEM;
3466                                        return -1;
3467                                }
3468                                n = strlen(p);
3469                        } else {
3470                                n = snprintf(buf, bufsize,
3471                                             ",GROUP:%s", sidstr);
3472                        }
3473                } else if (StrnCaseCmp(name, "group", 5) == 0) {
3474                        if (determine_size) {
3475                                p = talloc_asprintf(ctx, "%s", sidstr);
3476                                if (!p) {
3477                                        errno = ENOMEM;
3478                                        return -1;
3479                                }
3480                                n = strlen(p);
3481                        } else {
3482                                n = snprintf(buf, bufsize, "%s", sidstr);
3483                        }
3484                }
3485
3486                if (!determine_size && n > bufsize) {
3487                        errno = ERANGE;
3488                        return -1;
3489                }
3490                buf += n;
3491                n_used += n;
3492                bufsize -= n;
3493
3494                /* Add aces to value buffer  */
3495                for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
3496
3497                        SEC_ACE *ace = &sd->dacl->ace[i];
3498                        convert_sid_to_string(ipc_cli, pol,
3499                                              sidstr, numeric, &ace->trustee);
3500
3501                        if (all || all_nt) {
3502                                if (determine_size) {
3503                                        p = talloc_asprintf(ctx,
3504                                                            ",ACL:"
3505                                                            "%s:%d/%d/0x%08x",
3506                                                            sidstr,
3507                                                            ace->type,
3508                                                            ace->flags,
3509                                                            ace->info.mask);
3510                                        if (!p) {
3511                                                errno = ENOMEM;
3512                                                return -1;
3513                                        }
3514                                        n = strlen(p);
3515                                } else {
3516                                        n = snprintf(buf, bufsize,
3517                                                     ",ACL:%s:%d/%d/0x%08x",
3518                                                     sidstr,
3519                                                     ace->type,
3520                                                     ace->flags,
3521                                                     ace->info.mask);
3522                                }
3523                        } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
3524                                    StrCaseCmp(name + 3, sidstr) == 0) ||
3525                                   (StrnCaseCmp(name, "acl+", 4) == 0 &&
3526                                    StrCaseCmp(name + 4, sidstr) == 0)) {
3527                                if (determine_size) {
3528                                        p = talloc_asprintf(ctx,
3529                                                            "%d/%d/0x%08x",
3530                                                            ace->type,
3531                                                            ace->flags,
3532                                                            ace->info.mask);
3533                                        if (!p) {
3534                                                errno = ENOMEM;
3535                                                return -1;
3536                                        }
3537                                        n = strlen(p);
3538                                } else {
3539                                        n = snprintf(buf, bufsize,
3540                                                     "%d/%d/0x%08x",
3541                                                     ace->type,
3542                                                     ace->flags,
3543                                                     ace->info.mask);
3544                                }
3545                        }
3546                        if (n > bufsize) {
3547                                errno = ERANGE;
3548                                return -1;
3549                        }
3550                        buf += n;
3551                        n_used += n;
3552                        bufsize -= n;
3553                }
3554
3555                /* Restore name pointer to its original value */
3556                name -= 19;
3557        }
3558
3559        if (all || some_dos) {
3560                /* Point to the portion after "system.dos_attr." */
3561                name += 16;     /* if (all) this will be invalid but unused */
3562
3563                /* Obtain the DOS attributes */
3564                if (!smbc_getatr(context, srv, filename, &mode, &size,
3565                                 &c_time, &a_time, &m_time, &ino)) {
3566
3567                        errno = smbc_errno(context, &srv->cli);
3568                        return -1;
3569
3570                }
3571
3572                if (all || all_dos) {
3573                        if (determine_size) {
3574                                p = talloc_asprintf(ctx,
3575                                                    "%sMODE:0x%x",
3576                                                    (ipc_cli &&
3577                                                     (all || some_nt)
3578                                                     ? ","
3579                                                     : ""),
3580                                                    mode);
3581                                if (!p) {
3582                                        errno = ENOMEM;
3583                                        return -1;
3584                                }
3585                                n = strlen(p);
3586                        } else {
3587                                n = snprintf(buf, bufsize,
3588                                             "%sMODE:0x%x",
3589                                             (ipc_cli &&
3590                                              (all || some_nt)
3591                                              ? ","
3592                                              : ""),
3593                                             mode);
3594                        }
3595                } else if (StrCaseCmp(name, "mode") == 0) {
3596                        if (determine_size) {
3597                                p = talloc_asprintf(ctx, "0x%x", mode);
3598                                if (!p) {
3599                                        errno = ENOMEM;
3600                                        return -1;
3601                                }
3602                                n = strlen(p);
3603                        } else {
3604                                n = snprintf(buf, bufsize, "0x%x", mode);
3605                        }
3606                }
3607
3608                if (!determine_size && n > bufsize) {
3609                        errno = ERANGE;
3610                        return -1;
3611                }
3612                buf += n;
3613                n_used += n;
3614                bufsize -= n;
3615
3616                if (all || all_dos) {
3617                        if (determine_size) {
3618                                p = talloc_asprintf(ctx,
3619                                                    ",SIZE:%llu",
3620                                                    (unsigned long long) size);
3621                                if (!p) {
3622                                        errno = ENOMEM;
3623                                        return -1;
3624                                }
3625                                n = strlen(p);
3626                        } else {
3627                                n = snprintf(buf, bufsize,
3628                                             ",SIZE:%llu",
3629                                             (unsigned long long) size);
3630                        }
3631                } else if (StrCaseCmp(name, "size") == 0) {
3632                        if (determine_size) {
3633                                p = talloc_asprintf(ctx,
3634                                                    "%llu",
3635                                                    (unsigned long long) size);
3636                                if (!p) {
3637                                        errno = ENOMEM;
3638                                        return -1;
3639                                }
3640                                n = strlen(p);
3641                        } else {
3642                                n = snprintf(buf, bufsize,
3643                                             "%llu",
3644                                             (unsigned long long) size);
3645                        }
3646                }
3647
3648                if (!determine_size && n > bufsize) {
3649                        errno = ERANGE;
3650                        return -1;
3651                }
3652                buf += n;
3653                n_used += n;
3654                bufsize -= n;
3655
3656                if (all || all_dos) {
3657                        if (determine_size) {
3658                                p = talloc_asprintf(ctx,
3659                                                    ",C_TIME:%lu", c_time);
3660                                if (!p) {
3661                                        errno = ENOMEM;
3662                                        return -1;
3663                                }
3664                                n = strlen(p);
3665                        } else {
3666                                n = snprintf(buf, bufsize,
3667                                             ",C_TIME:%lu", c_time);
3668                        }
3669                } else if (StrCaseCmp(name, "c_time") == 0) {
3670                        if (determine_size) {
3671                                p = talloc_asprintf(ctx, "%lu", c_time);
3672                                if (!p) {
3673                                        errno = ENOMEM;
3674                                        return -1;
3675                                }
3676                                n = strlen(p);
3677                        } else {
3678                                n = snprintf(buf, bufsize, "%lu", c_time);
3679                        }
3680                }
3681
3682                if (!determine_size && n > bufsize) {
3683                        errno = ERANGE;
3684                        return -1;
3685                }
3686                buf += n;
3687                n_used += n;
3688                bufsize -= n;
3689
3690                if (all || all_dos) {
3691                        if (determine_size) {
3692                                p = talloc_asprintf(ctx,
3693                                                    ",A_TIME:%lu", a_time);
3694                                if (!p) {
3695                                        errno = ENOMEM;
3696                                        return -1;
3697                                }
3698                                n = strlen(p);
3699                        } else {
3700                                n = snprintf(buf, bufsize,
3701                                             ",A_TIME:%lu", a_time);
3702                        }
3703                } else if (StrCaseCmp(name, "a_time") == 0) {
3704                        if (determine_size) {
3705                                p = talloc_asprintf(ctx, "%lu", a_time);
3706                                if (!p) {
3707                                        errno = ENOMEM;
3708                                        return -1;
3709                                }
3710                                n = strlen(p);
3711                        } else {
3712                                n = snprintf(buf, bufsize, "%lu", a_time);
3713                        }
3714                }
3715
3716                if (!determine_size && n > bufsize) {
3717                        errno = ERANGE;
3718                        return -1;
3719                }
3720                buf += n;
3721                n_used += n;
3722                bufsize -= n;
3723
3724                if (all || all_dos) {
3725                        if (determine_size) {
3726                                p = talloc_asprintf(ctx,
3727                                                    ",M_TIME:%lu", m_time);
3728                                if (!p) {
3729                                        errno = ENOMEM;
3730                                        return -1;
3731                                }
3732                                n = strlen(p);
3733                        } else {
3734                                n = snprintf(buf, bufsize,
3735                                             ",M_TIME:%lu", m_time);
3736                        }
3737                } else if (StrCaseCmp(name, "m_time") == 0) {
3738                        if (determine_size) {
3739                                p = talloc_asprintf(ctx, "%lu", m_time);
3740                                if (!p) {
3741                                        errno = ENOMEM;
3742                                        return -1;
3743                                }
3744                                n = strlen(p);
3745                        } else {
3746                                n = snprintf(buf, bufsize, "%lu", m_time);
3747                        }
3748                }
3749
3750                if (!determine_size && n > bufsize) {
3751                        errno = ERANGE;
3752                        return -1;
3753                }
3754                buf += n;
3755                n_used += n;
3756                bufsize -= n;
3757
3758                if (all || all_dos) {
3759                        if (determine_size) {
3760                                p = talloc_asprintf(ctx,
3761                                                    ",INODE:%llu",
3762                                                    (unsigned long long) ino);
3763                                if (!p) {
3764                                        errno = ENOMEM;
3765                                        return -1;
3766                                }
3767                                n = strlen(p);
3768                        } else {
3769                                n = snprintf(buf, bufsize,
3770                                             ",INODE:%llu",
3771                                             (unsigned long long) ino);
3772                        }
3773                } else if (StrCaseCmp(name, "inode") == 0) {
3774                        if (determine_size) {
3775                                p = talloc_asprintf(ctx,
3776                                                    "%llu",
3777                                                    (unsigned long long) ino);
3778                                if (!p) {
3779                                        errno = ENOMEM;
3780                                        return -1;
3781                                }
3782                                n = strlen(p);
3783                        } else {
3784                                n = snprintf(buf, bufsize,
3785                                             "%llu",
3786                                             (unsigned long long) ino);
3787                        }
3788                }
3789
3790                if (!determine_size && n > bufsize) {
3791                        errno = ERANGE;
3792                        return -1;
3793                }
3794                buf += n;
3795                n_used += n;
3796                bufsize -= n;
3797
3798                /* Restore name pointer to its original value */
3799                name -= 16;
3800        }
3801
3802        if (n_used == 0) {
3803                errno = ENOATTR;
3804                return -1;
3805        }
3806
3807	return n_used;
3808}
3809
3810
3811/*****************************************************
3812set the ACLs on a file given an ascii description
3813*******************************************************/
3814static int cacl_set(TALLOC_CTX *ctx, struct cli_state *cli,
3815                    struct cli_state *ipc_cli, POLICY_HND *pol,
3816                    const char *filename, const char *the_acl,
3817                    int mode, int flags)
3818{
3819	int fnum;
3820        int err = 0;
3821	SEC_DESC *sd = NULL, *old;
3822        SEC_ACL *dacl = NULL;
3823	DOM_SID *owner_sid = NULL;
3824	DOM_SID *grp_sid = NULL;
3825	uint32 i, j;
3826	size_t sd_size;
3827	int ret = 0;
3828        char *p;
3829        BOOL numeric = True;
3830
3831        /* the_acl will be null for REMOVE_ALL operations */
3832        if (the_acl) {
3833                numeric = ((p = strchr(the_acl, ':')) != NULL &&
3834                           p > the_acl &&
3835                           p[-1] != '+');
3836
3837                /* if this is to set the entire ACL... */
3838                if (*the_acl == '*') {
3839                        /* ... then increment past the first colon */
3840                        the_acl = p + 1;
3841                }
3842
3843                sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
3844
3845                if (!sd) {
3846                        errno = EINVAL;
3847                        return -1;
3848                }
3849        }
3850
3851	/* The desired access below is the only one I could find that works
3852	   with NT4, W2KP and Samba */
3853
3854	fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
3855
3856	if (fnum == -1) {
3857                DEBUG(5, ("cacl_set failed to open %s: %s\n",
3858                          filename, cli_errstr(cli)));
3859                errno = 0;
3860		return -1;
3861	}
3862
3863	old = cli_query_secdesc(cli, fnum, ctx);
3864
3865	if (!old) {
3866                DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
3867                errno = 0;
3868		return -1;
3869	}
3870
3871	cli_close(cli, fnum);
3872
3873	switch (mode) {
3874	case SMBC_XATTR_MODE_REMOVE_ALL:
3875                old->dacl->num_aces = 0;
3876                SAFE_FREE(old->dacl->ace);
3877                SAFE_FREE(old->dacl);
3878                old->off_dacl = 0;
3879                dacl = old->dacl;
3880                break;
3881
3882        case SMBC_XATTR_MODE_REMOVE:
3883		for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
3884			BOOL found = False;
3885
3886			for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
3887                                if (sec_ace_equal(&sd->dacl->ace[i],
3888                                                  &old->dacl->ace[j])) {
3889					uint32 k;
3890					for (k=j; k<old->dacl->num_aces-1;k++) {
3891						old->dacl->ace[k] = old->dacl->ace[k+1];
3892					}
3893					old->dacl->num_aces--;
3894					if (old->dacl->num_aces == 0) {
3895						SAFE_FREE(old->dacl->ace);
3896						SAFE_FREE(old->dacl);
3897						old->off_dacl = 0;
3898					}
3899					found = True;
3900                                        dacl = old->dacl;
3901					break;
3902				}
3903			}
3904
3905			if (!found) {
3906                                err = ENOATTR;
3907                                ret = -1;
3908                                goto failed;
3909			}
3910		}
3911		break;
3912
3913	case SMBC_XATTR_MODE_ADD:
3914		for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
3915			BOOL found = False;
3916
3917			for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
3918				if (sid_equal(&sd->dacl->ace[i].trustee,
3919					      &old->dacl->ace[j].trustee)) {
3920                                        if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
3921                                                err = EEXIST;
3922                                                ret = -1;
3923                                                goto failed;
3924                                        }
3925                                        old->dacl->ace[j] = sd->dacl->ace[i];
3926                                        ret = -1;
3927					found = True;
3928				}
3929			}
3930
3931			if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
3932                                err = ENOATTR;
3933                                ret = -1;
3934                                goto failed;
3935			}
3936
3937                        for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
3938                                add_ace(&old->dacl, &sd->dacl->ace[i], ctx);
3939                        }
3940		}
3941                dacl = old->dacl;
3942		break;
3943
3944	case SMBC_XATTR_MODE_SET:
3945 		old = sd;
3946                owner_sid = old->owner_sid;
3947                grp_sid = old->grp_sid;
3948                dacl = old->dacl;
3949		break;
3950
3951        case SMBC_XATTR_MODE_CHOWN:
3952                owner_sid = sd->owner_sid;
3953                break;
3954
3955        case SMBC_XATTR_MODE_CHGRP:
3956                grp_sid = sd->grp_sid;
3957                break;
3958	}
3959
3960	/* Denied ACE entries must come before allowed ones */
3961	sort_acl(old->dacl);
3962
3963	/* Create new security descriptor and set it */
3964	sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
3965			   owner_sid, grp_sid, NULL, dacl, &sd_size);
3966
3967	fnum = cli_nt_create(cli, filename,
3968                             WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
3969
3970	if (fnum == -1) {
3971		DEBUG(5, ("cacl_set failed to open %s: %s\n",
3972                          filename, cli_errstr(cli)));
3973                errno = 0;
3974		return -1;
3975	}
3976
3977	if (!cli_set_secdesc(cli, fnum, sd)) {
3978		DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
3979		ret = -1;
3980	}
3981
3982	/* Clean up */
3983
3984 failed:
3985	cli_close(cli, fnum);
3986
3987        if (err != 0) {
3988                errno = err;
3989        }
3990
3991	return ret;
3992}
3993
3994
3995int smbc_setxattr_ctx(SMBCCTX *context,
3996                      const char *fname,
3997                      const char *name,
3998                      const void *value,
3999                      size_t size,
4000                      int flags)
4001{
4002        int ret;
4003        int ret2;
4004        SMBCSRV *srv;
4005        SMBCSRV *ipc_srv;
4006	fstring server, share, user, password, workgroup;
4007	pstring path;
4008        TALLOC_CTX *ctx;
4009        POLICY_HND pol;
4010        DOS_ATTR_DESC *dad;
4011
4012	if (!context || !context->internal ||
4013	    !context->internal->_initialized) {
4014
4015		errno = EINVAL;  /* Best I can think of ... */
4016		return -1;
4017
4018	}
4019
4020	if (!fname) {
4021
4022		errno = EINVAL;
4023		return -1;
4024
4025	}
4026
4027	DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n", fname, name, (int) size, (const char*)value));
4028
4029	if (smbc_parse_path(context, fname,
4030                            server, sizeof(server),
4031                            share, sizeof(share),
4032                            path, sizeof(path),
4033                            user, sizeof(user),
4034                            password, sizeof(password),
4035                            NULL, 0)) {
4036                errno = EINVAL;
4037                return -1;
4038        }
4039
4040	if (user[0] == (char)0) fstrcpy(user, context->user);
4041
4042	fstrcpy(workgroup, context->workgroup);
4043
4044	srv = smbc_server(context, server, share, workgroup, user, password);
4045	if (!srv) {
4046		return -1;  /* errno set by smbc_server */
4047	}
4048
4049        if (! srv->no_nt_session) {
4050                ipc_srv = smbc_attr_server(context, server, share,
4051                                           workgroup, user, password,
4052                                           &pol);
4053                srv->no_nt_session = True;
4054        } else {
4055                ipc_srv = NULL;
4056        }
4057
4058        ctx = talloc_init("smbc_setxattr");
4059        if (!ctx) {
4060                errno = ENOMEM;
4061                return -1;
4062        }
4063
4064        /*
4065         * Are they asking to set the entire set of known attributes?
4066         */
4067        if (StrCaseCmp(name, "system.*") == 0 ||
4068            StrCaseCmp(name, "system.*+") == 0) {
4069                /* Yup. */
4070                char *namevalue =
4071                        talloc_asprintf(ctx, "%s:%s", name+7, (const char *) value);
4072                if (! namevalue) {
4073                        errno = ENOMEM;
4074                        ret = -1;
4075                        return -1;
4076                }
4077
4078                if (ipc_srv) {
4079                        ret = cacl_set(ctx, &srv->cli,
4080                                       &ipc_srv->cli, &pol, path,
4081                                       namevalue,
4082                                       (*namevalue == '*'
4083                                        ? SMBC_XATTR_MODE_SET
4084                                        : SMBC_XATTR_MODE_ADD),
4085                                       flags);
4086                } else {
4087                        ret = 0;
4088                }
4089
4090                /* get a DOS Attribute Descriptor with current attributes */
4091                dad = dos_attr_query(context, ctx, path, srv);
4092                if (dad) {
4093                        /* Overwrite old with new, using what was provided */
4094                        dos_attr_parse(context, dad, srv, namevalue);
4095
4096                        /* Set the new DOS attributes */
4097#if 0                           /* not yet implemented */
4098                        if (! cli_setpathinfo(&srv->cli, path,
4099                                              dad->c_time,
4100                                              dad->a_time,
4101                                              dad->m_time,
4102                                              dad->mode)) {
4103                                if (!cli_setatr(&srv->cli, path,
4104                                                dad->mode, dad->m_time)) {
4105                                        errno = smbc_errno(context, &srv->cli);
4106                                }
4107                        }
4108#else
4109                        if (!cli_setatr(&srv->cli, path,
4110                                        dad->mode, dad->m_time)) {
4111                                errno = smbc_errno(context, &srv->cli);
4112                        }
4113#endif
4114                }
4115
4116                /* we only fail if both NT and DOS sets failed */
4117                if (ret < 0 && ! dad) {
4118                        ret = -1; /* in case dad was null */
4119                }
4120                else {
4121                        ret = 0;
4122                }
4123
4124                talloc_destroy(ctx);
4125                return ret;
4126        }
4127
4128        /*
4129         * Are they asking to set an access control element or to set
4130         * the entire access control list?
4131         */
4132        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
4133            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
4134            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
4135            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
4136            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
4137
4138                /* Yup. */
4139                char *namevalue =
4140                        talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4141
4142                if (! ipc_srv) {
4143                        ret = -1; /* errno set by smbc_server() */
4144                }
4145                else if (! namevalue) {
4146                        errno = ENOMEM;
4147                        ret = -1;
4148                } else {
4149                        ret = cacl_set(ctx, &srv->cli,
4150                                       &ipc_srv->cli, &pol, path,
4151                                       namevalue,
4152                                       (*namevalue == '*'
4153                                        ? SMBC_XATTR_MODE_SET
4154                                        : SMBC_XATTR_MODE_ADD),
4155                                       flags);
4156                }
4157                talloc_destroy(ctx);
4158                return ret;
4159        }
4160
4161        /*
4162         * Are they asking to set the owner?
4163         */
4164        if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
4165            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
4166
4167                /* Yup. */
4168                char *namevalue =
4169                        talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4170
4171                if (! ipc_srv) {
4172
4173                        ret = -1; /* errno set by smbc_server() */
4174                }
4175                else if (! namevalue) {
4176                        errno = ENOMEM;
4177                        ret = -1;
4178                } else {
4179                        ret = cacl_set(ctx, &srv->cli,
4180                                       &ipc_srv->cli, &pol, path,
4181                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
4182                }
4183                talloc_destroy(ctx);
4184                return ret;
4185        }
4186
4187        /*
4188         * Are they asking to set the group?
4189         */
4190        if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
4191            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
4192
4193                /* Yup. */
4194                char *namevalue =
4195                        talloc_asprintf(ctx, "%s:%s", name+19, (const char *) value);
4196
4197                if (! ipc_srv) {
4198                        /* errno set by smbc_server() */
4199                        ret = -1;
4200                }
4201                else if (! namevalue) {
4202                        errno = ENOMEM;
4203                        ret = -1;
4204                } else {
4205                        ret = cacl_set(ctx, &srv->cli,
4206                                       &ipc_srv->cli, &pol, path,
4207                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
4208                }
4209                talloc_destroy(ctx);
4210                return ret;
4211        }
4212
4213        /*
4214         * Are they asking to set a DOS attribute?
4215         */
4216        if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
4217            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
4218            StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
4219            StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
4220            StrCaseCmp(name, "system.dos_attr.m_time") == 0) {
4221
4222                /* get a DOS Attribute Descriptor with current attributes */
4223                dad = dos_attr_query(context, ctx, path, srv);
4224                if (dad) {
4225                        char *namevalue =
4226                                talloc_asprintf(ctx, "%s:%s", name+16, (const char *) value);
4227                        if (! namevalue) {
4228                                errno = ENOMEM;
4229                                ret = -1;
4230                        } else {
4231                                /* Overwrite old with provided new params */
4232                                dos_attr_parse(context, dad, srv, namevalue);
4233
4234                                /* Set the new DOS attributes */
4235#if 0                           /* not yet implemented */
4236                                ret2 = cli_setpathinfo(&srv->cli, path,
4237                                                       dad->c_time,
4238                                                       dad->a_time,
4239                                                       dad->m_time,
4240                                                       dad->mode);
4241                                if (! ret2) {
4242                                        ret2 = cli_setatr(&srv->cli, path,
4243                                                          dad->mode,
4244                                                          dad->m_time);
4245                                        if (! ret2) {
4246                                                errno = smbc_errno(context,
4247                                                                   &srv->cli);
4248                                        }
4249                                }
4250#else
4251                                ret2 = cli_setatr(&srv->cli, path,
4252                                                  dad->mode, dad->m_time);
4253                                if (! ret2) {
4254                                        errno = smbc_errno(context, &srv->cli);
4255                                }
4256#endif
4257
4258                                /* ret2 has True (success) / False (failure) */
4259                                if (ret2) {
4260                                        ret = 0;
4261                                } else {
4262                                        ret = -1;
4263                                }
4264                        }
4265                } else {
4266                        ret = -1;
4267                }
4268
4269                talloc_destroy(ctx);
4270                return ret;
4271        }
4272
4273        /* Unsupported attribute name */
4274        talloc_destroy(ctx);
4275        errno = EINVAL;
4276        return -1;
4277}
4278
4279int smbc_getxattr_ctx(SMBCCTX *context,
4280                      const char *fname,
4281                      const char *name,
4282                      const void *value,
4283                      size_t size)
4284{
4285        int ret;
4286        SMBCSRV *srv;
4287        SMBCSRV *ipc_srv;
4288        fstring server, share, user, password, workgroup;
4289        pstring path;
4290        TALLOC_CTX *ctx;
4291        POLICY_HND pol;
4292
4293
4294        if (!context || !context->internal ||
4295            !context->internal->_initialized) {
4296
4297                errno = EINVAL;  /* Best I can think of ... */
4298                return -1;
4299
4300        }
4301
4302        if (!fname) {
4303
4304                errno = EINVAL;
4305                return -1;
4306
4307        }
4308
4309        DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
4310
4311        if (smbc_parse_path(context, fname,
4312                            server, sizeof(server),
4313                            share, sizeof(share),
4314                            path, sizeof(path),
4315                            user, sizeof(user),
4316                            password, sizeof(password),
4317                            NULL, 0)) {
4318                errno = EINVAL;
4319                return -1;
4320        }
4321
4322        if (user[0] == (char)0) fstrcpy(user, context->user);
4323
4324        fstrcpy(workgroup, context->workgroup);
4325
4326        srv = smbc_server(context, server, share, workgroup, user, password);
4327        if (!srv) {
4328                return -1;  /* errno set by smbc_server */
4329        }
4330
4331        if (! srv->no_nt_session) {
4332                ipc_srv = smbc_attr_server(context, server, share,
4333                                           workgroup, user, password,
4334                                           &pol);
4335                if (! ipc_srv) {
4336                        srv->no_nt_session = True;
4337                }
4338        } else {
4339                ipc_srv = NULL;
4340        }
4341
4342        ctx = talloc_init("smbc:getxattr");
4343        if (!ctx) {
4344                errno = ENOMEM;
4345                return -1;
4346        }
4347
4348        /* Are they requesting a supported attribute? */
4349        if (StrCaseCmp(name, "system.*") == 0 ||
4350            StrCaseCmp(name, "system.*+") == 0 ||
4351            StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
4352            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
4353            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
4354            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
4355            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
4356            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
4357            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
4358            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
4359            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
4360            StrCaseCmp(name, "system.dos_attr.*") == 0 ||
4361            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
4362            StrCaseCmp(name, "system.dos_attr.size") == 0 ||
4363            StrCaseCmp(name, "system.dos_attr.c_time") == 0 ||
4364            StrCaseCmp(name, "system.dos_attr.a_time") == 0 ||
4365            StrCaseCmp(name, "system.dos_attr.m_time") == 0 ||
4366            StrCaseCmp(name, "system.dos_attr.inode") == 0) {
4367
4368                /* Yup. */
4369                ret = cacl_get(context, ctx, srv,
4370                               ipc_srv == NULL ? NULL : &ipc_srv->cli,
4371                               &pol, path, name, (const char *) value, size);
4372                if (ret < 0 && errno == 0) {
4373                        errno = smbc_errno(context, &srv->cli);
4374                }
4375                talloc_destroy(ctx);
4376                return ret;
4377        }
4378
4379        /* Unsupported attribute name */
4380        talloc_destroy(ctx);
4381        errno = EINVAL;
4382        return -1;
4383}
4384
4385
4386int smbc_removexattr_ctx(SMBCCTX *context,
4387                      const char *fname,
4388                      const char *name)
4389{
4390        int ret;
4391        SMBCSRV *srv;
4392        SMBCSRV *ipc_srv;
4393        fstring server, share, user, password, workgroup;
4394        pstring path;
4395        TALLOC_CTX *ctx;
4396        POLICY_HND pol;
4397
4398        if (!context || !context->internal ||
4399            !context->internal->_initialized) {
4400
4401                errno = EINVAL;  /* Best I can think of ... */
4402                return -1;
4403
4404        }
4405
4406        if (!fname) {
4407
4408                errno = EINVAL;
4409                return -1;
4410
4411        }
4412
4413        DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
4414
4415        if (smbc_parse_path(context, fname,
4416                            server, sizeof(server),
4417                            share, sizeof(share),
4418                            path, sizeof(path),
4419                            user, sizeof(user),
4420                            password, sizeof(password),
4421                            NULL, 0)) {
4422                errno = EINVAL;
4423                return -1;
4424        }
4425
4426        if (user[0] == (char)0) fstrcpy(user, context->user);
4427
4428        fstrcpy(workgroup, context->workgroup);
4429
4430        srv = smbc_server(context, server, share, workgroup, user, password);
4431        if (!srv) {
4432                return -1;  /* errno set by smbc_server */
4433        }
4434
4435        if (! srv->no_nt_session) {
4436                ipc_srv = smbc_attr_server(context, server, share,
4437                                           workgroup, user, password,
4438                                           &pol);
4439                srv->no_nt_session = True;
4440        } else {
4441                ipc_srv = NULL;
4442        }
4443
4444        if (! ipc_srv) {
4445                return -1; /* errno set by smbc_attr_server */
4446        }
4447
4448        ctx = talloc_init("smbc_removexattr");
4449        if (!ctx) {
4450                errno = ENOMEM;
4451                return -1;
4452        }
4453
4454        /* Are they asking to set the entire ACL? */
4455        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
4456            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
4457
4458                /* Yup. */
4459                ret = cacl_set(ctx, &srv->cli,
4460                               &ipc_srv->cli, &pol, path,
4461                               NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
4462                talloc_destroy(ctx);
4463                return ret;
4464        }
4465
4466        /*
4467         * Are they asking to remove one or more spceific security descriptor
4468         * attributes?
4469         */
4470        if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
4471            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
4472            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
4473            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
4474            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
4475            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
4476            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
4477
4478                /* Yup. */
4479                ret = cacl_set(ctx, &srv->cli,
4480                               &ipc_srv->cli, &pol, path,
4481                               name + 19, SMBC_XATTR_MODE_REMOVE, 0);
4482                talloc_destroy(ctx);
4483                return ret;
4484        }
4485
4486        /* Unsupported attribute name */
4487        talloc_destroy(ctx);
4488        errno = EINVAL;
4489        return -1;
4490}
4491
4492int smbc_listxattr_ctx(SMBCCTX *context,
4493                       const char *fname,
4494                       char *list,
4495                       size_t size)
4496{
4497        /*
4498         * This isn't quite what listxattr() is supposed to do.  This returns
4499         * the complete set of attribute names, always, rather than only those
4500         * attribute names which actually exist for a file.  Hmmm...
4501         */
4502        const char supported[] =
4503                "system.*\0"
4504                "system.*+\0"
4505                "system.nt_sec_desc.revision\0"
4506                "system.nt_sec_desc.owner\0"
4507                "system.nt_sec_desc.owner+\0"
4508                "system.nt_sec_desc.group\0"
4509                "system.nt_sec_desc.group+\0"
4510                "system.nt_sec_desc.acl\0"
4511                "system.nt_sec_desc.acl+\0"
4512                "system.nt_sec_desc.*\0"
4513                "system.nt_sec_desc.*+\0"
4514                "system.dos_attr.*\0"
4515                "system.dos_attr.mode\0"
4516                "system.dos_attr.c_time\0"
4517                "system.dos_attr.a_time\0"
4518                "system.dos_attr.m_time\0"
4519                ;
4520
4521        if (size == 0) {
4522                return sizeof(supported);
4523        }
4524
4525        if (sizeof(supported) > size) {
4526                errno = ERANGE;
4527                return -1;
4528        }
4529
4530        /* this can't be strcpy() because there are embedded null characters */
4531        memcpy(list, supported, sizeof(supported));
4532        return sizeof(supported);
4533}
4534
4535
4536/*
4537 * Open a print file to be written to by other calls
4538 */
4539
4540static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
4541{
4542        fstring server, share, user, password;
4543        pstring path;
4544
4545        if (!context || !context->internal ||
4546            !context->internal->_initialized) {
4547
4548                errno = EINVAL;
4549                return NULL;
4550
4551        }
4552
4553        if (!fname) {
4554
4555                errno = EINVAL;
4556                return NULL;
4557
4558        }
4559
4560        DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
4561
4562        if (smbc_parse_path(context, fname,
4563                            server, sizeof(server),
4564                            share, sizeof(share),
4565                            path, sizeof(path),
4566                            user, sizeof(user),
4567                            password, sizeof(password),
4568                            NULL, 0)) {
4569                errno = EINVAL;
4570                return NULL;
4571        }
4572
4573        /* What if the path is empty, or the file exists? */
4574
4575        return context->open(context, fname, O_WRONLY, 666);
4576
4577}
4578
4579/*
4580 * Routine to print a file on a remote server ...
4581 *
4582 * We open the file, which we assume to be on a remote server, and then
4583 * copy it to a print file on the share specified by printq.
4584 */
4585
4586static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
4587{
4588        SMBCFILE *fid1, *fid2;
4589        int bytes, saverr, tot_bytes = 0;
4590        char buf[4096];
4591
4592        if (!c_file || !c_file->internal->_initialized || !c_print ||
4593            !c_print->internal->_initialized) {
4594
4595                errno = EINVAL;
4596                return -1;
4597
4598        }
4599
4600        if (!fname && !printq) {
4601
4602                errno = EINVAL;
4603                return -1;
4604
4605        }
4606
4607        /* Try to open the file for reading ... */
4608
4609        if ((int)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
4610
4611                DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
4612                return -1;  /* smbc_open sets errno */
4613
4614        }
4615
4616        /* Now, try to open the printer file for writing */
4617
4618        if ((int)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
4619
4620                saverr = errno;  /* Save errno */
4621                c_file->close(c_file, fid1);
4622                errno = saverr;
4623                return -1;
4624
4625        }
4626
4627        while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
4628
4629                tot_bytes += bytes;
4630
4631                if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
4632
4633                        saverr = errno;
4634                        c_file->close(c_file, fid1);
4635                        c_print->close(c_print, fid2);
4636                        errno = saverr;
4637
4638                }
4639
4640        }
4641
4642        saverr = errno;
4643
4644        c_file->close(c_file, fid1);  /* We have to close these anyway */
4645        c_print->close(c_print, fid2);
4646
4647        if (bytes < 0) {
4648
4649                errno = saverr;
4650                return -1;
4651
4652        }
4653
4654        return tot_bytes;
4655
4656}
4657
4658/*
4659 * Routine to list print jobs on a printer share ...
4660 */
4661
4662static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_list_print_job_fn fn)
4663{
4664        SMBCSRV *srv;
4665        fstring server, share, user, password, workgroup;
4666        pstring path;
4667
4668        if (!context || !context->internal ||
4669            !context->internal->_initialized) {
4670
4671                errno = EINVAL;
4672                return -1;
4673
4674        }
4675
4676        if (!fname) {
4677
4678                errno = EINVAL;
4679                return -1;
4680
4681        }
4682
4683        DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
4684
4685        if (smbc_parse_path(context, fname,
4686                            server, sizeof(server),
4687                            share, sizeof(share),
4688                            path, sizeof(path),
4689                            user, sizeof(user),
4690                            password, sizeof(password),
4691                            NULL, 0)) {
4692                errno = EINVAL;
4693                return -1;
4694        }
4695
4696        if (user[0] == (char)0) fstrcpy(user, context->user);
4697
4698        fstrcpy(workgroup, context->workgroup);
4699
4700        srv = smbc_server(context, server, share, workgroup, user, password);
4701
4702        if (!srv) {
4703
4704                return -1;  /* errno set by smbc_server */
4705
4706        }
4707
4708        if (cli_print_queue(&srv->cli, (void (*)(struct print_job_info *))fn) < 0) {
4709
4710                errno = smbc_errno(context, &srv->cli);
4711                return -1;
4712
4713        }
4714
4715        return 0;
4716
4717}
4718
4719/*
4720 * Delete a print job from a remote printer share
4721 */
4722
4723static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
4724{
4725        SMBCSRV *srv;
4726        fstring server, share, user, password, workgroup;
4727        pstring path;
4728        int err;
4729
4730        if (!context || !context->internal ||
4731            !context->internal->_initialized) {
4732
4733                errno = EINVAL;
4734                return -1;
4735
4736        }
4737
4738        if (!fname) {
4739
4740                errno = EINVAL;
4741                return -1;
4742
4743        }
4744
4745        DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
4746
4747        if (smbc_parse_path(context, fname,
4748                            server, sizeof(server),
4749                            share, sizeof(share),
4750                            path, sizeof(path),
4751                            user, sizeof(user),
4752                            password, sizeof(password),
4753                            NULL, 0)) {
4754                errno = EINVAL;
4755                return -1;
4756        }
4757
4758        if (user[0] == (char)0) fstrcpy(user, context->user);
4759
4760        fstrcpy(workgroup, context->workgroup);
4761
4762        srv = smbc_server(context, server, share, workgroup, user, password);
4763
4764        if (!srv) {
4765
4766                return -1;  /* errno set by smbc_server */
4767
4768        }
4769
4770        if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
4771
4772                if (err < 0)
4773                        errno = smbc_errno(context, &srv->cli);
4774                else if (err == ERRnosuchprintjob)
4775                        errno = EINVAL;
4776                return -1;
4777
4778        }
4779
4780        return 0;
4781
4782}
4783
4784/*
4785 * Get a new empty handle to fill in with your own info
4786 */
4787SMBCCTX * smbc_new_context(void)
4788{
4789        SMBCCTX * context;
4790
4791        context = SMB_MALLOC_P(SMBCCTX);
4792        if (!context) {
4793                errno = ENOMEM;
4794                return NULL;
4795        }
4796
4797        ZERO_STRUCTP(context);
4798
4799        context->internal = SMB_MALLOC_P(struct smbc_internal_data);
4800        if (!context->internal) {
4801                errno = ENOMEM;
4802                return NULL;
4803        }
4804
4805        ZERO_STRUCTP(context->internal);
4806
4807
4808        /* ADD REASONABLE DEFAULTS */
4809        context->debug            = 0;
4810        context->timeout          = 20000; /* 20 seconds */
4811
4812	context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
4813	context->options.urlencode_readdir_entries = False;/* backward compat */
4814	context->options.one_share_per_server      = False;/* backward compat */
4815
4816        context->open                              = smbc_open_ctx;
4817        context->creat                             = smbc_creat_ctx;
4818        context->read                              = smbc_read_ctx;
4819        context->write                             = smbc_write_ctx;
4820        context->close                             = smbc_close_ctx;
4821        context->unlink                            = smbc_unlink_ctx;
4822        context->rename                            = smbc_rename_ctx;
4823        context->lseek                             = smbc_lseek_ctx;
4824        context->stat                              = smbc_stat_ctx;
4825        context->fstat                             = smbc_fstat_ctx;
4826        context->opendir                           = smbc_opendir_ctx;
4827        context->closedir                          = smbc_closedir_ctx;
4828        context->readdir                           = smbc_readdir_ctx;
4829        context->getdents                          = smbc_getdents_ctx;
4830        context->mkdir                             = smbc_mkdir_ctx;
4831        context->rmdir                             = smbc_rmdir_ctx;
4832        context->telldir                           = smbc_telldir_ctx;
4833        context->lseekdir                          = smbc_lseekdir_ctx;
4834        context->fstatdir                          = smbc_fstatdir_ctx;
4835        context->chmod                             = smbc_chmod_ctx;
4836        context->utimes                            = smbc_utimes_ctx;
4837        context->setxattr                          = smbc_setxattr_ctx;
4838        context->getxattr                          = smbc_getxattr_ctx;
4839        context->removexattr                       = smbc_removexattr_ctx;
4840        context->listxattr                         = smbc_listxattr_ctx;
4841        context->open_print_job                    = smbc_open_print_job_ctx;
4842        context->print_file                        = smbc_print_file_ctx;
4843        context->list_print_jobs                   = smbc_list_print_jobs_ctx;
4844        context->unlink_print_job                  = smbc_unlink_print_job_ctx;
4845
4846        context->callbacks.check_server_fn         = smbc_check_server;
4847        context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
4848
4849        smbc_default_cache_functions(context);
4850
4851        return context;
4852}
4853
4854/*
4855 * Free a context
4856 *
4857 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
4858 * and thus you'll be leaking memory if not handled properly.
4859 *
4860 */
4861int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
4862{
4863        if (!context) {
4864                errno = EBADF;
4865                return 1;
4866        }
4867
4868        if (shutdown_ctx) {
4869                SMBCFILE * f;
4870                DEBUG(1,("Performing aggressive shutdown.\n"));
4871
4872                f = context->internal->_files;
4873                while (f) {
4874                        context->close(context, f);
4875                        f = f->next;
4876                }
4877                context->internal->_files = NULL;
4878
4879                /* First try to remove the servers the nice way. */
4880                if (context->callbacks.purge_cached_fn(context)) {
4881                        SMBCSRV * s;
4882                        SMBCSRV * next;
4883                        DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
4884                        s = context->internal->_servers;
4885                        while (s) {
4886                                DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
4887                                cli_shutdown(&s->cli);
4888                                context->callbacks.remove_cached_srv_fn(context, s);
4889                                next = s->next;
4890                                DLIST_REMOVE(context->internal->_servers, s);
4891                                SAFE_FREE(s);
4892                                s = next;
4893                        }
4894                        context->internal->_servers = NULL;
4895                }
4896        }
4897        else {
4898                /* This is the polite way */
4899                if (context->callbacks.purge_cached_fn(context)) {
4900                        DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
4901                        errno = EBUSY;
4902                        return 1;
4903                }
4904                if (context->internal->_servers) {
4905                        DEBUG(1, ("Active servers in context, free_context failed.\n"));
4906                        errno = EBUSY;
4907                        return 1;
4908                }
4909                if (context->internal->_files) {
4910                        DEBUG(1, ("Active files in context, free_context failed.\n"));
4911                        errno = EBUSY;
4912                        return 1;
4913                }
4914        }
4915
4916        /* Things we have to clean up */
4917        SAFE_FREE(context->workgroup);
4918        SAFE_FREE(context->netbios_name);
4919        SAFE_FREE(context->user);
4920
4921        DEBUG(3, ("Context %p succesfully freed\n", context));
4922        SAFE_FREE(context->internal);
4923        SAFE_FREE(context);
4924        return 0;
4925}
4926
4927
4928/*
4929 * Initialise the library etc
4930 *
4931 * We accept a struct containing handle information.
4932 * valid values for info->debug from 0 to 100,
4933 * and insist that info->fn must be non-null.
4934 */
4935SMBCCTX * smbc_init_context(SMBCCTX * context)
4936{
4937        pstring conf;
4938        int pid;
4939        char *user = NULL, *home = NULL;
4940
4941        if (!context || !context->internal) {
4942                errno = EBADF;
4943                return NULL;
4944        }
4945
4946        /* Do not initialise the same client twice */
4947        if (context->internal->_initialized) {
4948                return 0;
4949        }
4950
4951        if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
4952
4953                errno = EINVAL;
4954                return NULL;
4955
4956        }
4957
4958        if (!smbc_initialized) {
4959                /* Do some library wide intialisations the first time we get called */
4960
4961                /* Set this to what the user wants */
4962                DEBUGLEVEL = context->debug;
4963
4964                setup_logging( "libsmbclient", True);
4965
4966                /* Here we would open the smb.conf file if needed ... */
4967
4968                home = getenv("HOME");
4969
4970                slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
4971
4972                load_interfaces();  /* Load the list of interfaces ... */
4973
4974                in_client = True; /* FIXME, make a param */
4975
4976                if (!lp_load(conf, True, False, False)) {
4977
4978                        /*
4979                         * Well, if that failed, try the dyn_CONFIGFILE
4980                         * Which points to the standard locn, and if that
4981                         * fails, silently ignore it and use the internal
4982                         * defaults ...
4983                         */
4984
4985                   if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
4986                      DEBUG(5, ("Could not load either config file: %s or %s\n",
4987                             conf, dyn_CONFIGFILE));
4988                   }
4989                }
4990
4991                reopen_logs();  /* Get logging working ... */
4992
4993                /*
4994                 * Block SIGPIPE (from lib/util_sock.c: write())
4995                 * It is not needed and should not stop execution
4996                 */
4997                BlockSignals(True, SIGPIPE);
4998
4999                /* Done with one-time initialisation */
5000                smbc_initialized = 1;
5001
5002        }
5003
5004        if (!context->user) {
5005                /*
5006                 * FIXME: Is this the best way to get the user info?
5007                 */
5008                user = getenv("USER");
5009                /* walk around as "guest" if no username can be found */
5010                if (!user) context->user = SMB_STRDUP("guest");
5011                else context->user = SMB_STRDUP(user);
5012        }
5013
5014        if (!context->netbios_name) {
5015                /*
5016                 * We try to get our netbios name from the config. If that fails we fall
5017                 * back on constructing our netbios name from our hostname etc
5018                 */
5019                if (global_myname()) {
5020                        context->netbios_name = SMB_STRDUP(global_myname());
5021                }
5022                else {
5023                        /*
5024                         * Hmmm, I want to get hostname as well, but I am too lazy for the moment
5025                         */
5026                        pid = sys_getpid();
5027                        context->netbios_name = SMB_MALLOC(17);
5028                        if (!context->netbios_name) {
5029                                errno = ENOMEM;
5030                                return NULL;
5031                        }
5032                        slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
5033                }
5034        }
5035
5036        DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
5037
5038        if (!context->workgroup) {
5039                if (lp_workgroup()) {
5040                        context->workgroup = SMB_STRDUP(lp_workgroup());
5041                }
5042                else {
5043                        /* TODO: Think about a decent default workgroup */
5044                        context->workgroup = SMB_STRDUP("samba");
5045                }
5046        }
5047
5048        DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
5049
5050        /* shortest timeout is 1 second */
5051        if (context->timeout > 0 && context->timeout < 1000)
5052                context->timeout = 1000;
5053
5054        /*
5055         * FIXME: Should we check the function pointers here?
5056         */
5057
5058        context->internal->_initialized = 1;
5059
5060        return context;
5061}
5062
5063
5064/* Return the verion of samba, and thus libsmbclient */
5065const char *
5066smbc_version(void)
5067{
5068        return samba_version_string();
5069}
5070