• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.0.25b/source/libsmb/
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
29struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir);
30struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list,
31					 struct smbc_dirent *dirent);
32
33/*
34 * DOS Attribute values (used internally)
35 */
36typedef struct DOS_ATTR_DESC {
37	int mode;
38	SMB_OFF_T size;
39	time_t create_time;
40	time_t access_time;
41	time_t write_time;
42	time_t change_time;
43	SMB_INO_T inode;
44} DOS_ATTR_DESC;
45
46
47/*
48 * Internal flags for extended attributes
49 */
50
51/* internal mode values */
52#define SMBC_XATTR_MODE_ADD          1
53#define SMBC_XATTR_MODE_REMOVE       2
54#define SMBC_XATTR_MODE_REMOVE_ALL   3
55#define SMBC_XATTR_MODE_SET          4
56#define SMBC_XATTR_MODE_CHOWN        5
57#define SMBC_XATTR_MODE_CHGRP        6
58
59#define CREATE_ACCESS_READ      READ_CONTROL_ACCESS
60
61/*We should test for this in configure ... */
62#ifndef ENOTSUP
63#define ENOTSUP EOPNOTSUPP
64#endif
65
66/*
67 * Functions exported by libsmb_cache.c that we need here
68 */
69int smbc_default_cache_functions(SMBCCTX *context);
70
71/*
72 * check if an element is part of the list.
73 * FIXME: Does not belong here !
74 * Can anyone put this in a macro in dlinklist.h ?
75 * -- Tom
76 */
77static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
78	if (!p || !list) return False;
79	do {
80		if (p == list) return True;
81		list = list->next;
82	} while (list);
83	return False;
84}
85
86/*
87 * Find an lsa pipe handle associated with a cli struct.
88 */
89static struct rpc_pipe_client *
90find_lsa_pipe_hnd(struct cli_state *ipc_cli)
91{
92	struct rpc_pipe_client *pipe_hnd;
93
94	for (pipe_hnd = ipc_cli->pipe_list;
95             pipe_hnd;
96             pipe_hnd = pipe_hnd->next) {
97
98		if (pipe_hnd->pipe_idx == PI_LSARPC) {
99			return pipe_hnd;
100		}
101	}
102
103	return NULL;
104}
105
106static int
107smbc_close_ctx(SMBCCTX *context,
108               SMBCFILE *file);
109static off_t
110smbc_lseek_ctx(SMBCCTX *context,
111               SMBCFILE *file,
112               off_t offset,
113               int whence);
114
115extern BOOL in_client;
116
117/*
118 * Is the logging working / configfile read ?
119 */
120static int smbc_initialized = 0;
121
122static int
123hex2int( unsigned int _char )
124{
125    if ( _char >= 'A' && _char <='F')
126	return _char - 'A' + 10;
127    if ( _char >= 'a' && _char <='f')
128	return _char - 'a' + 10;
129    if ( _char >= '0' && _char <='9')
130	return _char - '0';
131    return -1;
132}
133
134/*
135 * smbc_urldecode()
136 *
137 * Convert strings of %xx to their single character equivalent.  Each 'x' must
138 * be a valid hexadecimal digit, or that % sequence is left undecoded.
139 *
140 * dest may, but need not be, the same pointer as src.
141 *
142 * Returns the number of % sequences which could not be converted due to lack
143 * of two following hexadecimal digits.
144 */
145int
146smbc_urldecode(char *dest, char * src, size_t max_dest_len)
147{
148        int old_length = strlen(src);
149        int i = 0;
150        int err_count = 0;
151        pstring temp;
152        char * p;
153
154        if ( old_length == 0 ) {
155                return 0;
156        }
157
158        p = temp;
159        while ( i < old_length ) {
160                unsigned char character = src[ i++ ];
161
162                if (character == '%') {
163                        int a = i+1 < old_length ? hex2int( src[i] ) : -1;
164                        int b = i+1 < old_length ? hex2int( src[i+1] ) : -1;
165
166                        /* Replace valid sequence */
167                        if (a != -1 && b != -1) {
168
169                                /* Replace valid %xx sequence with %dd */
170                                character = (a * 16) + b;
171
172                                if (character == '\0') {
173                                        break; /* Stop at %00 */
174                                }
175
176                                i += 2;
177                        } else {
178
179                                err_count++;
180                        }
181                }
182
183                *p++ = character;
184        }
185
186        *p = '\0';
187
188        strncpy(dest, temp, max_dest_len - 1);
189        dest[max_dest_len - 1] = '\0';
190
191        return err_count;
192}
193
194/*
195 * smbc_urlencode()
196 *
197 * Convert any characters not specifically allowed in a URL into their %xx
198 * equivalent.
199 *
200 * Returns the remaining buffer length.
201 */
202int
203smbc_urlencode(char * dest, char * src, int max_dest_len)
204{
205        char hex[] = "0123456789ABCDEF";
206
207        for (; *src != '\0' && max_dest_len >= 3; src++) {
208
209                if ((*src < '0' &&
210                     *src != '-' &&
211                     *src != '.') ||
212                    (*src > '9' &&
213                     *src < 'A') ||
214                    (*src > 'Z' &&
215                     *src < 'a' &&
216                     *src != '_') ||
217                    (*src > 'z')) {
218                        *dest++ = '%';
219                        *dest++ = hex[(*src >> 4) & 0x0f];
220                        *dest++ = hex[*src & 0x0f];
221                        max_dest_len -= 3;
222                } else {
223                        *dest++ = *src;
224                        max_dest_len--;
225                }
226        }
227
228        *dest++ = '\0';
229        max_dest_len--;
230
231        return max_dest_len;
232}
233
234/*
235 * Function to parse a path and turn it into components
236 *
237 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
238 * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
239 * general format ("smb:" only; we do not look for "cifs:").
240 *
241 *
242 * We accept:
243 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
244 *
245 * Meaning of URLs:
246 *
247 * smb://           Show all workgroups.
248 *
249 *                  The method of locating the list of workgroups varies
250 *                  depending upon the setting of the context variable
251 *                  context->options.browse_max_lmb_count.  This value
252 *                  determine the maximum number of local master browsers to
253 *                  query for the list of workgroups.  In order to ensure that
254 *                  a complete list of workgroups is obtained, all master
255 *                  browsers must be queried, but if there are many
256 *                  workgroups, the time spent querying can begin to add up.
257 *                  For small networks (not many workgroups), it is suggested
258 *                  that this variable be set to 0, indicating query all local
259 *                  master browsers.  When the network has many workgroups, a
260 *                  reasonable setting for this variable might be around 3.
261 *
262 * smb://name/      if name<1D> or name<1B> exists, list servers in
263 *                  workgroup, else, if name<20> exists, list all shares
264 *                  for server ...
265 *
266 * If "options" are provided, this function returns the entire option list as a
267 * string, for later parsing by the caller.  Note that currently, no options
268 * are supported.
269 */
270
271static const char *smbc_prefix = "smb:";
272
273static int
274smbc_parse_path(SMBCCTX *context,
275                const char *fname,
276                char *workgroup, int workgroup_len,
277                char *server, int server_len,
278                char *share, int share_len,
279                char *path, int path_len,
280		char *user, int user_len,
281                char *password, int password_len,
282                char *options, int options_len)
283{
284	static pstring s;
285	pstring userinfo;
286	const char *p;
287	char *q, *r;
288	int len;
289
290	server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
291
292        /*
293         * Assume we wont find an authentication domain to parse, so default
294         * to the workgroup in the provided context.
295         */
296        if (workgroup != NULL) {
297                strncpy(workgroup, context->workgroup, workgroup_len - 1);
298                workgroup[workgroup_len - 1] = '\0';
299        }
300
301        if (options != NULL && options_len > 0) {
302                options[0] = (char)0;
303        }
304	pstrcpy(s, fname);
305
306	/* see if it has the right prefix */
307	len = strlen(smbc_prefix);
308	if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
309                return -1; /* What about no smb: ? */
310        }
311
312	p = s + len;
313
314	/* Watch the test below, we are testing to see if we should exit */
315
316	if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
317
318                DEBUG(1, ("Invalid path (does not begin with smb://"));
319		return -1;
320
321	}
322
323	p += 2;  /* Skip the double slash */
324
325        /* See if any options were specified */
326        if ((q = strrchr(p, '?')) != NULL ) {
327                /* There are options.  Null terminate here and point to them */
328                *q++ = '\0';
329
330                DEBUG(4, ("Found options '%s'", q));
331
332                /* Copy the options */
333                if (options != NULL && options_len > 0) {
334                        safe_strcpy(options, q, options_len - 1);
335                }
336        }
337
338	if (*p == (char)0)
339	    goto decoding;
340
341	if (*p == '/') {
342		int wl = strlen(context->workgroup);
343
344		if (wl > 16) {
345			wl = 16;
346		}
347
348		strncpy(server, context->workgroup, wl);
349                server[wl] = '\0';
350		return 0;
351	}
352
353	/*
354	 * ok, its for us. Now parse out the server, share etc.
355	 *
356	 * However, we want to parse out [[domain;]user[:password]@] if it
357	 * exists ...
358	 */
359
360	/* check that '@' occurs before '/', if '/' exists at all */
361	q = strchr_m(p, '@');
362	r = strchr_m(p, '/');
363	if (q && (!r || q < r)) {
364		pstring username, passwd, domain;
365		const char *u = userinfo;
366
367		next_token_no_ltrim(&p, userinfo, "@", sizeof(fstring));
368
369		username[0] = passwd[0] = domain[0] = 0;
370
371		if (strchr_m(u, ';')) {
372
373			next_token_no_ltrim(&u, domain, ";", sizeof(fstring));
374
375		}
376
377		if (strchr_m(u, ':')) {
378
379			next_token_no_ltrim(&u, username, ":", sizeof(fstring));
380
381			pstrcpy(passwd, u);
382
383		}
384		else {
385
386			pstrcpy(username, u);
387
388		}
389
390                if (domain[0] && workgroup) {
391                        strncpy(workgroup, domain, workgroup_len - 1);
392                        workgroup[workgroup_len - 1] = '\0';
393                }
394
395		if (username[0]) {
396			strncpy(user, username, user_len - 1);
397                        user[user_len - 1] = '\0';
398                }
399
400		if (passwd[0]) {
401			strncpy(password, passwd, password_len - 1);
402                        password[password_len - 1] = '\0';
403                }
404
405	}
406
407	if (!next_token(&p, server, "/", sizeof(fstring))) {
408
409		return -1;
410
411	}
412
413	if (*p == (char)0) goto decoding;  /* That's it ... */
414
415	if (!next_token(&p, share, "/", sizeof(fstring))) {
416
417		return -1;
418
419	}
420
421        /*
422         * Prepend a leading slash if there's a file path, as required by
423         * NetApp filers.
424         */
425        *path = '\0';
426        if (*p != '\0') {
427                *path = '/';
428                safe_strcpy(path + 1, p, path_len - 2);
429        }
430
431	all_string_sub(path, "/", "\\", 0);
432
433 decoding:
434	(void) smbc_urldecode(path, path, path_len);
435	(void) smbc_urldecode(server, server, server_len);
436	(void) smbc_urldecode(share, share, share_len);
437	(void) smbc_urldecode(user, user, user_len);
438	(void) smbc_urldecode(password, password, password_len);
439
440	return 0;
441}
442
443/*
444 * Verify that the options specified in a URL are valid
445 */
446static int
447smbc_check_options(char *server,
448                   char *share,
449                   char *path,
450                   char *options)
451{
452        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' "
453                  "path='%s' options='%s'\n",
454                  server, share, path, options));
455
456        /* No options at all is always ok */
457        if (! *options) return 0;
458
459        /* Currently, we don't support any options. */
460        return -1;
461}
462
463/*
464 * Convert an SMB error into a UNIX error ...
465 */
466static int
467smbc_errno(SMBCCTX *context,
468           struct cli_state *c)
469{
470	int ret = cli_errno(c);
471
472        if (cli_is_dos_error(c)) {
473                uint8 eclass;
474                uint32 ecode;
475
476                cli_dos_error(c, &eclass, &ecode);
477
478                DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n",
479                         (int)eclass, (int)ecode, (int)ecode, ret));
480        } else {
481                NTSTATUS status;
482
483                status = cli_nt_error(c);
484
485                DEBUG(3,("smbc errno %s -> %d\n",
486                         nt_errstr(status), ret));
487        }
488
489	return ret;
490}
491
492/*
493 * Check a server for being alive and well.
494 * returns 0 if the server is in shape. Returns 1 on error
495 *
496 * Also useable outside libsmbclient to enable external cache
497 * to do some checks too.
498 */
499static int
500smbc_check_server(SMBCCTX * context,
501                  SMBCSRV * server)
502{
503        socklen_t size;
504        struct sockaddr addr;
505
506        /*
507         * Although the use of port 139 is not a guarantee that we're using
508         * netbios, we assume so.  We don't want to send a keepalive packet if
509         * not netbios because it's not valid, and Vista, at least,
510         * disconnects the client on such a request.
511         */
512        if (server->cli->port == 139) {
513                /* Assuming netbios.  Send a keepalive packet */
514                if ( send_keepalive(server->cli->fd) == False ) {
515                        return 1;
516                }
517        } else {
518                /*
519                 * Assuming not netbios.  Try a different method to detect if
520                 * the connection is still alive.
521                 */
522                size = sizeof(addr);
523                if (getpeername(server->cli->fd, &addr, &size) == -1) {
524                        return 1;
525                }
526        }
527
528	/* connection is ok */
529	return 0;
530}
531
532/*
533 * Remove a server from the cached server list it's unused.
534 * On success, 0 is returned. 1 is returned if the server could not be removed.
535 *
536 * Also useable outside libsmbclient
537 */
538int
539smbc_remove_unused_server(SMBCCTX * context,
540                          SMBCSRV * srv)
541{
542	SMBCFILE * file;
543
544	/* are we being fooled ? */
545	if (!context || !context->internal ||
546	    !context->internal->_initialized || !srv) return 1;
547
548
549	/* Check all open files/directories for a relation with this server */
550	for (file = context->internal->_files; file; file=file->next) {
551		if (file->srv == srv) {
552			/* Still used */
553			DEBUG(3, ("smbc_remove_usused_server: "
554                                  "%p still used by %p.\n",
555				  srv, file));
556			return 1;
557		}
558	}
559
560	DLIST_REMOVE(context->internal->_servers, srv);
561
562	cli_shutdown(srv->cli);
563	srv->cli = NULL;
564
565	DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
566
567	context->callbacks.remove_cached_srv_fn(context, srv);
568
569        SAFE_FREE(srv);
570
571	return 0;
572}
573
574static SMBCSRV *
575find_server(SMBCCTX *context,
576            const char *server,
577            const char *share,
578            fstring workgroup,
579            fstring username,
580            fstring password)
581{
582        SMBCSRV *srv;
583        int auth_called = 0;
584
585 check_server_cache:
586
587	srv = context->callbacks.get_cached_srv_fn(context, server, share,
588						   workgroup, username);
589
590	if (!auth_called && !srv && (!username[0] || !password[0])) {
591                if (context->internal->_auth_fn_with_context != NULL) {
592                         context->internal->_auth_fn_with_context(
593                                context,
594                                server, share,
595                                workgroup, sizeof(fstring),
596                                username, sizeof(fstring),
597                                password, sizeof(fstring));
598                } else {
599                        context->callbacks.auth_fn(
600                                server, share,
601                                workgroup, sizeof(fstring),
602                                username, sizeof(fstring),
603                                password, sizeof(fstring));
604                }
605
606		/*
607                 * However, smbc_auth_fn may have picked up info relating to
608                 * an existing connection, so try for an existing connection
609                 * again ...
610                 */
611		auth_called = 1;
612		goto check_server_cache;
613
614	}
615
616	if (srv) {
617		if (context->callbacks.check_server_fn(context, srv)) {
618			/*
619                         * This server is no good anymore
620                         * Try to remove it and check for more possible
621                         * servers in the cache
622                         */
623			if (context->callbacks.remove_unused_server_fn(context,
624                                                                       srv)) {
625                                /*
626                                 * We could not remove the server completely,
627                                 * remove it from the cache so we will not get
628                                 * it again. It will be removed when the last
629                                 * file/dir is closed.
630                                 */
631				context->callbacks.remove_cached_srv_fn(context,
632                                                                        srv);
633			}
634
635			/*
636                         * Maybe there are more cached connections to this
637                         * server
638                         */
639			goto check_server_cache;
640		}
641
642		return srv;
643 	}
644
645        return NULL;
646}
647
648/*
649 * Connect to a server, possibly on an existing connection
650 *
651 * Here, what we want to do is: If the server and username
652 * match an existing connection, reuse that, otherwise, establish a
653 * new connection.
654 *
655 * If we have to create a new connection, call the auth_fn to get the
656 * info we need, unless the username and password were passed in.
657 */
658
659static SMBCSRV *
660smbc_server(SMBCCTX *context,
661            BOOL connect_if_not_found,
662            const char *server,
663            const char *share,
664            fstring workgroup,
665            fstring username,
666            fstring password)
667{
668	SMBCSRV *srv=NULL;
669	struct cli_state *c;
670	struct nmb_name called, calling;
671	const char *server_n = server;
672	pstring ipenv;
673	struct in_addr ip;
674	int tried_reverse = 0;
675        int port_try_first;
676        int port_try_next;
677        const char *username_used;
678
679	zero_ip(&ip);
680	ZERO_STRUCT(c);
681
682	if (server[0] == 0) {
683		errno = EPERM;
684		return NULL;
685	}
686
687        /* Look for a cached connection */
688        srv = find_server(context, server, share,
689                          workgroup, username, password);
690
691        /*
692         * If we found a connection and we're only allowed one share per
693         * server...
694         */
695        if (srv && *share != '\0' && context->options.one_share_per_server) {
696
697                /*
698                 * ... then if there's no current connection to the share,
699                 * connect to it.  find_server(), or rather the function
700                 * pointed to by context->callbacks.get_cached_srv_fn which
701                 * was called by find_server(), will have issued a tree
702                 * disconnect if the requested share is not the same as the
703                 * one that was already connected.
704                 */
705                if (srv->cli->cnum == (uint16) -1) {
706                        /* Ensure we have accurate auth info */
707                        if (context->internal->_auth_fn_with_context != NULL) {
708                                context->internal->_auth_fn_with_context(
709                                        context,
710                                        server, share,
711                                        workgroup, sizeof(fstring),
712                                        username, sizeof(fstring),
713                                        password, sizeof(fstring));
714                        } else {
715                                context->callbacks.auth_fn(
716                                        server, share,
717                                        workgroup, sizeof(fstring),
718                                        username, sizeof(fstring),
719                                        password, sizeof(fstring));
720                        }
721
722                        if (! cli_send_tconX(srv->cli, share, "?????",
723                                             password, strlen(password)+1)) {
724
725                                errno = smbc_errno(context, srv->cli);
726                                cli_shutdown(srv->cli);
727				srv->cli = NULL;
728                                context->callbacks.remove_cached_srv_fn(context,
729                                                                        srv);
730                                srv = NULL;
731                        }
732
733                        /*
734                         * Regenerate the dev value since it's based on both
735                         * server and share
736                         */
737                        if (srv) {
738                                srv->dev = (dev_t)(str_checksum(server) ^
739                                                   str_checksum(share));
740                        }
741                }
742        }
743
744        /* If we have a connection... */
745        if (srv) {
746
747                /* ... then we're done here.  Give 'em what they came for. */
748                return srv;
749        }
750
751        /* If we're not asked to connect when a connection doesn't exist... */
752        if (! connect_if_not_found) {
753                /* ... then we're done here. */
754                return NULL;
755        }
756
757	make_nmb_name(&calling, context->netbios_name, 0x0);
758	make_nmb_name(&called , server, 0x20);
759
760	DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
761
762	DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
763
764 again:
765	slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
766
767	zero_ip(&ip);
768
769	/* have to open a new connection */
770	if ((c = cli_initialise()) == NULL) {
771		errno = ENOMEM;
772		return NULL;
773	}
774
775	if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
776		c->use_kerberos = True;
777	}
778	if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) {
779		c->fallback_after_kerberos = True;
780	}
781
782	c->timeout = context->timeout;
783
784        /*
785         * Force use of port 139 for first try if share is $IPC, empty, or
786         * null, so browse lists can work
787         */
788        if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
789                port_try_first = 139;
790                port_try_next = 445;
791        } else {
792                port_try_first = 445;
793                port_try_next = 139;
794        }
795
796        c->port = port_try_first;
797
798	if (!cli_connect(c, server_n, &ip)) {
799
800                /* First connection attempt failed.  Try alternate port. */
801                c->port = port_try_next;
802
803                if (!cli_connect(c, server_n, &ip)) {
804                        cli_shutdown(c);
805                        errno = ETIMEDOUT;
806                        return NULL;
807                }
808 	}
809
810	if (!cli_session_request(c, &calling, &called)) {
811		cli_shutdown(c);
812		if (strcmp(called.name, "*SMBSERVER")) {
813			make_nmb_name(&called , "*SMBSERVER", 0x20);
814			goto again;
815		} else {  /* Try one more time, but ensure we don't loop */
816
817			/* Only try this if server is an IP address ... */
818
819			if (is_ipaddress(server) && !tried_reverse) {
820				fstring remote_name;
821				struct in_addr rem_ip;
822
823				if ((rem_ip.s_addr=inet_addr(server)) == INADDR_NONE) {
824					DEBUG(4, ("Could not convert IP address "
825						"%s to struct in_addr\n", server));
826					errno = ETIMEDOUT;
827					return NULL;
828				}
829
830				tried_reverse++; /* Yuck */
831
832				if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
833					make_nmb_name(&called, remote_name, 0x20);
834					goto again;
835				}
836			}
837		}
838		errno = ETIMEDOUT;
839		return NULL;
840	}
841
842	DEBUG(4,(" session request ok\n"));
843
844	if (!cli_negprot(c)) {
845		cli_shutdown(c);
846		errno = ETIMEDOUT;
847		return NULL;
848	}
849
850        username_used = username;
851
852	if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
853					       password, strlen(password),
854					       password, strlen(password),
855					       workgroup))) {
856
857                /* Failed.  Try an anonymous login, if allowed by flags. */
858                username_used = "";
859
860                if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) ||
861                     !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
862							password, 1,
863							password, 0,
864							workgroup))) {
865
866                        cli_shutdown(c);
867                        errno = EPERM;
868                        return NULL;
869                }
870	}
871
872	DEBUG(4,(" session setup ok\n"));
873
874	if (!cli_send_tconX(c, share, "?????",
875			    password, strlen(password)+1)) {
876		errno = smbc_errno(context, c);
877		cli_shutdown(c);
878		return NULL;
879	}
880
881	DEBUG(4,(" tconx ok\n"));
882
883	/*
884	 * Ok, we have got a nice connection
885	 * Let's allocate a server structure.
886	 */
887
888	srv = SMB_MALLOC_P(SMBCSRV);
889	if (!srv) {
890		errno = ENOMEM;
891		goto failed;
892	}
893
894	ZERO_STRUCTP(srv);
895	srv->cli = c;
896	srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
897        srv->no_pathinfo = False;
898        srv->no_pathinfo2 = False;
899        srv->no_nt_session = False;
900
901	/* now add it to the cache (internal or external)  */
902	/* Let the cache function set errno if it wants to */
903	errno = 0;
904	if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
905		int saved_errno = errno;
906		DEBUG(3, (" Failed to add server to cache\n"));
907		errno = saved_errno;
908		if (errno == 0) {
909			errno = ENOMEM;
910		}
911		goto failed;
912	}
913
914	DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
915		  server, share, srv));
916
917	DLIST_ADD(context->internal->_servers, srv);
918	return srv;
919
920 failed:
921	cli_shutdown(c);
922	if (!srv) {
923		return NULL;
924	}
925
926	SAFE_FREE(srv);
927	return NULL;
928}
929
930/*
931 * Connect to a server for getting/setting attributes, possibly on an existing
932 * connection.  This works similarly to smbc_server().
933 */
934SMBCSRV *
935smbc_attr_server(SMBCCTX *context,
936                 const char *server,
937                 const char *share,
938                 fstring workgroup,
939                 fstring username,
940                 fstring password,
941                 POLICY_HND *pol)
942{
943        int flags;
944        struct in_addr ip;
945	struct cli_state *ipc_cli;
946	struct rpc_pipe_client *pipe_hnd;
947        NTSTATUS nt_status;
948	SMBCSRV *ipc_srv=NULL;
949
950        /*
951         * See if we've already created this special connection.  Reference
952         * our "special" share name '*IPC$', which is an impossible real share
953         * name due to the leading asterisk.
954         */
955        ipc_srv = find_server(context, server, "*IPC$",
956                              workgroup, username, password);
957        if (!ipc_srv) {
958
959                /* We didn't find a cached connection.  Get the password */
960                if (*password == '\0') {
961                        /* ... then retrieve it now. */
962                        if (context->internal->_auth_fn_with_context != NULL) {
963                                context->internal->_auth_fn_with_context(
964                                        context,
965                                        server, share,
966                                        workgroup, sizeof(fstring),
967                                        username, sizeof(fstring),
968                                        password, sizeof(fstring));
969                        } else {
970                                context->callbacks.auth_fn(
971                                        server, share,
972                                        workgroup, sizeof(fstring),
973                                        username, sizeof(fstring),
974                                        password, sizeof(fstring));
975                        }
976                }
977
978                flags = 0;
979                if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) {
980                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
981                }
982
983                zero_ip(&ip);
984                nt_status = cli_full_connection(&ipc_cli,
985                                                global_myname(), server,
986                                                &ip, 0, "IPC$", "?????",
987                                                username, workgroup,
988                                                password, flags,
989                                                Undefined, NULL);
990                if (! NT_STATUS_IS_OK(nt_status)) {
991                        DEBUG(1,("cli_full_connection failed! (%s)\n",
992                                 nt_errstr(nt_status)));
993                        errno = ENOTSUP;
994                        return NULL;
995                }
996
997                ipc_srv = SMB_MALLOC_P(SMBCSRV);
998                if (!ipc_srv) {
999                        errno = ENOMEM;
1000                        cli_shutdown(ipc_cli);
1001                        return NULL;
1002                }
1003
1004                ZERO_STRUCTP(ipc_srv);
1005                ipc_srv->cli = ipc_cli;
1006
1007                if (pol) {
1008                        pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli,
1009                                                            PI_LSARPC,
1010                                                            &nt_status);
1011                        if (!pipe_hnd) {
1012                                DEBUG(1, ("cli_nt_session_open fail!\n"));
1013                                errno = ENOTSUP;
1014                                cli_shutdown(ipc_srv->cli);
1015                                free(ipc_srv);
1016                                return NULL;
1017                        }
1018
1019                        /*
1020                         * Some systems don't support
1021                         * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
1022                         * so we might as well do it too.
1023                         */
1024
1025                        nt_status = rpccli_lsa_open_policy(
1026                                pipe_hnd,
1027                                ipc_srv->cli->mem_ctx,
1028                                True,
1029                                GENERIC_EXECUTE_ACCESS,
1030                                pol);
1031
1032                        if (!NT_STATUS_IS_OK(nt_status)) {
1033                                errno = smbc_errno(context, ipc_srv->cli);
1034                                cli_shutdown(ipc_srv->cli);
1035                                return NULL;
1036                        }
1037                }
1038
1039                /* now add it to the cache (internal or external) */
1040
1041                errno = 0;      /* let cache function set errno if it likes */
1042                if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
1043                                                         server,
1044                                                         "*IPC$",
1045                                                         workgroup,
1046                                                         username)) {
1047                        DEBUG(3, (" Failed to add server to cache\n"));
1048                        if (errno == 0) {
1049                                errno = ENOMEM;
1050                        }
1051                        cli_shutdown(ipc_srv->cli);
1052                        free(ipc_srv);
1053                        return NULL;
1054                }
1055
1056                DLIST_ADD(context->internal->_servers, ipc_srv);
1057        }
1058
1059        return ipc_srv;
1060}
1061
1062/*
1063 * Routine to open() a file ...
1064 */
1065
1066static SMBCFILE *
1067smbc_open_ctx(SMBCCTX *context,
1068              const char *fname,
1069              int flags,
1070              mode_t mode)
1071{
1072	fstring server, share, user, password, workgroup;
1073	pstring path;
1074        pstring targetpath;
1075	struct cli_state *targetcli;
1076	SMBCSRV *srv   = NULL;
1077	SMBCFILE *file = NULL;
1078	int fd;
1079
1080	if (!context || !context->internal ||
1081	    !context->internal->_initialized) {
1082
1083		errno = EINVAL;  /* Best I can think of ... */
1084		return NULL;
1085
1086	}
1087
1088	if (!fname) {
1089
1090		errno = EINVAL;
1091		return NULL;
1092
1093	}
1094
1095	if (smbc_parse_path(context, fname,
1096                            workgroup, sizeof(workgroup),
1097                            server, sizeof(server),
1098                            share, sizeof(share),
1099                            path, sizeof(path),
1100                            user, sizeof(user),
1101                            password, sizeof(password),
1102                            NULL, 0)) {
1103                errno = EINVAL;
1104                return NULL;
1105        }
1106
1107	if (user[0] == (char)0) fstrcpy(user, context->user);
1108
1109	srv = smbc_server(context, True,
1110                          server, share, workgroup, user, password);
1111
1112	if (!srv) {
1113
1114		if (errno == EPERM) errno = EACCES;
1115		return NULL;  /* smbc_server sets errno */
1116
1117	}
1118
1119	/* Hmmm, the test for a directory is suspect here ... FIXME */
1120
1121	if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
1122
1123		fd = -1;
1124
1125	}
1126	else {
1127
1128		file = SMB_MALLOC_P(SMBCFILE);
1129
1130		if (!file) {
1131
1132			errno = ENOMEM;
1133			return NULL;
1134
1135		}
1136
1137		ZERO_STRUCTP(file);
1138
1139		/*d_printf(">>>open: resolving %s\n", path);*/
1140		if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1141		{
1142			d_printf("Could not resolve %s\n", path);
1143			SAFE_FREE(file);
1144			return NULL;
1145		}
1146		/*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
1147
1148		if ((fd = cli_open(targetcli, targetpath, flags,
1149                                   context->internal->_share_mode)) < 0) {
1150
1151			/* Handle the error ... */
1152
1153			SAFE_FREE(file);
1154			errno = smbc_errno(context, targetcli);
1155			return NULL;
1156
1157		}
1158
1159		/* Fill in file struct */
1160
1161		file->cli_fd  = fd;
1162		file->fname   = SMB_STRDUP(fname);
1163		file->srv     = srv;
1164		file->offset  = 0;
1165		file->file    = True;
1166
1167		DLIST_ADD(context->internal->_files, file);
1168
1169                /*
1170                 * If the file was opened in O_APPEND mode, all write
1171                 * operations should be appended to the file.  To do that,
1172                 * though, using this protocol, would require a getattrE()
1173                 * call for each and every write, to determine where the end
1174                 * of the file is. (There does not appear to be an append flag
1175                 * in the protocol.)  Rather than add all of that overhead of
1176                 * retrieving the current end-of-file offset prior to each
1177                 * write operation, we'll assume that most append operations
1178                 * will continuously write, so we'll just set the offset to
1179                 * the end of the file now and hope that's adequate.
1180                 *
1181                 * Note to self: If this proves inadequate, and O_APPEND
1182                 * should, in some cases, be forced for each write, add a
1183                 * field in the context options structure, for
1184                 * "strict_append_mode" which would select between the current
1185                 * behavior (if FALSE) or issuing a getattrE() prior to each
1186                 * write and forcing the write to the end of the file (if
1187                 * TRUE).  Adding that capability will likely require adding
1188                 * an "append" flag into the _SMBCFILE structure to track
1189                 * whether a file was opened in O_APPEND mode.  -- djl
1190                 */
1191                if (flags & O_APPEND) {
1192                        if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) {
1193                                (void) smbc_close_ctx(context, file);
1194                                errno = ENXIO;
1195                                return NULL;
1196                        }
1197                }
1198
1199		return file;
1200
1201	}
1202
1203	/* Check if opendir needed ... */
1204
1205	if (fd == -1) {
1206		int eno = 0;
1207
1208		eno = smbc_errno(context, srv->cli);
1209		file = context->opendir(context, fname);
1210		if (!file) errno = eno;
1211		return file;
1212
1213	}
1214
1215	errno = EINVAL; /* FIXME, correct errno ? */
1216	return NULL;
1217
1218}
1219
1220/*
1221 * Routine to create a file
1222 */
1223
1224static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
1225
1226static SMBCFILE *
1227smbc_creat_ctx(SMBCCTX *context,
1228               const char *path,
1229               mode_t mode)
1230{
1231
1232	if (!context || !context->internal ||
1233	    !context->internal->_initialized) {
1234
1235		errno = EINVAL;
1236		return NULL;
1237
1238	}
1239
1240	return smbc_open_ctx(context, path, creat_bits, mode);
1241}
1242
1243/*
1244 * Routine to read() a file ...
1245 */
1246
1247static ssize_t
1248smbc_read_ctx(SMBCCTX *context,
1249              SMBCFILE *file,
1250              void *buf,
1251              size_t count)
1252{
1253	int ret;
1254	fstring server, share, user, password;
1255	pstring path, targetpath;
1256	struct cli_state *targetcli;
1257
1258        /*
1259         * offset:
1260         *
1261         * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
1262         * appears to pass file->offset (which is type off_t) differently than
1263         * a local variable of type off_t.  Using local variable "offset" in
1264         * the call to cli_read() instead of file->offset fixes a problem
1265         * retrieving data at an offset greater than 4GB.
1266         */
1267        off_t offset;
1268
1269	if (!context || !context->internal ||
1270	    !context->internal->_initialized) {
1271
1272		errno = EINVAL;
1273		return -1;
1274
1275	}
1276
1277	DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
1278
1279	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1280
1281		errno = EBADF;
1282		return -1;
1283
1284	}
1285
1286	offset = file->offset;
1287
1288	/* Check that the buffer exists ... */
1289
1290	if (buf == NULL) {
1291
1292		errno = EINVAL;
1293		return -1;
1294
1295	}
1296
1297	/*d_printf(">>>read: parsing %s\n", file->fname);*/
1298	if (smbc_parse_path(context, file->fname,
1299                            NULL, 0,
1300                            server, sizeof(server),
1301                            share, sizeof(share),
1302                            path, sizeof(path),
1303                            user, sizeof(user),
1304                            password, sizeof(password),
1305                            NULL, 0)) {
1306                errno = EINVAL;
1307                return -1;
1308        }
1309
1310	/*d_printf(">>>read: resolving %s\n", path);*/
1311	if (!cli_resolve_path("", file->srv->cli, path,
1312                              &targetcli, targetpath))
1313	{
1314		d_printf("Could not resolve %s\n", path);
1315		return -1;
1316	}
1317	/*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
1318
1319	ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
1320
1321	if (ret < 0) {
1322
1323		errno = smbc_errno(context, targetcli);
1324		return -1;
1325
1326	}
1327
1328	file->offset += ret;
1329
1330	DEBUG(4, ("  --> %d\n", ret));
1331
1332	return ret;  /* Success, ret bytes of data ... */
1333
1334}
1335
1336/*
1337 * Routine to write() a file ...
1338 */
1339
1340static ssize_t
1341smbc_write_ctx(SMBCCTX *context,
1342               SMBCFILE *file,
1343               void *buf,
1344               size_t count)
1345{
1346	int ret;
1347        off_t offset;
1348	fstring server, share, user, password;
1349	pstring path, targetpath;
1350	struct cli_state *targetcli;
1351
1352	/* First check all pointers before dereferencing them */
1353
1354	if (!context || !context->internal ||
1355	    !context->internal->_initialized) {
1356
1357		errno = EINVAL;
1358		return -1;
1359
1360	}
1361
1362	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1363
1364		errno = EBADF;
1365		return -1;
1366
1367	}
1368
1369	/* Check that the buffer exists ... */
1370
1371	if (buf == NULL) {
1372
1373		errno = EINVAL;
1374		return -1;
1375
1376	}
1377
1378        offset = file->offset; /* See "offset" comment in smbc_read_ctx() */
1379
1380	/*d_printf(">>>write: parsing %s\n", file->fname);*/
1381	if (smbc_parse_path(context, file->fname,
1382                            NULL, 0,
1383                            server, sizeof(server),
1384                            share, sizeof(share),
1385                            path, sizeof(path),
1386                            user, sizeof(user),
1387                            password, sizeof(password),
1388                            NULL, 0)) {
1389                errno = EINVAL;
1390                return -1;
1391        }
1392
1393	/*d_printf(">>>write: resolving %s\n", path);*/
1394	if (!cli_resolve_path("", file->srv->cli, path,
1395                              &targetcli, targetpath))
1396	{
1397		d_printf("Could not resolve %s\n", path);
1398		return -1;
1399	}
1400	/*d_printf(">>>write: resolved path as %s\n", targetpath);*/
1401
1402
1403	ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count);
1404
1405	if (ret <= 0) {
1406
1407		errno = smbc_errno(context, targetcli);
1408		return -1;
1409
1410	}
1411
1412	file->offset += ret;
1413
1414	return ret;  /* Success, 0 bytes of data ... */
1415}
1416
1417/*
1418 * Routine to close() a file ...
1419 */
1420
1421static int
1422smbc_close_ctx(SMBCCTX *context,
1423               SMBCFILE *file)
1424{
1425        SMBCSRV *srv;
1426	fstring server, share, user, password;
1427	pstring path, targetpath;
1428	struct cli_state *targetcli;
1429
1430	if (!context || !context->internal ||
1431	    !context->internal->_initialized) {
1432
1433		errno = EINVAL;
1434		return -1;
1435
1436	}
1437
1438	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1439
1440		errno = EBADF;
1441		return -1;
1442
1443	}
1444
1445	/* IS a dir ... */
1446	if (!file->file) {
1447
1448		return context->closedir(context, file);
1449
1450	}
1451
1452	/*d_printf(">>>close: parsing %s\n", file->fname);*/
1453	if (smbc_parse_path(context, file->fname,
1454                            NULL, 0,
1455                            server, sizeof(server),
1456                            share, sizeof(share),
1457                            path, sizeof(path),
1458                            user, sizeof(user),
1459                            password, sizeof(password),
1460                            NULL, 0)) {
1461                errno = EINVAL;
1462                return -1;
1463        }
1464
1465	/*d_printf(">>>close: resolving %s\n", path);*/
1466	if (!cli_resolve_path("", file->srv->cli, path,
1467                              &targetcli, targetpath))
1468	{
1469		d_printf("Could not resolve %s\n", path);
1470		return -1;
1471	}
1472	/*d_printf(">>>close: resolved path as %s\n", targetpath);*/
1473
1474	if (!cli_close(targetcli, file->cli_fd)) {
1475
1476		DEBUG(3, ("cli_close failed on %s. purging server.\n",
1477			  file->fname));
1478		/* Deallocate slot and remove the server
1479		 * from the server cache if unused */
1480		errno = smbc_errno(context, targetcli);
1481		srv = file->srv;
1482		DLIST_REMOVE(context->internal->_files, file);
1483		SAFE_FREE(file->fname);
1484		SAFE_FREE(file);
1485		context->callbacks.remove_unused_server_fn(context, srv);
1486
1487		return -1;
1488
1489	}
1490
1491	DLIST_REMOVE(context->internal->_files, file);
1492	SAFE_FREE(file->fname);
1493	SAFE_FREE(file);
1494
1495	return 0;
1496}
1497
1498/*
1499 * Get info from an SMB server on a file. Use a qpathinfo call first
1500 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
1501 */
1502static BOOL
1503smbc_getatr(SMBCCTX * context,
1504            SMBCSRV *srv,
1505            char *path,
1506            uint16 *mode,
1507            SMB_OFF_T *size,
1508            struct timespec *create_time_ts,
1509            struct timespec *access_time_ts,
1510            struct timespec *write_time_ts,
1511            struct timespec *change_time_ts,
1512            SMB_INO_T *ino)
1513{
1514	pstring fixedpath;
1515	pstring targetpath;
1516	struct cli_state *targetcli;
1517	time_t write_time;
1518
1519	if (!context || !context->internal ||
1520	    !context->internal->_initialized) {
1521
1522		errno = EINVAL;
1523 		return -1;
1524
1525 	}
1526
1527	/* path fixup for . and .. */
1528	if (strequal(path, ".") || strequal(path, ".."))
1529		pstrcpy(fixedpath, "\\");
1530	else
1531	{
1532		pstrcpy(fixedpath, path);
1533		trim_string(fixedpath, NULL, "\\..");
1534		trim_string(fixedpath, NULL, "\\.");
1535	}
1536	DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
1537
1538	if (!cli_resolve_path( "", srv->cli, fixedpath, &targetcli, targetpath))
1539	{
1540		d_printf("Couldn't resolve %s\n", path);
1541		return False;
1542	}
1543
1544	if (!srv->no_pathinfo2 &&
1545            cli_qpathinfo2(targetcli, targetpath,
1546                           create_time_ts,
1547                           access_time_ts,
1548                           write_time_ts,
1549                           change_time_ts,
1550                           size, mode, ino)) {
1551            return True;
1552        }
1553
1554	/* if this is NT then don't bother with the getatr */
1555	if (targetcli->capabilities & CAP_NT_SMBS) {
1556                errno = EPERM;
1557                return False;
1558        }
1559
1560	if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
1561
1562                struct timespec w_time_ts;
1563
1564                w_time_ts = convert_time_t_to_timespec(write_time);
1565
1566                if (write_time_ts != NULL) {
1567			*write_time_ts = w_time_ts;
1568                }
1569
1570                if (create_time_ts != NULL) {
1571                        *create_time_ts = w_time_ts;
1572                }
1573
1574                if (access_time_ts != NULL) {
1575                        *access_time_ts = w_time_ts;
1576                }
1577
1578                if (change_time_ts != NULL) {
1579                        *change_time_ts = w_time_ts;
1580                }
1581
1582		srv->no_pathinfo2 = True;
1583		return True;
1584	}
1585
1586        errno = EPERM;
1587	return False;
1588
1589}
1590
1591/*
1592 * Set file info on an SMB server.  Use setpathinfo call first.  If that
1593 * fails, use setattrE..
1594 *
1595 * Access and modification time parameters are always used and must be
1596 * provided.  Create time, if zero, will be determined from the actual create
1597 * time of the file.  If non-zero, the create time will be set as well.
1598 *
1599 * "mode" (attributes) parameter may be set to -1 if it is not to be set.
1600 */
1601static BOOL
1602smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
1603            time_t create_time,
1604            time_t access_time,
1605            time_t write_time,
1606            time_t change_time,
1607            uint16 mode)
1608{
1609        int fd;
1610        int ret;
1611
1612        /*
1613         * First, try setpathinfo (if qpathinfo succeeded), for it is the
1614         * modern function for "new code" to be using, and it works given a
1615         * filename rather than requiring that the file be opened to have its
1616         * attributes manipulated.
1617         */
1618        if (srv->no_pathinfo ||
1619            ! cli_setpathinfo(srv->cli, path,
1620                              create_time,
1621                              access_time,
1622                              write_time,
1623                              change_time,
1624                              mode)) {
1625
1626                /*
1627                 * setpathinfo is not supported; go to plan B.
1628                 *
1629                 * cli_setatr() does not work on win98, and it also doesn't
1630                 * support setting the access time (only the modification
1631                 * time), so in all cases, we open the specified file and use
1632                 * cli_setattrE() which should work on all OS versions, and
1633                 * supports both times.
1634                 */
1635
1636                /* Don't try {q,set}pathinfo() again, with this server */
1637                srv->no_pathinfo = True;
1638
1639                /* Open the file */
1640                if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
1641
1642                        errno = smbc_errno(context, srv->cli);
1643                        return -1;
1644                }
1645
1646                /* Set the new attributes */
1647                ret = cli_setattrE(srv->cli, fd,
1648                                   change_time,
1649                                   access_time,
1650                                   write_time);
1651
1652                /* Close the file */
1653                cli_close(srv->cli, fd);
1654
1655                /*
1656                 * Unfortunately, setattrE() doesn't have a provision for
1657                 * setting the access mode (attributes).  We'll have to try
1658                 * cli_setatr() for that, and with only this parameter, it
1659                 * seems to work on win98.
1660                 */
1661                if (ret && mode != (uint16) -1) {
1662                        ret = cli_setatr(srv->cli, path, mode, 0);
1663                }
1664
1665                if (! ret) {
1666                        errno = smbc_errno(context, srv->cli);
1667                        return False;
1668                }
1669        }
1670
1671        return True;
1672}
1673
1674 /*
1675  * Routine to unlink() a file
1676  */
1677
1678static int
1679smbc_unlink_ctx(SMBCCTX *context,
1680                const char *fname)
1681{
1682	fstring server, share, user, password, workgroup;
1683	pstring path, targetpath;
1684	struct cli_state *targetcli;
1685	SMBCSRV *srv = NULL;
1686
1687	if (!context || !context->internal ||
1688	    !context->internal->_initialized) {
1689
1690		errno = EINVAL;  /* Best I can think of ... */
1691		return -1;
1692
1693	}
1694
1695	if (!fname) {
1696
1697		errno = EINVAL;
1698		return -1;
1699
1700	}
1701
1702	if (smbc_parse_path(context, fname,
1703                            workgroup, sizeof(workgroup),
1704                            server, sizeof(server),
1705                            share, sizeof(share),
1706                            path, sizeof(path),
1707                            user, sizeof(user),
1708                            password, sizeof(password),
1709                            NULL, 0)) {
1710                errno = EINVAL;
1711                return -1;
1712        }
1713
1714	if (user[0] == (char)0) fstrcpy(user, context->user);
1715
1716	srv = smbc_server(context, True,
1717                          server, share, workgroup, user, password);
1718
1719	if (!srv) {
1720
1721		return -1;  /* smbc_server sets errno */
1722
1723	}
1724
1725	/*d_printf(">>>unlink: resolving %s\n", path);*/
1726	if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
1727	{
1728		d_printf("Could not resolve %s\n", path);
1729		return -1;
1730	}
1731	/*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
1732
1733	if (!cli_unlink(targetcli, targetpath)) {
1734
1735		errno = smbc_errno(context, targetcli);
1736
1737		if (errno == EACCES) { /* Check if the file is a directory */
1738
1739			int saverr = errno;
1740			SMB_OFF_T size = 0;
1741			uint16 mode = 0;
1742			struct timespec write_time_ts;
1743                        struct timespec access_time_ts;
1744                        struct timespec change_time_ts;
1745			SMB_INO_T ino = 0;
1746
1747			if (!smbc_getatr(context, srv, path, &mode, &size,
1748					 NULL,
1749                                         &access_time_ts,
1750                                         &write_time_ts,
1751                                         &change_time_ts,
1752                                         &ino)) {
1753
1754				/* Hmmm, bad error ... What? */
1755
1756				errno = smbc_errno(context, targetcli);
1757				return -1;
1758
1759			}
1760			else {
1761
1762				if (IS_DOS_DIR(mode))
1763					errno = EISDIR;
1764				else
1765					errno = saverr;  /* Restore this */
1766
1767			}
1768		}
1769
1770		return -1;
1771
1772	}
1773
1774	return 0;  /* Success ... */
1775
1776}
1777
1778/*
1779 * Routine to rename() a file
1780 */
1781
1782static int
1783smbc_rename_ctx(SMBCCTX *ocontext,
1784                const char *oname,
1785                SMBCCTX *ncontext,
1786                const char *nname)
1787{
1788	fstring server1;
1789        fstring share1;
1790        fstring server2;
1791        fstring share2;
1792        fstring user1;
1793        fstring user2;
1794        fstring password1;
1795        fstring password2;
1796        fstring workgroup;
1797	pstring path1;
1798        pstring path2;
1799        pstring targetpath1;
1800        pstring targetpath2;
1801	struct cli_state *targetcli1;
1802        struct cli_state *targetcli2;
1803	SMBCSRV *srv = NULL;
1804
1805	if (!ocontext || !ncontext ||
1806	    !ocontext->internal || !ncontext->internal ||
1807	    !ocontext->internal->_initialized ||
1808	    !ncontext->internal->_initialized) {
1809
1810		errno = EINVAL;  /* Best I can think of ... */
1811		return -1;
1812
1813	}
1814
1815	if (!oname || !nname) {
1816
1817		errno = EINVAL;
1818		return -1;
1819
1820	}
1821
1822	DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
1823
1824	smbc_parse_path(ocontext, oname,
1825                        workgroup, sizeof(workgroup),
1826                        server1, sizeof(server1),
1827                        share1, sizeof(share1),
1828                        path1, sizeof(path1),
1829                        user1, sizeof(user1),
1830                        password1, sizeof(password1),
1831                        NULL, 0);
1832
1833	if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
1834
1835	smbc_parse_path(ncontext, nname,
1836                        NULL, 0,
1837                        server2, sizeof(server2),
1838                        share2, sizeof(share2),
1839                        path2, sizeof(path2),
1840                        user2, sizeof(user2),
1841                        password2, sizeof(password2),
1842                        NULL, 0);
1843
1844	if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
1845
1846	if (strcmp(server1, server2) || strcmp(share1, share2) ||
1847	    strcmp(user1, user2)) {
1848
1849		/* Can't rename across file systems, or users?? */
1850
1851		errno = EXDEV;
1852		return -1;
1853
1854	}
1855
1856	srv = smbc_server(ocontext, True,
1857                          server1, share1, workgroup, user1, password1);
1858	if (!srv) {
1859
1860		return -1;
1861
1862	}
1863
1864	/*d_printf(">>>rename: resolving %s\n", path1);*/
1865	if (!cli_resolve_path( "", srv->cli, path1, &targetcli1, targetpath1))
1866	{
1867		d_printf("Could not resolve %s\n", path1);
1868		return -1;
1869	}
1870	/*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
1871	/*d_printf(">>>rename: resolving %s\n", path2);*/
1872	if (!cli_resolve_path( "", srv->cli, path2, &targetcli2, targetpath2))
1873	{
1874		d_printf("Could not resolve %s\n", path2);
1875		return -1;
1876	}
1877	/*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
1878
1879	if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
1880            strcmp(targetcli1->share, targetcli2->share))
1881	{
1882		/* can't rename across file systems */
1883
1884		errno = EXDEV;
1885		return -1;
1886	}
1887
1888	if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
1889		int eno = smbc_errno(ocontext, targetcli1);
1890
1891		if (eno != EEXIST ||
1892		    !cli_unlink(targetcli1, targetpath2) ||
1893		    !cli_rename(targetcli1, targetpath1, targetpath2)) {
1894
1895			errno = eno;
1896			return -1;
1897
1898		}
1899	}
1900
1901	return 0; /* Success */
1902
1903}
1904
1905/*
1906 * A routine to lseek() a file
1907 */
1908
1909static off_t
1910smbc_lseek_ctx(SMBCCTX *context,
1911               SMBCFILE *file,
1912               off_t offset,
1913               int whence)
1914{
1915	SMB_OFF_T size;
1916	fstring server, share, user, password;
1917	pstring path, targetpath;
1918	struct cli_state *targetcli;
1919
1920	if (!context || !context->internal ||
1921	    !context->internal->_initialized) {
1922
1923		errno = EINVAL;
1924		return -1;
1925
1926	}
1927
1928	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
1929
1930		errno = EBADF;
1931		return -1;
1932
1933	}
1934
1935	if (!file->file) {
1936
1937		errno = EINVAL;
1938		return -1;      /* Can't lseek a dir ... */
1939
1940	}
1941
1942	switch (whence) {
1943	case SEEK_SET:
1944		file->offset = offset;
1945		break;
1946
1947	case SEEK_CUR:
1948		file->offset += offset;
1949		break;
1950
1951	case SEEK_END:
1952		/*d_printf(">>>lseek: parsing %s\n", file->fname);*/
1953		if (smbc_parse_path(context, file->fname,
1954                                    NULL, 0,
1955                                    server, sizeof(server),
1956                                    share, sizeof(share),
1957                                    path, sizeof(path),
1958                                    user, sizeof(user),
1959                                    password, sizeof(password),
1960                                    NULL, 0)) {
1961
1962					errno = EINVAL;
1963					return -1;
1964			}
1965
1966		/*d_printf(">>>lseek: resolving %s\n", path);*/
1967		if (!cli_resolve_path("", file->srv->cli, path,
1968                                      &targetcli, targetpath))
1969		{
1970			d_printf("Could not resolve %s\n", path);
1971			return -1;
1972		}
1973		/*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
1974
1975		if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
1976                                   &size, NULL, NULL, NULL, NULL, NULL))
1977		{
1978		    SMB_OFF_T b_size = size;
1979			if (!cli_getattrE(targetcli, file->cli_fd,
1980                                          NULL, &b_size, NULL, NULL, NULL))
1981		    {
1982			errno = EINVAL;
1983			return -1;
1984		    } else
1985			size = b_size;
1986		}
1987		file->offset = size + offset;
1988		break;
1989
1990	default:
1991		errno = EINVAL;
1992		break;
1993
1994	}
1995
1996	return file->offset;
1997
1998}
1999
2000/*
2001 * Generate an inode number from file name for those things that need it
2002 */
2003
2004static ino_t
2005smbc_inode(SMBCCTX *context,
2006           const char *name)
2007{
2008
2009	if (!context || !context->internal ||
2010	    !context->internal->_initialized) {
2011
2012		errno = EINVAL;
2013		return -1;
2014
2015	}
2016
2017	if (!*name) return 2; /* FIXME, why 2 ??? */
2018	return (ino_t)str_checksum(name);
2019
2020}
2021
2022/*
2023 * Routine to put basic stat info into a stat structure ... Used by stat and
2024 * fstat below.
2025 */
2026
2027static int
2028smbc_setup_stat(SMBCCTX *context,
2029                struct stat *st,
2030                char *fname,
2031                SMB_OFF_T size,
2032                int mode)
2033{
2034
2035	st->st_mode = 0;
2036
2037	if (IS_DOS_DIR(mode)) {
2038		st->st_mode = SMBC_DIR_MODE;
2039	} else {
2040		st->st_mode = SMBC_FILE_MODE;
2041	}
2042
2043	if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
2044	if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
2045	if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
2046	if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
2047
2048	st->st_size = size;
2049#ifdef HAVE_STAT_ST_BLKSIZE
2050	st->st_blksize = 512;
2051#endif
2052#ifdef HAVE_STAT_ST_BLOCKS
2053	st->st_blocks = (size+511)/512;
2054#endif
2055	st->st_uid = getuid();
2056	st->st_gid = getgid();
2057
2058	if (IS_DOS_DIR(mode)) {
2059		st->st_nlink = 2;
2060	} else {
2061		st->st_nlink = 1;
2062	}
2063
2064	if (st->st_ino == 0) {
2065		st->st_ino = smbc_inode(context, fname);
2066	}
2067
2068	return True;  /* FIXME: Is this needed ? */
2069
2070}
2071
2072/*
2073 * Routine to stat a file given a name
2074 */
2075
2076static int
2077smbc_stat_ctx(SMBCCTX *context,
2078              const char *fname,
2079              struct stat *st)
2080{
2081	SMBCSRV *srv;
2082	fstring server;
2083        fstring share;
2084        fstring user;
2085        fstring password;
2086        fstring workgroup;
2087	pstring path;
2088	struct timespec write_time_ts;
2089        struct timespec access_time_ts;
2090        struct timespec change_time_ts;
2091	SMB_OFF_T size = 0;
2092	uint16 mode = 0;
2093	SMB_INO_T ino = 0;
2094
2095	if (!context || !context->internal ||
2096	    !context->internal->_initialized) {
2097
2098		errno = EINVAL;  /* Best I can think of ... */
2099		return -1;
2100
2101	}
2102
2103	if (!fname) {
2104
2105		errno = EINVAL;
2106		return -1;
2107
2108	}
2109
2110	DEBUG(4, ("smbc_stat(%s)\n", fname));
2111
2112	if (smbc_parse_path(context, fname,
2113                            workgroup, sizeof(workgroup),
2114                            server, sizeof(server),
2115                            share, sizeof(share),
2116                            path, sizeof(path),
2117                            user, sizeof(user),
2118                            password, sizeof(password),
2119                            NULL, 0)) {
2120                errno = EINVAL;
2121                return -1;
2122        }
2123
2124	if (user[0] == (char)0) fstrcpy(user, context->user);
2125
2126	srv = smbc_server(context, True,
2127                          server, share, workgroup, user, password);
2128
2129	if (!srv) {
2130		return -1;  /* errno set by smbc_server */
2131	}
2132
2133	if (!smbc_getatr(context, srv, path, &mode, &size,
2134			 NULL,
2135                         &access_time_ts,
2136                         &write_time_ts,
2137                         &change_time_ts,
2138                         &ino)) {
2139
2140		errno = smbc_errno(context, srv->cli);
2141		return -1;
2142
2143	}
2144
2145	st->st_ino = ino;
2146
2147	smbc_setup_stat(context, st, path, size, mode);
2148
2149	set_atimespec(st, access_time_ts);
2150	set_ctimespec(st, change_time_ts);
2151	set_mtimespec(st, write_time_ts);
2152	st->st_dev   = srv->dev;
2153
2154	return 0;
2155
2156}
2157
2158/*
2159 * Routine to stat a file given an fd
2160 */
2161
2162static int
2163smbc_fstat_ctx(SMBCCTX *context,
2164               SMBCFILE *file,
2165               struct stat *st)
2166{
2167	struct timespec change_time_ts;
2168        struct timespec access_time_ts;
2169        struct timespec write_time_ts;
2170	SMB_OFF_T size;
2171	uint16 mode;
2172	fstring server;
2173        fstring share;
2174        fstring user;
2175        fstring password;
2176	pstring path;
2177        pstring targetpath;
2178	struct cli_state *targetcli;
2179	SMB_INO_T ino = 0;
2180
2181	if (!context || !context->internal ||
2182	    !context->internal->_initialized) {
2183
2184		errno = EINVAL;
2185		return -1;
2186
2187	}
2188
2189	if (!file || !DLIST_CONTAINS(context->internal->_files, file)) {
2190
2191		errno = EBADF;
2192		return -1;
2193
2194	}
2195
2196	if (!file->file) {
2197
2198		return context->fstatdir(context, file, st);
2199
2200	}
2201
2202	/*d_printf(">>>fstat: parsing %s\n", file->fname);*/
2203	if (smbc_parse_path(context, file->fname,
2204                            NULL, 0,
2205                            server, sizeof(server),
2206                            share, sizeof(share),
2207                            path, sizeof(path),
2208                            user, sizeof(user),
2209                            password, sizeof(password),
2210                            NULL, 0)) {
2211                errno = EINVAL;
2212                return -1;
2213        }
2214
2215	/*d_printf(">>>fstat: resolving %s\n", path);*/
2216	if (!cli_resolve_path("", file->srv->cli, path,
2217                              &targetcli, targetpath))
2218	{
2219		d_printf("Could not resolve %s\n", path);
2220		return -1;
2221	}
2222	/*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
2223
2224	if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
2225                           NULL,
2226                           &access_time_ts,
2227                           &write_time_ts,
2228                           &change_time_ts,
2229                           &ino)) {
2230
2231		time_t change_time, access_time, write_time;
2232
2233		if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
2234				&change_time, &access_time, &write_time)) {
2235
2236			errno = EINVAL;
2237			return -1;
2238		}
2239
2240		change_time_ts = convert_time_t_to_timespec(change_time);
2241		access_time_ts = convert_time_t_to_timespec(access_time);
2242		write_time_ts = convert_time_t_to_timespec(write_time);
2243	}
2244
2245	st->st_ino = ino;
2246
2247	smbc_setup_stat(context, st, file->fname, size, mode);
2248
2249	set_atimespec(st, access_time_ts);
2250	set_ctimespec(st, change_time_ts);
2251	set_mtimespec(st, write_time_ts);
2252	st->st_dev = file->srv->dev;
2253
2254	return 0;
2255
2256}
2257
2258/*
2259 * Routine to open a directory
2260 * We accept the URL syntax explained in smbc_parse_path(), above.
2261 */
2262
2263static void
2264smbc_remove_dir(SMBCFILE *dir)
2265{
2266	struct smbc_dir_list *d,*f;
2267
2268	d = dir->dir_list;
2269	while (d) {
2270
2271		f = d; d = d->next;
2272
2273		SAFE_FREE(f->dirent);
2274		SAFE_FREE(f);
2275
2276	}
2277
2278	dir->dir_list = dir->dir_end = dir->dir_next = NULL;
2279
2280}
2281
2282static int
2283add_dirent(SMBCFILE *dir,
2284           const char *name,
2285           const char *comment,
2286           uint32 type)
2287{
2288	struct smbc_dirent *dirent;
2289	int size;
2290        int name_length = (name == NULL ? 0 : strlen(name));
2291        int comment_len = (comment == NULL ? 0 : strlen(comment));
2292
2293	/*
2294	 * Allocate space for the dirent, which must be increased by the
2295	 * size of the name and the comment and 1 each for the null terminator.
2296	 */
2297
2298	size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
2299
2300	dirent = (struct smbc_dirent *)SMB_MALLOC(size);
2301
2302	if (!dirent) {
2303
2304		dir->dir_error = ENOMEM;
2305		return -1;
2306
2307	}
2308
2309	ZERO_STRUCTP(dirent);
2310
2311	if (dir->dir_list == NULL) {
2312
2313		dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
2314		if (!dir->dir_list) {
2315
2316			SAFE_FREE(dirent);
2317			dir->dir_error = ENOMEM;
2318			return -1;
2319
2320		}
2321		ZERO_STRUCTP(dir->dir_list);
2322
2323		dir->dir_end = dir->dir_next = dir->dir_list;
2324	}
2325	else {
2326
2327		dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
2328
2329		if (!dir->dir_end->next) {
2330
2331			SAFE_FREE(dirent);
2332			dir->dir_error = ENOMEM;
2333			return -1;
2334
2335		}
2336		ZERO_STRUCTP(dir->dir_end->next);
2337
2338		dir->dir_end = dir->dir_end->next;
2339	}
2340
2341	dir->dir_end->next = NULL;
2342	dir->dir_end->dirent = dirent;
2343
2344	dirent->smbc_type = type;
2345	dirent->namelen = name_length;
2346	dirent->commentlen = comment_len;
2347	dirent->dirlen = size;
2348
2349        /*
2350         * dirent->namelen + 1 includes the null (no null termination needed)
2351         * Ditto for dirent->commentlen.
2352         * The space for the two null bytes was allocated.
2353         */
2354	strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
2355	dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
2356	strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
2357
2358	return 0;
2359
2360}
2361
2362static void
2363list_unique_wg_fn(const char *name,
2364                  uint32 type,
2365                  const char *comment,
2366                  void *state)
2367{
2368	SMBCFILE *dir = (SMBCFILE *)state;
2369        struct smbc_dir_list *dir_list;
2370        struct smbc_dirent *dirent;
2371	int dirent_type;
2372        int do_remove = 0;
2373
2374	dirent_type = dir->dir_type;
2375
2376	if (add_dirent(dir, name, comment, dirent_type) < 0) {
2377
2378		/* An error occurred, what do we do? */
2379		/* FIXME: Add some code here */
2380	}
2381
2382        /* Point to the one just added */
2383        dirent = dir->dir_end->dirent;
2384
2385        /* See if this was a duplicate */
2386        for (dir_list = dir->dir_list;
2387             dir_list != dir->dir_end;
2388             dir_list = dir_list->next) {
2389                if (! do_remove &&
2390                    strcmp(dir_list->dirent->name, dirent->name) == 0) {
2391                        /* Duplicate.  End end of list need to be removed. */
2392                        do_remove = 1;
2393                }
2394
2395                if (do_remove && dir_list->next == dir->dir_end) {
2396                        /* Found the end of the list.  Remove it. */
2397                        dir->dir_end = dir_list;
2398                        free(dir_list->next);
2399                        free(dirent);
2400                        dir_list->next = NULL;
2401                        break;
2402                }
2403        }
2404}
2405
2406static void
2407list_fn(const char *name,
2408        uint32 type,
2409        const char *comment,
2410        void *state)
2411{
2412	SMBCFILE *dir = (SMBCFILE *)state;
2413	int dirent_type;
2414
2415	/*
2416         * We need to process the type a little ...
2417         *
2418         * Disk share     = 0x00000000
2419         * Print share    = 0x00000001
2420         * Comms share    = 0x00000002 (obsolete?)
2421         * IPC$ share     = 0x00000003
2422         *
2423         * administrative shares:
2424         * ADMIN$, IPC$, C$, D$, E$ ...  are type |= 0x80000000
2425         */
2426
2427	if (dir->dir_type == SMBC_FILE_SHARE) {
2428
2429		switch (type) {
2430                case 0 | 0x80000000:
2431		case 0:
2432			dirent_type = SMBC_FILE_SHARE;
2433			break;
2434
2435		case 1:
2436			dirent_type = SMBC_PRINTER_SHARE;
2437			break;
2438
2439		case 2:
2440			dirent_type = SMBC_COMMS_SHARE;
2441			break;
2442
2443                case 3 | 0x80000000:
2444		case 3:
2445			dirent_type = SMBC_IPC_SHARE;
2446			break;
2447
2448		default:
2449			dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
2450			break;
2451		}
2452	}
2453	else {
2454                dirent_type = dir->dir_type;
2455        }
2456
2457	if (add_dirent(dir, name, comment, dirent_type) < 0) {
2458
2459		/* An error occurred, what do we do? */
2460		/* FIXME: Add some code here */
2461
2462	}
2463}
2464
2465static void
2466dir_list_fn(const char *mnt,
2467            file_info *finfo,
2468            const char *mask,
2469            void *state)
2470{
2471
2472	if (add_dirent((SMBCFILE *)state, finfo->name, "",
2473		       (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
2474
2475		/* Handle an error ... */
2476
2477		/* FIXME: Add some code ... */
2478
2479	}
2480
2481}
2482
2483static int
2484net_share_enum_rpc(struct cli_state *cli,
2485                   void (*fn)(const char *name,
2486                              uint32 type,
2487                              const char *comment,
2488                              void *state),
2489                   void *state)
2490{
2491        int i;
2492	WERROR result;
2493	ENUM_HND enum_hnd;
2494        uint32 info_level = 1;
2495	uint32 preferred_len = 0xffffffff;
2496        uint32 type;
2497	SRV_SHARE_INFO_CTR ctr;
2498	fstring name = "";
2499        fstring comment = "";
2500        void *mem_ctx;
2501	struct rpc_pipe_client *pipe_hnd;
2502        NTSTATUS nt_status;
2503
2504        /* Open the server service pipe */
2505        pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status);
2506        if (!pipe_hnd) {
2507                DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
2508                return -1;
2509        }
2510
2511        /* Allocate a context for parsing and for the entries in "ctr" */
2512        mem_ctx = talloc_init("libsmbclient: net_share_enum_rpc");
2513        if (mem_ctx == NULL) {
2514                DEBUG(0, ("out of memory for net_share_enum_rpc!\n"));
2515                cli_rpc_pipe_close(pipe_hnd);
2516                return -1;
2517        }
2518
2519        /* Issue the NetShareEnum RPC call and retrieve the response */
2520	init_enum_hnd(&enum_hnd, 0);
2521	result = rpccli_srvsvc_net_share_enum(pipe_hnd,
2522                                              mem_ctx,
2523                                              info_level,
2524                                              &ctr,
2525                                              preferred_len,
2526                                              &enum_hnd);
2527
2528        /* Was it successful? */
2529	if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) {
2530                /*  Nope.  Go clean up. */
2531		goto done;
2532        }
2533
2534        /* For each returned entry... */
2535        for (i = 0; i < ctr.num_entries; i++) {
2536
2537                /* pull out the share name */
2538                rpcstr_pull_unistr2_fstring(
2539                        name, &ctr.share.info1[i].info_1_str.uni_netname);
2540
2541                /* pull out the share's comment */
2542                rpcstr_pull_unistr2_fstring(
2543                        comment, &ctr.share.info1[i].info_1_str.uni_remark);
2544
2545                /* Get the type value */
2546                type = ctr.share.info1[i].info_1.type;
2547
2548                /* Add this share to the list */
2549                (*fn)(name, type, comment, state);
2550        }
2551
2552done:
2553        /* Close the server service pipe */
2554        cli_rpc_pipe_close(pipe_hnd);
2555
2556        /* Free all memory which was allocated for this request */
2557        TALLOC_FREE(mem_ctx);
2558
2559        /* Tell 'em if it worked */
2560        return W_ERROR_IS_OK(result) ? 0 : -1;
2561}
2562
2563
2564
2565static SMBCFILE *
2566smbc_opendir_ctx(SMBCCTX *context,
2567                 const char *fname)
2568{
2569        int saved_errno;
2570	fstring server, share, user, password, options;
2571	pstring workgroup;
2572	pstring path;
2573        uint16 mode;
2574        char *p;
2575	SMBCSRV *srv  = NULL;
2576	SMBCFILE *dir = NULL;
2577        struct _smbc_callbacks *cb;
2578	struct in_addr rem_ip;
2579
2580	if (!context || !context->internal ||
2581	    !context->internal->_initialized) {
2582	        DEBUG(4, ("no valid context\n"));
2583		errno = EINVAL + 8192;
2584		return NULL;
2585
2586	}
2587
2588	if (!fname) {
2589		DEBUG(4, ("no valid fname\n"));
2590		errno = EINVAL + 8193;
2591		return NULL;
2592	}
2593
2594	if (smbc_parse_path(context, fname,
2595                            workgroup, sizeof(workgroup),
2596                            server, sizeof(server),
2597                            share, sizeof(share),
2598                            path, sizeof(path),
2599                            user, sizeof(user),
2600                            password, sizeof(password),
2601                            options, sizeof(options))) {
2602	        DEBUG(4, ("no valid path\n"));
2603		errno = EINVAL + 8194;
2604		return NULL;
2605	}
2606
2607	DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
2608                  "path='%s' options='%s'\n",
2609                  fname, server, share, path, options));
2610
2611        /* Ensure the options are valid */
2612        if (smbc_check_options(server, share, path, options)) {
2613                DEBUG(4, ("unacceptable options (%s)\n", options));
2614                errno = EINVAL + 8195;
2615                return NULL;
2616        }
2617
2618	if (user[0] == (char)0) fstrcpy(user, context->user);
2619
2620	dir = SMB_MALLOC_P(SMBCFILE);
2621
2622	if (!dir) {
2623
2624		errno = ENOMEM;
2625		return NULL;
2626
2627	}
2628
2629	ZERO_STRUCTP(dir);
2630
2631	dir->cli_fd   = 0;
2632	dir->fname    = SMB_STRDUP(fname);
2633	dir->srv      = NULL;
2634	dir->offset   = 0;
2635	dir->file     = False;
2636	dir->dir_list = dir->dir_next = dir->dir_end = NULL;
2637
2638	if (server[0] == (char)0) {
2639
2640                int i;
2641                int count;
2642                int max_lmb_count;
2643                struct ip_service *ip_list;
2644                struct ip_service server_addr;
2645                struct user_auth_info u_info;
2646                struct cli_state *cli;
2647
2648		if (share[0] != (char)0 || path[0] != (char)0) {
2649
2650			errno = EINVAL + 8196;
2651			if (dir) {
2652				SAFE_FREE(dir->fname);
2653				SAFE_FREE(dir);
2654			}
2655			return NULL;
2656		}
2657
2658                /* Determine how many local master browsers to query */
2659                max_lmb_count = (context->options.browse_max_lmb_count == 0
2660                                 ? INT_MAX
2661                                 : context->options.browse_max_lmb_count);
2662
2663                pstrcpy(u_info.username, user);
2664                pstrcpy(u_info.password, password);
2665
2666		/*
2667                 * We have server and share and path empty but options
2668                 * requesting that we scan all master browsers for their list
2669                 * of workgroups/domains.  This implies that we must first try
2670                 * broadcast queries to find all master browsers, and if that
2671                 * doesn't work, then try our other methods which return only
2672                 * a single master browser.
2673                 */
2674
2675                ip_list = NULL;
2676                if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
2677
2678                        SAFE_FREE(ip_list);
2679
2680                        if (!find_master_ip(workgroup, &server_addr.ip)) {
2681
2682				if (dir) {
2683					SAFE_FREE(dir->fname);
2684					SAFE_FREE(dir);
2685				}
2686                                errno = ENOENT;
2687                                return NULL;
2688                        }
2689
2690                        ip_list = &server_addr;
2691                        count = 1;
2692                }
2693
2694                for (i = 0; i < count && i < max_lmb_count; i++) {
2695                        DEBUG(99, ("Found master browser %d of %d: %s\n",
2696                                   i+1, MAX(count, max_lmb_count),
2697                                   inet_ntoa(ip_list[i].ip)));
2698
2699                        cli = get_ipc_connect_master_ip(&ip_list[i],
2700                                                        workgroup, &u_info);
2701			/* cli == NULL is the master browser refused to talk or
2702			   could not be found */
2703			if ( !cli )
2704				continue;
2705
2706                        fstrcpy(server, cli->desthost);
2707                        cli_shutdown(cli);
2708
2709                        DEBUG(4, ("using workgroup %s %s\n",
2710                                  workgroup, server));
2711
2712                        /*
2713                         * For each returned master browser IP address, get a
2714                         * connection to IPC$ on the server if we do not
2715                         * already have one, and determine the
2716                         * workgroups/domains that it knows about.
2717                         */
2718
2719                        srv = smbc_server(context, True, server, "IPC$",
2720                                          workgroup, user, password);
2721                        if (!srv) {
2722                                continue;
2723                        }
2724
2725                        dir->srv = srv;
2726                        dir->dir_type = SMBC_WORKGROUP;
2727
2728                        /* Now, list the stuff ... */
2729
2730                        if (!cli_NetServerEnum(srv->cli,
2731                                               workgroup,
2732                                               SV_TYPE_DOMAIN_ENUM,
2733                                               list_unique_wg_fn,
2734                                               (void *)dir)) {
2735                                continue;
2736                        }
2737                }
2738
2739                SAFE_FREE(ip_list);
2740        } else {
2741                /*
2742                 * Server not an empty string ... Check the rest and see what
2743                 * gives
2744                 */
2745		if (*share == '\0') {
2746			if (*path != '\0') {
2747
2748                                /* Should not have empty share with path */
2749				errno = EINVAL + 8197;
2750				if (dir) {
2751					SAFE_FREE(dir->fname);
2752					SAFE_FREE(dir);
2753				}
2754				return NULL;
2755
2756			}
2757
2758			/*
2759                         * We don't know if <server> is really a server name
2760                         * or is a workgroup/domain name.  If we already have
2761                         * a server structure for it, we'll use it.
2762                         * Otherwise, check to see if <server><1D>,
2763                         * <server><1B>, or <server><20> translates.  We check
2764                         * to see if <server> is an IP address first.
2765                         */
2766
2767                        /*
2768                         * See if we have an existing server.  Do not
2769                         * establish a connection if one does not already
2770                         * exist.
2771                         */
2772                        srv = smbc_server(context, False, server, "IPC$",
2773                                          workgroup, user, password);
2774
2775                        /*
2776                         * If no existing server and not an IP addr, look for
2777                         * LMB or DMB
2778                         */
2779			if (!srv &&
2780                            !is_ipaddress(server) &&
2781			    (resolve_name(server, &rem_ip, 0x1d) ||   /* LMB */
2782                             resolve_name(server, &rem_ip, 0x1b) )) { /* DMB */
2783
2784				fstring buserver;
2785
2786				dir->dir_type = SMBC_SERVER;
2787
2788				/*
2789				 * Get the backup list ...
2790				 */
2791				if (!name_status_find(server, 0, 0,
2792                                                      rem_ip, buserver)) {
2793
2794                                        DEBUG(0, ("Could not get name of "
2795                                                  "local/domain master browser "
2796                                                  "for server %s\n", server));
2797					if (dir) {
2798						SAFE_FREE(dir->fname);
2799						SAFE_FREE(dir);
2800					}
2801					errno = EPERM;
2802					return NULL;
2803
2804				}
2805
2806				/*
2807                                 * Get a connection to IPC$ on the server if
2808                                 * we do not already have one
2809                                 */
2810				srv = smbc_server(context, True,
2811                                                  buserver, "IPC$",
2812                                                  workgroup, user, password);
2813				if (!srv) {
2814				        DEBUG(0, ("got no contact to IPC$\n"));
2815					if (dir) {
2816						SAFE_FREE(dir->fname);
2817						SAFE_FREE(dir);
2818					}
2819					return NULL;
2820
2821				}
2822
2823				dir->srv = srv;
2824
2825				/* Now, list the servers ... */
2826				if (!cli_NetServerEnum(srv->cli, server,
2827                                                       0x0000FFFE, list_fn,
2828						       (void *)dir)) {
2829
2830					if (dir) {
2831						SAFE_FREE(dir->fname);
2832						SAFE_FREE(dir);
2833					}
2834					return NULL;
2835				}
2836			} else if (srv ||
2837                                   (resolve_name(server, &rem_ip, 0x20))) {
2838
2839                                /* If we hadn't found the server, get one now */
2840                                if (!srv) {
2841                                        srv = smbc_server(context, True,
2842                                                          server, "IPC$",
2843                                                          workgroup,
2844                                                          user, password);
2845                                }
2846
2847                                if (!srv) {
2848                                        if (dir) {
2849                                                SAFE_FREE(dir->fname);
2850                                                SAFE_FREE(dir);
2851                                        }
2852                                        return NULL;
2853
2854                                }
2855
2856                                dir->dir_type = SMBC_FILE_SHARE;
2857                                dir->srv = srv;
2858
2859                                /* List the shares ... */
2860
2861                                if (net_share_enum_rpc(
2862                                            srv->cli,
2863                                            list_fn,
2864                                            (void *) dir) < 0 &&
2865                                    cli_RNetShareEnum(
2866                                            srv->cli,
2867                                            list_fn,
2868                                            (void *)dir) < 0) {
2869
2870                                        errno = cli_errno(srv->cli);
2871                                        if (dir) {
2872                                                SAFE_FREE(dir->fname);
2873                                                SAFE_FREE(dir);
2874                                        }
2875                                        return NULL;
2876
2877                                }
2878                        } else {
2879                                /* Neither the workgroup nor server exists */
2880                                errno = ECONNREFUSED;
2881                                if (dir) {
2882                                        SAFE_FREE(dir->fname);
2883                                        SAFE_FREE(dir);
2884                                }
2885                                return NULL;
2886			}
2887
2888		}
2889		else {
2890                        /*
2891                         * The server and share are specified ... work from
2892                         * there ...
2893                         */
2894			pstring targetpath;
2895			struct cli_state *targetcli;
2896
2897			/* We connect to the server and list the directory */
2898			dir->dir_type = SMBC_FILE_SHARE;
2899
2900			srv = smbc_server(context, True, server, share,
2901                                          workgroup, user, password);
2902
2903			if (!srv) {
2904
2905				if (dir) {
2906					SAFE_FREE(dir->fname);
2907					SAFE_FREE(dir);
2908				}
2909				return NULL;
2910
2911			}
2912
2913			dir->srv = srv;
2914
2915			/* Now, list the files ... */
2916
2917                        p = path + strlen(path);
2918			pstrcat(path, "\\*");
2919
2920			if (!cli_resolve_path("", srv->cli, path,
2921                                              &targetcli, targetpath))
2922			{
2923				d_printf("Could not resolve %s\n", path);
2924				if (dir) {
2925					SAFE_FREE(dir->fname);
2926					SAFE_FREE(dir);
2927				}
2928				return NULL;
2929			}
2930
2931			if (cli_list(targetcli, targetpath,
2932                                     aDIR | aSYSTEM | aHIDDEN,
2933                                     dir_list_fn, (void *)dir) < 0) {
2934
2935				if (dir) {
2936					SAFE_FREE(dir->fname);
2937					SAFE_FREE(dir);
2938				}
2939				saved_errno = smbc_errno(context, targetcli);
2940
2941                                if (saved_errno == EINVAL) {
2942                                    /*
2943                                     * See if they asked to opendir something
2944                                     * other than a directory.  If so, the
2945                                     * converted error value we got would have
2946                                     * been EINVAL rather than ENOTDIR.
2947                                     */
2948                                    *p = '\0'; /* restore original path */
2949
2950                                    if (smbc_getatr(context, srv, path,
2951                                                    &mode, NULL,
2952                                                    NULL, NULL, NULL, NULL,
2953                                                    NULL) &&
2954                                        ! IS_DOS_DIR(mode)) {
2955
2956                                        /* It is.  Correct the error value */
2957                                        saved_errno = ENOTDIR;
2958                                    }
2959                                }
2960
2961                                /*
2962                                 * If there was an error and the server is no
2963                                 * good any more...
2964                                 */
2965                                cb = &context->callbacks;
2966                                if (cli_is_error(targetcli) &&
2967                                    cb->check_server_fn(context, srv)) {
2968
2969                                    /* ... then remove it. */
2970                                    if (cb->remove_unused_server_fn(context,
2971                                                                    srv)) {
2972                                        /*
2973                                         * We could not remove the server
2974                                         * completely, remove it from the
2975                                         * cache so we will not get it
2976                                         * again. It will be removed when the
2977                                         * last file/dir is closed.
2978                                         */
2979                                        cb->remove_cached_srv_fn(context, srv);
2980                                    }
2981                                }
2982
2983                                errno = saved_errno;
2984				return NULL;
2985			}
2986		}
2987
2988	}
2989
2990	DLIST_ADD(context->internal->_files, dir);
2991	return dir;
2992
2993}
2994
2995/*
2996 * Routine to close a directory
2997 */
2998
2999static int
3000smbc_closedir_ctx(SMBCCTX *context,
3001                  SMBCFILE *dir)
3002{
3003
3004        if (!context || !context->internal ||
3005	    !context->internal->_initialized) {
3006
3007		errno = EINVAL;
3008		return -1;
3009
3010	}
3011
3012	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3013
3014		errno = EBADF;
3015		return -1;
3016
3017	}
3018
3019	smbc_remove_dir(dir); /* Clean it up */
3020
3021	DLIST_REMOVE(context->internal->_files, dir);
3022
3023	if (dir) {
3024
3025		SAFE_FREE(dir->fname);
3026		SAFE_FREE(dir);    /* Free the space too */
3027	}
3028
3029	return 0;
3030
3031}
3032
3033static void
3034smbc_readdir_internal(SMBCCTX * context,
3035                      struct smbc_dirent *dest,
3036                      struct smbc_dirent *src,
3037                      int max_namebuf_len)
3038{
3039        if (context->options.urlencode_readdir_entries) {
3040
3041                /* url-encode the name.  get back remaining buffer space */
3042                max_namebuf_len =
3043                        smbc_urlencode(dest->name, src->name, max_namebuf_len);
3044
3045                /* We now know the name length */
3046                dest->namelen = strlen(dest->name);
3047
3048                /* Save the pointer to the beginning of the comment */
3049                dest->comment = dest->name + dest->namelen + 1;
3050
3051                /* Copy the comment */
3052                strncpy(dest->comment, src->comment, max_namebuf_len - 1);
3053                dest->comment[max_namebuf_len - 1] = '\0';
3054
3055                /* Save other fields */
3056                dest->smbc_type = src->smbc_type;
3057                dest->commentlen = strlen(dest->comment);
3058                dest->dirlen = ((dest->comment + dest->commentlen + 1) -
3059                                (char *) dest);
3060        } else {
3061
3062                /* No encoding.  Just copy the entry as is. */
3063                memcpy(dest, src, src->dirlen);
3064                dest->comment = (char *)(&dest->name + src->namelen + 1);
3065        }
3066
3067}
3068
3069/*
3070 * Routine to get a directory entry
3071 */
3072
3073struct smbc_dirent *
3074smbc_readdir_ctx(SMBCCTX *context,
3075                 SMBCFILE *dir)
3076{
3077        int maxlen;
3078	struct smbc_dirent *dirp, *dirent;
3079
3080	/* Check that all is ok first ... */
3081
3082	if (!context || !context->internal ||
3083	    !context->internal->_initialized) {
3084
3085		errno = EINVAL;
3086                DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
3087		return NULL;
3088
3089	}
3090
3091	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3092
3093		errno = EBADF;
3094                DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
3095		return NULL;
3096
3097	}
3098
3099	if (dir->file != False) { /* FIXME, should be dir, perhaps */
3100
3101		errno = ENOTDIR;
3102                DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
3103		return NULL;
3104
3105	}
3106
3107	if (!dir->dir_next) {
3108		return NULL;
3109        }
3110
3111        dirent = dir->dir_next->dirent;
3112        if (!dirent) {
3113
3114                errno = ENOENT;
3115                return NULL;
3116
3117        }
3118
3119        dirp = (struct smbc_dirent *)context->internal->_dirent;
3120        maxlen = (sizeof(context->internal->_dirent) -
3121                  sizeof(struct smbc_dirent));
3122
3123        smbc_readdir_internal(context, dirp, dirent, maxlen);
3124
3125        dir->dir_next = dir->dir_next->next;
3126
3127        return dirp;
3128}
3129
3130/*
3131 * Routine to get directory entries
3132 */
3133
3134static int
3135smbc_getdents_ctx(SMBCCTX *context,
3136                  SMBCFILE *dir,
3137                  struct smbc_dirent *dirp,
3138                  int count)
3139{
3140	int rem = count;
3141        int reqd;
3142        int maxlen;
3143	char *ndir = (char *)dirp;
3144	struct smbc_dir_list *dirlist;
3145
3146	/* Check that all is ok first ... */
3147
3148	if (!context || !context->internal ||
3149	    !context->internal->_initialized) {
3150
3151		errno = EINVAL;
3152		return -1;
3153
3154	}
3155
3156	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3157
3158		errno = EBADF;
3159		return -1;
3160
3161	}
3162
3163	if (dir->file != False) { /* FIXME, should be dir, perhaps */
3164
3165		errno = ENOTDIR;
3166		return -1;
3167
3168	}
3169
3170	/*
3171	 * Now, retrieve the number of entries that will fit in what was passed
3172	 * We have to figure out if the info is in the list, or we need to
3173	 * send a request to the server to get the info.
3174	 */
3175
3176	while ((dirlist = dir->dir_next)) {
3177		struct smbc_dirent *dirent;
3178
3179		if (!dirlist->dirent) {
3180
3181			errno = ENOENT;  /* Bad error */
3182			return -1;
3183
3184		}
3185
3186                /* Do urlencoding of next entry, if so selected */
3187                dirent = (struct smbc_dirent *)context->internal->_dirent;
3188                maxlen = (sizeof(context->internal->_dirent) -
3189                          sizeof(struct smbc_dirent));
3190                smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen);
3191
3192                reqd = dirent->dirlen;
3193
3194		if (rem < reqd) {
3195
3196			if (rem < count) { /* We managed to copy something */
3197
3198				errno = 0;
3199				return count - rem;
3200
3201			}
3202			else { /* Nothing copied ... */
3203
3204				errno = EINVAL;  /* Not enough space ... */
3205				return -1;
3206
3207			}
3208
3209		}
3210
3211		memcpy(ndir, dirent, reqd); /* Copy the data in ... */
3212
3213		((struct smbc_dirent *)ndir)->comment =
3214			(char *)(&((struct smbc_dirent *)ndir)->name +
3215                                 dirent->namelen +
3216                                 1);
3217
3218		ndir += reqd;
3219
3220		rem -= reqd;
3221
3222		dir->dir_next = dirlist = dirlist -> next;
3223	}
3224
3225	if (rem == count)
3226		return 0;
3227	else
3228		return count - rem;
3229
3230}
3231
3232/*
3233 * Routine to create a directory ...
3234 */
3235
3236static int
3237smbc_mkdir_ctx(SMBCCTX *context,
3238               const char *fname,
3239               mode_t mode)
3240{
3241	SMBCSRV *srv;
3242	fstring server;
3243        fstring share;
3244        fstring user;
3245        fstring password;
3246        fstring workgroup;
3247	pstring path, targetpath;
3248	struct cli_state *targetcli;
3249
3250	if (!context || !context->internal ||
3251	    !context->internal->_initialized) {
3252
3253		errno = EINVAL;
3254		return -1;
3255
3256	}
3257
3258	if (!fname) {
3259
3260		errno = EINVAL;
3261		return -1;
3262
3263	}
3264
3265	DEBUG(4, ("smbc_mkdir(%s)\n", fname));
3266
3267	if (smbc_parse_path(context, fname,
3268                            workgroup, sizeof(workgroup),
3269                            server, sizeof(server),
3270                            share, sizeof(share),
3271                            path, sizeof(path),
3272                            user, sizeof(user),
3273                            password, sizeof(password),
3274                            NULL, 0)) {
3275                errno = EINVAL;
3276                return -1;
3277        }
3278
3279	if (user[0] == (char)0) fstrcpy(user, context->user);
3280
3281	srv = smbc_server(context, True,
3282                          server, share, workgroup, user, password);
3283
3284	if (!srv) {
3285
3286		return -1;  /* errno set by smbc_server */
3287
3288	}
3289
3290	/*d_printf(">>>mkdir: resolving %s\n", path);*/
3291	if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3292	{
3293		d_printf("Could not resolve %s\n", path);
3294		return -1;
3295	}
3296	/*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
3297
3298	if (!cli_mkdir(targetcli, targetpath)) {
3299
3300		errno = smbc_errno(context, targetcli);
3301		return -1;
3302
3303	}
3304
3305	return 0;
3306
3307}
3308
3309/*
3310 * Our list function simply checks to see if a directory is not empty
3311 */
3312
3313static int smbc_rmdir_dirempty = True;
3314
3315static void
3316rmdir_list_fn(const char *mnt,
3317              file_info *finfo,
3318              const char *mask,
3319              void *state)
3320{
3321	if (strncmp(finfo->name, ".", 1) != 0 &&
3322            strncmp(finfo->name, "..", 2) != 0) {
3323
3324		smbc_rmdir_dirempty = False;
3325        }
3326}
3327
3328/*
3329 * Routine to remove a directory
3330 */
3331
3332static int
3333smbc_rmdir_ctx(SMBCCTX *context,
3334               const char *fname)
3335{
3336	SMBCSRV *srv;
3337	fstring server;
3338        fstring share;
3339        fstring user;
3340        fstring password;
3341        fstring workgroup;
3342	pstring path;
3343        pstring targetpath;
3344	struct cli_state *targetcli;
3345
3346	if (!context || !context->internal ||
3347	    !context->internal->_initialized) {
3348
3349		errno = EINVAL;
3350		return -1;
3351
3352	}
3353
3354	if (!fname) {
3355
3356		errno = EINVAL;
3357		return -1;
3358
3359	}
3360
3361	DEBUG(4, ("smbc_rmdir(%s)\n", fname));
3362
3363	if (smbc_parse_path(context, fname,
3364                            workgroup, sizeof(workgroup),
3365                            server, sizeof(server),
3366                            share, sizeof(share),
3367                            path, sizeof(path),
3368                            user, sizeof(user),
3369                            password, sizeof(password),
3370                            NULL, 0))
3371        {
3372                errno = EINVAL;
3373                return -1;
3374        }
3375
3376	if (user[0] == (char)0) fstrcpy(user, context->user);
3377
3378	srv = smbc_server(context, True,
3379                          server, share, workgroup, user, password);
3380
3381	if (!srv) {
3382
3383		return -1;  /* errno set by smbc_server */
3384
3385	}
3386
3387	/*d_printf(">>>rmdir: resolving %s\n", path);*/
3388	if (!cli_resolve_path( "", srv->cli, path, &targetcli, targetpath))
3389	{
3390		d_printf("Could not resolve %s\n", path);
3391		return -1;
3392	}
3393	/*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
3394
3395
3396	if (!cli_rmdir(targetcli, targetpath)) {
3397
3398		errno = smbc_errno(context, targetcli);
3399
3400		if (errno == EACCES) {  /* Check if the dir empty or not */
3401
3402                        /* Local storage to avoid buffer overflows */
3403			pstring lpath;
3404
3405			smbc_rmdir_dirempty = True;  /* Make this so ... */
3406
3407			pstrcpy(lpath, targetpath);
3408			pstrcat(lpath, "\\*");
3409
3410			if (cli_list(targetcli, lpath,
3411                                     aDIR | aSYSTEM | aHIDDEN,
3412                                     rmdir_list_fn, NULL) < 0) {
3413
3414				/* Fix errno to ignore latest error ... */
3415				DEBUG(5, ("smbc_rmdir: "
3416                                          "cli_list returned an error: %d\n",
3417					  smbc_errno(context, targetcli)));
3418				errno = EACCES;
3419
3420			}
3421
3422			if (smbc_rmdir_dirempty)
3423				errno = EACCES;
3424			else
3425				errno = ENOTEMPTY;
3426
3427		}
3428
3429		return -1;
3430
3431	}
3432
3433	return 0;
3434
3435}
3436
3437/*
3438 * Routine to return the current directory position
3439 */
3440
3441static off_t
3442smbc_telldir_ctx(SMBCCTX *context,
3443                 SMBCFILE *dir)
3444{
3445	off_t ret_val; /* Squash warnings about cast */
3446
3447	if (!context || !context->internal ||
3448	    !context->internal->_initialized) {
3449
3450		errno = EINVAL;
3451		return -1;
3452
3453	}
3454
3455	if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
3456
3457		errno = EBADF;
3458		return -1;
3459
3460	}
3461
3462	if (dir->file != False) { /* FIXME, should be dir, perhaps */
3463
3464		errno = ENOTDIR;
3465		return -1;
3466
3467	}
3468
3469	/*
3470	 * We return the pointer here as the offset
3471	 */
3472	ret_val = (off_t)(long)dir->dir_next;
3473	return ret_val;
3474
3475}
3476
3477/*
3478 * A routine to run down the list and see if the entry is OK
3479 */
3480
3481struct smbc_dir_list *
3482smbc_check_dir_ent(struct smbc_dir_list *list,
3483                   struct smbc_dirent *dirent)
3484{
3485
3486	/* Run down the list looking for what we want */
3487
3488	if (dirent) {
3489
3490		struct smbc_dir_list *tmp = list;
3491
3492		while (tmp) {
3493
3494			if (tmp->dirent == dirent)
3495				return tmp;
3496
3497			tmp = tmp->next;
3498
3499		}
3500
3501	}
3502
3503	return NULL;  /* Not found, or an error */
3504
3505}
3506
3507
3508/*
3509 * Routine to seek on a directory
3510 */
3511
3512static int
3513smbc_lseekdir_ctx(SMBCCTX *context,
3514                  SMBCFILE *dir,
3515                  off_t offset)
3516{
3517	long int l_offset = offset;  /* Handle problems of size */
3518	struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
3519	struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
3520
3521	if (!context || !context->internal ||
3522	    !context->internal->_initialized) {
3523
3524		errno = EINVAL;
3525		return -1;
3526
3527	}
3528
3529	if (dir->file != False) { /* FIXME, should be dir, perhaps */
3530
3531		errno = ENOTDIR;
3532		return -1;
3533
3534	}
3535
3536	/* Now, check what we were passed and see if it is OK ... */
3537
3538	if (dirent == NULL) {  /* Seek to the begining of the list */
3539
3540		dir->dir_next = dir->dir_list;
3541		return 0;
3542
3543	}
3544
3545	/* Now, run down the list and make sure that the entry is OK       */
3546	/* This may need to be changed if we change the format of the list */
3547
3548	if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
3549
3550		errno = EINVAL;   /* Bad entry */
3551		return -1;
3552
3553	}
3554
3555	dir->dir_next = list_ent;
3556
3557	return 0;
3558
3559}
3560
3561/*
3562 * Routine to fstat a dir
3563 */
3564
3565static int
3566smbc_fstatdir_ctx(SMBCCTX *context,
3567                  SMBCFILE *dir,
3568                  struct stat *st)
3569{
3570
3571	if (!context || !context->internal ||
3572	    !context->internal->_initialized) {
3573
3574		errno = EINVAL;
3575		return -1;
3576
3577	}
3578
3579	/* No code yet ... */
3580
3581	return 0;
3582
3583}
3584
3585static int
3586smbc_chmod_ctx(SMBCCTX *context,
3587               const char *fname,
3588               mode_t newmode)
3589{
3590        SMBCSRV *srv;
3591	fstring server;
3592        fstring share;
3593        fstring user;
3594        fstring password;
3595        fstring workgroup;
3596	pstring path;
3597	uint16 mode;
3598
3599	if (!context || !context->internal ||
3600	    !context->internal->_initialized) {
3601
3602		errno = EINVAL;  /* Best I can think of ... */
3603		return -1;
3604
3605	}
3606
3607	if (!fname) {
3608
3609		errno = EINVAL;
3610		return -1;
3611
3612	}
3613
3614	DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
3615
3616	if (smbc_parse_path(context, fname,
3617                            workgroup, sizeof(workgroup),
3618                            server, sizeof(server),
3619                            share, sizeof(share),
3620                            path, sizeof(path),
3621                            user, sizeof(user),
3622                            password, sizeof(password),
3623                            NULL, 0)) {
3624                errno = EINVAL;
3625                return -1;
3626        }
3627
3628	if (user[0] == (char)0) fstrcpy(user, context->user);
3629
3630	srv = smbc_server(context, True,
3631                          server, share, workgroup, user, password);
3632
3633	if (!srv) {
3634		return -1;  /* errno set by smbc_server */
3635	}
3636
3637	mode = 0;
3638
3639	if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
3640	if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
3641	if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
3642	if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
3643
3644	if (!cli_setatr(srv->cli, path, mode, 0)) {
3645		errno = smbc_errno(context, srv->cli);
3646		return -1;
3647	}
3648
3649        return 0;
3650}
3651
3652static int
3653smbc_utimes_ctx(SMBCCTX *context,
3654                const char *fname,
3655                struct timeval *tbuf)
3656{
3657        SMBCSRV *srv;
3658	fstring server;
3659        fstring share;
3660        fstring user;
3661        fstring password;
3662        fstring workgroup;
3663	pstring path;
3664        time_t access_time;
3665        time_t write_time;
3666
3667	if (!context || !context->internal ||
3668	    !context->internal->_initialized) {
3669
3670		errno = EINVAL;  /* Best I can think of ... */
3671		return -1;
3672
3673	}
3674
3675	if (!fname) {
3676
3677		errno = EINVAL;
3678		return -1;
3679
3680	}
3681
3682        if (tbuf == NULL) {
3683                access_time = write_time = time(NULL);
3684        } else {
3685                access_time = tbuf[0].tv_sec;
3686                write_time = tbuf[1].tv_sec;
3687        }
3688
3689        if (DEBUGLVL(4))
3690        {
3691                char *p;
3692                char atimebuf[32];
3693                char mtimebuf[32];
3694
3695                strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
3696                atimebuf[sizeof(atimebuf) - 1] = '\0';
3697                if ((p = strchr(atimebuf, '\n')) != NULL) {
3698                        *p = '\0';
3699                }
3700
3701                strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
3702                mtimebuf[sizeof(mtimebuf) - 1] = '\0';
3703                if ((p = strchr(mtimebuf, '\n')) != NULL) {
3704                        *p = '\0';
3705                }
3706
3707                dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
3708                        fname, atimebuf, mtimebuf);
3709        }
3710
3711	if (smbc_parse_path(context, fname,
3712                            workgroup, sizeof(workgroup),
3713                            server, sizeof(server),
3714                            share, sizeof(share),
3715                            path, sizeof(path),
3716                            user, sizeof(user),
3717                            password, sizeof(password),
3718                            NULL, 0)) {
3719                errno = EINVAL;
3720                return -1;
3721        }
3722
3723	if (user[0] == (char)0) fstrcpy(user, context->user);
3724
3725	srv = smbc_server(context, True,
3726                          server, share, workgroup, user, password);
3727
3728	if (!srv) {
3729		return -1;      /* errno set by smbc_server */
3730	}
3731
3732        if (!smbc_setatr(context, srv, path,
3733                         0, access_time, write_time, 0, 0)) {
3734                return -1;      /* errno set by smbc_setatr */
3735        }
3736
3737        return 0;
3738}
3739
3740
3741/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
3742   However NT4 gives a "The information may have been modified by a
3743   computer running Windows NT 5.0" if denied ACEs do not appear before
3744   allowed ACEs. */
3745
3746static int
3747ace_compare(SEC_ACE *ace1,
3748            SEC_ACE *ace2)
3749{
3750	if (sec_ace_equal(ace1, ace2))
3751		return 0;
3752
3753	if (ace1->type != ace2->type)
3754		return ace2->type - ace1->type;
3755
3756	if (sid_compare(&ace1->trustee, &ace2->trustee))
3757		return sid_compare(&ace1->trustee, &ace2->trustee);
3758
3759	if (ace1->flags != ace2->flags)
3760		return ace1->flags - ace2->flags;
3761
3762	if (ace1->access_mask != ace2->access_mask)
3763		return ace1->access_mask - ace2->access_mask;
3764
3765	if (ace1->size != ace2->size)
3766		return ace1->size - ace2->size;
3767
3768	return memcmp(ace1, ace2, sizeof(SEC_ACE));
3769}
3770
3771
3772static void
3773sort_acl(SEC_ACL *the_acl)
3774{
3775	uint32 i;
3776	if (!the_acl) return;
3777
3778	qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
3779              QSORT_CAST ace_compare);
3780
3781	for (i=1;i<the_acl->num_aces;) {
3782		if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
3783			int j;
3784			for (j=i; j<the_acl->num_aces-1; j++) {
3785				the_acl->aces[j] = the_acl->aces[j+1];
3786			}
3787			the_acl->num_aces--;
3788		} else {
3789			i++;
3790		}
3791	}
3792}
3793
3794/* convert a SID to a string, either numeric or username/group */
3795static void
3796convert_sid_to_string(struct cli_state *ipc_cli,
3797                      POLICY_HND *pol,
3798                      fstring str,
3799                      BOOL numeric,
3800                      DOM_SID *sid)
3801{
3802	char **domains = NULL;
3803	char **names = NULL;
3804	enum lsa_SidType *types = NULL;
3805	struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3806	sid_to_string(str, sid);
3807
3808	if (numeric) {
3809		return;     /* no lookup desired */
3810	}
3811
3812	if (!pipe_hnd) {
3813		return;
3814	}
3815
3816	/* Ask LSA to convert the sid to a name */
3817
3818	if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ipc_cli->mem_ctx,
3819						 pol, 1, sid, &domains,
3820						 &names, &types)) ||
3821	    !domains || !domains[0] || !names || !names[0]) {
3822		return;
3823	}
3824
3825	/* Converted OK */
3826
3827	slprintf(str, sizeof(fstring) - 1, "%s%s%s",
3828		 domains[0], lp_winbind_separator(),
3829		 names[0]);
3830}
3831
3832/* convert a string to a SID, either numeric or username/group */
3833static BOOL
3834convert_string_to_sid(struct cli_state *ipc_cli,
3835                      POLICY_HND *pol,
3836                      BOOL numeric,
3837                      DOM_SID *sid,
3838                      const char *str)
3839{
3840	enum lsa_SidType *types = NULL;
3841	DOM_SID *sids = NULL;
3842	BOOL result = True;
3843	struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
3844
3845	if (!pipe_hnd) {
3846		return False;
3847	}
3848
3849        if (numeric) {
3850                if (strncmp(str, "S-", 2) == 0) {
3851                        return string_to_sid(sid, str);
3852                }
3853
3854                result = False;
3855                goto done;
3856        }
3857
3858	if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ipc_cli->mem_ctx,
3859						  pol, 1, &str, NULL, &sids,
3860						  &types))) {
3861		result = False;
3862		goto done;
3863	}
3864
3865	sid_copy(sid, &sids[0]);
3866 done:
3867
3868	return result;
3869}
3870
3871
3872/* parse an ACE in the same format as print_ace() */
3873static BOOL
3874parse_ace(struct cli_state *ipc_cli,
3875          POLICY_HND *pol,
3876          SEC_ACE *ace,
3877          BOOL numeric,
3878          char *str)
3879{
3880	char *p;
3881	const char *cp;
3882	fstring tok;
3883	unsigned int atype;
3884        unsigned int aflags;
3885        unsigned int amask;
3886	DOM_SID sid;
3887	SEC_ACCESS mask;
3888	const struct perm_value *v;
3889        struct perm_value {
3890                const char *perm;
3891                uint32 mask;
3892        };
3893
3894        /* These values discovered by inspection */
3895        static const struct perm_value special_values[] = {
3896                { "R", 0x00120089 },
3897                { "W", 0x00120116 },
3898                { "X", 0x001200a0 },
3899                { "D", 0x00010000 },
3900                { "P", 0x00040000 },
3901                { "O", 0x00080000 },
3902                { NULL, 0 },
3903        };
3904
3905        static const struct perm_value standard_values[] = {
3906                { "READ",   0x001200a9 },
3907                { "CHANGE", 0x001301bf },
3908                { "FULL",   0x001f01ff },
3909                { NULL, 0 },
3910        };
3911
3912
3913	ZERO_STRUCTP(ace);
3914	p = strchr_m(str,':');
3915	if (!p) return False;
3916	*p = '\0';
3917	p++;
3918	/* Try to parse numeric form */
3919
3920	if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
3921	    convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3922		goto done;
3923	}
3924
3925	/* Try to parse text form */
3926
3927	if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
3928		return False;
3929	}
3930
3931	cp = p;
3932	if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3933		return False;
3934	}
3935
3936	if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
3937		atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
3938	} else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
3939		atype = SEC_ACE_TYPE_ACCESS_DENIED;
3940	} else {
3941		return False;
3942	}
3943
3944	/* Only numeric form accepted for flags at present */
3945
3946	if (!(next_token(&cp, tok, "/", sizeof(fstring)) &&
3947	      sscanf(tok, "%i", &aflags))) {
3948		return False;
3949	}
3950
3951	if (!next_token(&cp, tok, "/", sizeof(fstring))) {
3952		return False;
3953	}
3954
3955	if (strncmp(tok, "0x", 2) == 0) {
3956		if (sscanf(tok, "%i", &amask) != 1) {
3957			return False;
3958		}
3959		goto done;
3960	}
3961
3962	for (v = standard_values; v->perm; v++) {
3963		if (strcmp(tok, v->perm) == 0) {
3964			amask = v->mask;
3965			goto done;
3966		}
3967	}
3968
3969	p = tok;
3970
3971	while(*p) {
3972		BOOL found = False;
3973
3974		for (v = special_values; v->perm; v++) {
3975			if (v->perm[0] == *p) {
3976				amask |= v->mask;
3977				found = True;
3978			}
3979		}
3980
3981		if (!found) return False;
3982		p++;
3983	}
3984
3985	if (*p) {
3986		return False;
3987	}
3988
3989 done:
3990	mask = amask;
3991	init_sec_ace(ace, &sid, atype, mask, aflags);
3992	return True;
3993}
3994
3995/* add an ACE to a list of ACEs in a SEC_ACL */
3996static BOOL
3997add_ace(SEC_ACL **the_acl,
3998        SEC_ACE *ace,
3999        TALLOC_CTX *ctx)
4000{
4001	SEC_ACL *newacl;
4002	SEC_ACE *aces;
4003
4004	if (! *the_acl) {
4005		(*the_acl) = make_sec_acl(ctx, 3, 1, ace);
4006		return True;
4007	}
4008
4009	if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) {
4010		return False;
4011	}
4012	memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
4013	memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
4014	newacl = make_sec_acl(ctx, (*the_acl)->revision,
4015                              1+(*the_acl)->num_aces, aces);
4016	SAFE_FREE(aces);
4017	(*the_acl) = newacl;
4018	return True;
4019}
4020
4021
4022/* parse a ascii version of a security descriptor */
4023static SEC_DESC *
4024sec_desc_parse(TALLOC_CTX *ctx,
4025               struct cli_state *ipc_cli,
4026               POLICY_HND *pol,
4027               BOOL numeric,
4028               char *str)
4029{
4030	const char *p = str;
4031	fstring tok;
4032	SEC_DESC *ret = NULL;
4033	size_t sd_size;
4034	DOM_SID *group_sid=NULL;
4035        DOM_SID *owner_sid=NULL;
4036	SEC_ACL *dacl=NULL;
4037	int revision=1;
4038
4039	while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4040
4041		if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
4042			revision = strtol(tok+9, NULL, 16);
4043			continue;
4044		}
4045
4046		if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
4047			if (owner_sid) {
4048				DEBUG(5, ("OWNER specified more than once!\n"));
4049				goto done;
4050			}
4051			owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4052			if (!owner_sid ||
4053			    !convert_string_to_sid(ipc_cli, pol,
4054                                                   numeric,
4055                                                   owner_sid, tok+6)) {
4056				DEBUG(5, ("Failed to parse owner sid\n"));
4057				goto done;
4058			}
4059			continue;
4060		}
4061
4062		if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
4063			if (owner_sid) {
4064				DEBUG(5, ("OWNER specified more than once!\n"));
4065				goto done;
4066			}
4067			owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4068			if (!owner_sid ||
4069			    !convert_string_to_sid(ipc_cli, pol,
4070                                                   False,
4071                                                   owner_sid, tok+7)) {
4072				DEBUG(5, ("Failed to parse owner sid\n"));
4073				goto done;
4074			}
4075			continue;
4076		}
4077
4078		if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
4079			if (group_sid) {
4080				DEBUG(5, ("GROUP specified more than once!\n"));
4081				goto done;
4082			}
4083			group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4084			if (!group_sid ||
4085			    !convert_string_to_sid(ipc_cli, pol,
4086                                                   numeric,
4087                                                   group_sid, tok+6)) {
4088				DEBUG(5, ("Failed to parse group sid\n"));
4089				goto done;
4090			}
4091			continue;
4092		}
4093
4094		if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
4095			if (group_sid) {
4096				DEBUG(5, ("GROUP specified more than once!\n"));
4097				goto done;
4098			}
4099			group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
4100			if (!group_sid ||
4101			    !convert_string_to_sid(ipc_cli, pol,
4102                                                   False,
4103                                                   group_sid, tok+6)) {
4104				DEBUG(5, ("Failed to parse group sid\n"));
4105				goto done;
4106			}
4107			continue;
4108		}
4109
4110		if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
4111			SEC_ACE ace;
4112			if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
4113				DEBUG(5, ("Failed to parse ACL %s\n", tok));
4114				goto done;
4115			}
4116			if(!add_ace(&dacl, &ace, ctx)) {
4117				DEBUG(5, ("Failed to add ACL %s\n", tok));
4118				goto done;
4119			}
4120			continue;
4121		}
4122
4123		if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
4124			SEC_ACE ace;
4125			if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
4126				DEBUG(5, ("Failed to parse ACL %s\n", tok));
4127				goto done;
4128			}
4129			if(!add_ace(&dacl, &ace, ctx)) {
4130				DEBUG(5, ("Failed to add ACL %s\n", tok));
4131				goto done;
4132			}
4133			continue;
4134		}
4135
4136		DEBUG(5, ("Failed to parse security descriptor\n"));
4137		goto done;
4138	}
4139
4140	ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
4141			    owner_sid, group_sid, NULL, dacl, &sd_size);
4142
4143  done:
4144	SAFE_FREE(group_sid);
4145	SAFE_FREE(owner_sid);
4146
4147	return ret;
4148}
4149
4150
4151/* Obtain the current dos attributes */
4152static DOS_ATTR_DESC *
4153dos_attr_query(SMBCCTX *context,
4154               TALLOC_CTX *ctx,
4155               const char *filename,
4156               SMBCSRV *srv)
4157{
4158        struct timespec create_time_ts;
4159        struct timespec write_time_ts;
4160        struct timespec access_time_ts;
4161        struct timespec change_time_ts;
4162        SMB_OFF_T size = 0;
4163        uint16 mode = 0;
4164	SMB_INO_T inode = 0;
4165        DOS_ATTR_DESC *ret;
4166
4167        ret = TALLOC_P(ctx, DOS_ATTR_DESC);
4168        if (!ret) {
4169                errno = ENOMEM;
4170                return NULL;
4171        }
4172
4173        /* Obtain the DOS attributes */
4174        if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename),
4175                         &mode, &size,
4176                         &create_time_ts,
4177                         &access_time_ts,
4178                         &write_time_ts,
4179                         &change_time_ts,
4180                         &inode)) {
4181
4182                errno = smbc_errno(context, srv->cli);
4183                DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
4184                return NULL;
4185
4186        }
4187
4188        ret->mode = mode;
4189        ret->size = size;
4190        ret->create_time = convert_timespec_to_time_t(create_time_ts);
4191        ret->access_time = convert_timespec_to_time_t(access_time_ts);
4192        ret->write_time = convert_timespec_to_time_t(write_time_ts);
4193        ret->change_time = convert_timespec_to_time_t(change_time_ts);
4194        ret->inode = inode;
4195
4196        return ret;
4197}
4198
4199
4200/* parse a ascii version of a security descriptor */
4201static void
4202dos_attr_parse(SMBCCTX *context,
4203               DOS_ATTR_DESC *dad,
4204               SMBCSRV *srv,
4205               char *str)
4206{
4207        int n;
4208        const char *p = str;
4209	fstring tok;
4210        struct {
4211                const char * create_time_attr;
4212                const char * access_time_attr;
4213                const char * write_time_attr;
4214                const char * change_time_attr;
4215        } attr_strings;
4216
4217        /* Determine whether to use old-style or new-style attribute names */
4218        if (context->internal->_full_time_names) {
4219                /* new-style names */
4220                attr_strings.create_time_attr = "CREATE_TIME";
4221                attr_strings.access_time_attr = "ACCESS_TIME";
4222                attr_strings.write_time_attr = "WRITE_TIME";
4223                attr_strings.change_time_attr = "CHANGE_TIME";
4224        } else {
4225                /* old-style names */
4226                attr_strings.create_time_attr = NULL;
4227                attr_strings.access_time_attr = "A_TIME";
4228                attr_strings.write_time_attr = "M_TIME";
4229                attr_strings.change_time_attr = "C_TIME";
4230        }
4231
4232        /* if this is to set the entire ACL... */
4233        if (*str == '*') {
4234                /* ... then increment past the first colon if there is one */
4235                if ((p = strchr(str, ':')) != NULL) {
4236                        ++p;
4237                } else {
4238                        p = str;
4239                }
4240        }
4241
4242	while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) {
4243
4244		if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
4245			dad->mode = strtol(tok+5, NULL, 16);
4246			continue;
4247		}
4248
4249		if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
4250                        dad->size = (SMB_OFF_T)atof(tok+5);
4251			continue;
4252		}
4253
4254                n = strlen(attr_strings.access_time_attr);
4255                if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
4256                        dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
4257			continue;
4258		}
4259
4260                n = strlen(attr_strings.change_time_attr);
4261                if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
4262                        dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
4263			continue;
4264		}
4265
4266                n = strlen(attr_strings.write_time_attr);
4267                if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
4268                        dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
4269			continue;
4270		}
4271
4272		if (attr_strings.create_time_attr != NULL) {
4273			n = strlen(attr_strings.create_time_attr);
4274			if (StrnCaseCmp(tok, attr_strings.create_time_attr,
4275					n) == 0) {
4276				dad->create_time = (time_t)strtol(tok+n+1,
4277								  NULL, 10);
4278				continue;
4279			}
4280		}
4281
4282		if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
4283                        dad->inode = (SMB_INO_T)atof(tok+6);
4284			continue;
4285		}
4286	}
4287}
4288
4289/*****************************************************
4290 Retrieve the acls for a file.
4291*******************************************************/
4292
4293static int
4294cacl_get(SMBCCTX *context,
4295         TALLOC_CTX *ctx,
4296         SMBCSRV *srv,
4297         struct cli_state *ipc_cli,
4298         POLICY_HND *pol,
4299         char *filename,
4300         char *attr_name,
4301         char *buf,
4302         int bufsize)
4303{
4304	uint32 i;
4305        int n = 0;
4306        int n_used;
4307        BOOL all;
4308        BOOL all_nt;
4309        BOOL all_nt_acls;
4310        BOOL all_dos;
4311        BOOL some_nt;
4312        BOOL some_dos;
4313        BOOL exclude_nt_revision = False;
4314        BOOL exclude_nt_owner = False;
4315        BOOL exclude_nt_group = False;
4316        BOOL exclude_nt_acl = False;
4317        BOOL exclude_dos_mode = False;
4318        BOOL exclude_dos_size = False;
4319        BOOL exclude_dos_create_time = False;
4320        BOOL exclude_dos_access_time = False;
4321        BOOL exclude_dos_write_time = False;
4322        BOOL exclude_dos_change_time = False;
4323        BOOL exclude_dos_inode = False;
4324        BOOL numeric = True;
4325        BOOL determine_size = (bufsize == 0);
4326	int fnum = -1;
4327	SEC_DESC *sd;
4328	fstring sidstr;
4329        fstring name_sandbox;
4330        char *name;
4331        char *pExclude;
4332        char *p;
4333        struct timespec create_time_ts;
4334	struct timespec write_time_ts;
4335        struct timespec access_time_ts;
4336        struct timespec change_time_ts;
4337	time_t create_time = (time_t)0;
4338	time_t write_time = (time_t)0;
4339        time_t access_time = (time_t)0;
4340        time_t change_time = (time_t)0;
4341	SMB_OFF_T size = 0;
4342	uint16 mode = 0;
4343	SMB_INO_T ino = 0;
4344        struct cli_state *cli = srv->cli;
4345        struct {
4346                const char * create_time_attr;
4347                const char * access_time_attr;
4348                const char * write_time_attr;
4349                const char * change_time_attr;
4350        } attr_strings;
4351        struct {
4352                const char * create_time_attr;
4353                const char * access_time_attr;
4354                const char * write_time_attr;
4355                const char * change_time_attr;
4356        } excl_attr_strings;
4357
4358        /* Determine whether to use old-style or new-style attribute names */
4359        if (context->internal->_full_time_names) {
4360                /* new-style names */
4361                attr_strings.create_time_attr = "CREATE_TIME";
4362                attr_strings.access_time_attr = "ACCESS_TIME";
4363                attr_strings.write_time_attr = "WRITE_TIME";
4364                attr_strings.change_time_attr = "CHANGE_TIME";
4365
4366                excl_attr_strings.create_time_attr = "CREATE_TIME";
4367                excl_attr_strings.access_time_attr = "ACCESS_TIME";
4368                excl_attr_strings.write_time_attr = "WRITE_TIME";
4369                excl_attr_strings.change_time_attr = "CHANGE_TIME";
4370        } else {
4371                /* old-style names */
4372                attr_strings.create_time_attr = NULL;
4373                attr_strings.access_time_attr = "A_TIME";
4374                attr_strings.write_time_attr = "M_TIME";
4375                attr_strings.change_time_attr = "C_TIME";
4376
4377                excl_attr_strings.create_time_attr = NULL;
4378                excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
4379                excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
4380                excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
4381        }
4382
4383        /* Copy name so we can strip off exclusions (if any are specified) */
4384        strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
4385
4386        /* Ensure name is null terminated */
4387        name_sandbox[sizeof(name_sandbox) - 1] = '\0';
4388
4389        /* Play in the sandbox */
4390        name = name_sandbox;
4391
4392        /* If there are any exclusions, point to them and mask them from name */
4393        if ((pExclude = strchr(name, '!')) != NULL)
4394        {
4395                *pExclude++ = '\0';
4396        }
4397
4398        all = (StrnCaseCmp(name, "system.*", 8) == 0);
4399        all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
4400        all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
4401        all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
4402        some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
4403        some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
4404        numeric = (* (name + strlen(name) - 1) != '+');
4405
4406        /* Look for exclusions from "all" requests */
4407        if (all || all_nt || all_dos) {
4408
4409                /* Exclusions are delimited by '!' */
4410                for (;
4411                     pExclude != NULL;
4412                     pExclude = (p == NULL ? NULL : p + 1)) {
4413
4414                /* Find end of this exclusion name */
4415                if ((p = strchr(pExclude, '!')) != NULL)
4416                {
4417                    *p = '\0';
4418                }
4419
4420                /* Which exclusion name is this? */
4421                if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) {
4422                    exclude_nt_revision = True;
4423                }
4424                else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) {
4425                    exclude_nt_owner = True;
4426                }
4427                else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) {
4428                    exclude_nt_group = True;
4429                }
4430                else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) {
4431                    exclude_nt_acl = True;
4432                }
4433                else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) {
4434                    exclude_dos_mode = True;
4435                }
4436                else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) {
4437                    exclude_dos_size = True;
4438                }
4439                else if (excl_attr_strings.create_time_attr != NULL &&
4440                         StrCaseCmp(pExclude,
4441                                    excl_attr_strings.change_time_attr) == 0) {
4442                    exclude_dos_create_time = True;
4443                }
4444                else if (StrCaseCmp(pExclude,
4445                                    excl_attr_strings.access_time_attr) == 0) {
4446                    exclude_dos_access_time = True;
4447                }
4448                else if (StrCaseCmp(pExclude,
4449                                    excl_attr_strings.write_time_attr) == 0) {
4450                    exclude_dos_write_time = True;
4451                }
4452                else if (StrCaseCmp(pExclude,
4453                                    excl_attr_strings.change_time_attr) == 0) {
4454                    exclude_dos_change_time = True;
4455                }
4456                else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
4457                    exclude_dos_inode = True;
4458                }
4459                else {
4460                    DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
4461                              pExclude));
4462                    errno = ENOATTR;
4463                    return -1;
4464                }
4465            }
4466        }
4467
4468        n_used = 0;
4469
4470        /*
4471         * If we are (possibly) talking to an NT or new system and some NT
4472         * attributes have been requested...
4473         */
4474        if (ipc_cli && (all || some_nt || all_nt_acls)) {
4475                /* Point to the portion after "system.nt_sec_desc." */
4476                name += 19;     /* if (all) this will be invalid but unused */
4477
4478                /* ... then obtain any NT attributes which were requested */
4479                fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
4480
4481                if (fnum == -1) {
4482                        DEBUG(5, ("cacl_get failed to open %s: %s\n",
4483                                  filename, cli_errstr(cli)));
4484                        errno = 0;
4485                        return -1;
4486                }
4487
4488                sd = cli_query_secdesc(cli, fnum, ctx);
4489
4490                if (!sd) {
4491                        DEBUG(5,
4492                              ("cacl_get Failed to query old descriptor\n"));
4493                        errno = 0;
4494                        return -1;
4495                }
4496
4497                cli_close(cli, fnum);
4498
4499                if (! exclude_nt_revision) {
4500                        if (all || all_nt) {
4501                                if (determine_size) {
4502                                        p = talloc_asprintf(ctx,
4503                                                            "REVISION:%d",
4504                                                            sd->revision);
4505                                        if (!p) {
4506                                                errno = ENOMEM;
4507                                                return -1;
4508                                        }
4509                                        n = strlen(p);
4510                                } else {
4511                                        n = snprintf(buf, bufsize,
4512                                                     "REVISION:%d",
4513                                                     sd->revision);
4514                                }
4515                        } else if (StrCaseCmp(name, "revision") == 0) {
4516                                if (determine_size) {
4517                                        p = talloc_asprintf(ctx, "%d",
4518                                                            sd->revision);
4519                                        if (!p) {
4520                                                errno = ENOMEM;
4521                                                return -1;
4522                                        }
4523                                        n = strlen(p);
4524                                } else {
4525                                        n = snprintf(buf, bufsize, "%d",
4526                                                     sd->revision);
4527                                }
4528                        }
4529
4530                        if (!determine_size && n > bufsize) {
4531                                errno = ERANGE;
4532                                return -1;
4533                        }
4534                        buf += n;
4535                        n_used += n;
4536                        bufsize -= n;
4537                }
4538
4539                if (! exclude_nt_owner) {
4540                        /* Get owner and group sid */
4541                        if (sd->owner_sid) {
4542                                convert_sid_to_string(ipc_cli, pol,
4543                                                      sidstr,
4544                                                      numeric,
4545                                                      sd->owner_sid);
4546                        } else {
4547                                fstrcpy(sidstr, "");
4548                        }
4549
4550                        if (all || all_nt) {
4551                                if (determine_size) {
4552                                        p = talloc_asprintf(ctx, ",OWNER:%s",
4553                                                            sidstr);
4554                                        if (!p) {
4555                                                errno = ENOMEM;
4556                                                return -1;
4557                                        }
4558                                        n = strlen(p);
4559                                } else if (sidstr[0] != '\0') {
4560                                        n = snprintf(buf, bufsize,
4561                                                     ",OWNER:%s", sidstr);
4562                                }
4563                        } else if (StrnCaseCmp(name, "owner", 5) == 0) {
4564                                if (determine_size) {
4565                                        p = talloc_asprintf(ctx, "%s", sidstr);
4566                                        if (!p) {
4567                                                errno = ENOMEM;
4568                                                return -1;
4569                                        }
4570                                        n = strlen(p);
4571                                } else {
4572                                        n = snprintf(buf, bufsize, "%s",
4573                                                     sidstr);
4574                                }
4575                        }
4576
4577                        if (!determine_size && n > bufsize) {
4578                                errno = ERANGE;
4579                                return -1;
4580                        }
4581                        buf += n;
4582                        n_used += n;
4583                        bufsize -= n;
4584                }
4585
4586                if (! exclude_nt_group) {
4587                        if (sd->group_sid) {
4588                                convert_sid_to_string(ipc_cli, pol,
4589                                                      sidstr, numeric,
4590                                                      sd->group_sid);
4591                        } else {
4592                                fstrcpy(sidstr, "");
4593                        }
4594
4595                        if (all || all_nt) {
4596                                if (determine_size) {
4597                                        p = talloc_asprintf(ctx, ",GROUP:%s",
4598                                                            sidstr);
4599                                        if (!p) {
4600                                                errno = ENOMEM;
4601                                                return -1;
4602                                        }
4603                                        n = strlen(p);
4604                                } else if (sidstr[0] != '\0') {
4605                                        n = snprintf(buf, bufsize,
4606                                                     ",GROUP:%s", sidstr);
4607                                }
4608                        } else if (StrnCaseCmp(name, "group", 5) == 0) {
4609                                if (determine_size) {
4610                                        p = talloc_asprintf(ctx, "%s", sidstr);
4611                                        if (!p) {
4612                                                errno = ENOMEM;
4613                                                return -1;
4614                                        }
4615                                        n = strlen(p);
4616                                } else {
4617                                        n = snprintf(buf, bufsize,
4618                                                     "%s", sidstr);
4619                                }
4620                        }
4621
4622                        if (!determine_size && n > bufsize) {
4623                                errno = ERANGE;
4624                                return -1;
4625                        }
4626                        buf += n;
4627                        n_used += n;
4628                        bufsize -= n;
4629                }
4630
4631                if (! exclude_nt_acl) {
4632                        /* Add aces to value buffer  */
4633                        for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
4634
4635                                SEC_ACE *ace = &sd->dacl->aces[i];
4636                                convert_sid_to_string(ipc_cli, pol,
4637                                                      sidstr, numeric,
4638                                                      &ace->trustee);
4639
4640                                if (all || all_nt) {
4641                                        if (determine_size) {
4642                                                p = talloc_asprintf(
4643                                                        ctx,
4644                                                        ",ACL:"
4645                                                        "%s:%d/%d/0x%08x",
4646                                                        sidstr,
4647                                                        ace->type,
4648                                                        ace->flags,
4649                                                        ace->access_mask);
4650                                                if (!p) {
4651                                                        errno = ENOMEM;
4652                                                        return -1;
4653                                                }
4654                                                n = strlen(p);
4655                                        } else {
4656                                                n = snprintf(
4657                                                        buf, bufsize,
4658                                                        ",ACL:%s:%d/%d/0x%08x",
4659                                                        sidstr,
4660                                                        ace->type,
4661                                                        ace->flags,
4662                                                        ace->access_mask);
4663                                        }
4664                                } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
4665                                            StrCaseCmp(name+3, sidstr) == 0) ||
4666                                           (StrnCaseCmp(name, "acl+", 4) == 0 &&
4667                                            StrCaseCmp(name+4, sidstr) == 0)) {
4668                                        if (determine_size) {
4669                                                p = talloc_asprintf(
4670                                                        ctx,
4671                                                        "%d/%d/0x%08x",
4672                                                        ace->type,
4673                                                        ace->flags,
4674                                                        ace->access_mask);
4675                                                if (!p) {
4676                                                        errno = ENOMEM;
4677                                                        return -1;
4678                                                }
4679                                                n = strlen(p);
4680                                        } else {
4681                                                n = snprintf(buf, bufsize,
4682                                                             "%d/%d/0x%08x",
4683                                                             ace->type,
4684                                                             ace->flags,
4685                                                             ace->access_mask);
4686                                        }
4687                                } else if (all_nt_acls) {
4688                                        if (determine_size) {
4689                                                p = talloc_asprintf(
4690                                                        ctx,
4691                                                        "%s%s:%d/%d/0x%08x",
4692                                                        i ? "," : "",
4693                                                        sidstr,
4694                                                        ace->type,
4695                                                        ace->flags,
4696                                                        ace->access_mask);
4697                                                if (!p) {
4698                                                        errno = ENOMEM;
4699                                                        return -1;
4700                                                }
4701                                                n = strlen(p);
4702                                        } else {
4703                                                n = snprintf(buf, bufsize,
4704                                                             "%s%s:%d/%d/0x%08x",
4705                                                             i ? "," : "",
4706                                                             sidstr,
4707                                                             ace->type,
4708                                                             ace->flags,
4709                                                             ace->access_mask);
4710                                        }
4711                                }
4712                                if (!determine_size && n > bufsize) {
4713                                        errno = ERANGE;
4714                                        return -1;
4715                                }
4716                                buf += n;
4717                                n_used += n;
4718                                bufsize -= n;
4719                        }
4720                }
4721
4722                /* Restore name pointer to its original value */
4723                name -= 19;
4724        }
4725
4726        if (all || some_dos) {
4727                /* Point to the portion after "system.dos_attr." */
4728                name += 16;     /* if (all) this will be invalid but unused */
4729
4730                /* Obtain the DOS attributes */
4731                if (!smbc_getatr(context, srv, filename, &mode, &size,
4732                                 &create_time_ts,
4733                                 &access_time_ts,
4734                                 &write_time_ts,
4735                                 &change_time_ts,
4736                                 &ino)) {
4737
4738                        errno = smbc_errno(context, srv->cli);
4739                        return -1;
4740
4741                }
4742
4743                create_time = convert_timespec_to_time_t(create_time_ts);
4744                access_time = convert_timespec_to_time_t(access_time_ts);
4745                write_time = convert_timespec_to_time_t(write_time_ts);
4746                change_time = convert_timespec_to_time_t(change_time_ts);
4747
4748                if (! exclude_dos_mode) {
4749                        if (all || all_dos) {
4750                                if (determine_size) {
4751                                        p = talloc_asprintf(ctx,
4752                                                            "%sMODE:0x%x",
4753                                                            (ipc_cli &&
4754                                                             (all || some_nt)
4755                                                             ? ","
4756                                                             : ""),
4757                                                            mode);
4758                                        if (!p) {
4759                                                errno = ENOMEM;
4760                                                return -1;
4761                                        }
4762                                        n = strlen(p);
4763                                } else {
4764                                        n = snprintf(buf, bufsize,
4765                                                     "%sMODE:0x%x",
4766                                                     (ipc_cli &&
4767                                                      (all || some_nt)
4768                                                      ? ","
4769                                                      : ""),
4770                                                     mode);
4771                                }
4772                        } else if (StrCaseCmp(name, "mode") == 0) {
4773                                if (determine_size) {
4774                                        p = talloc_asprintf(ctx, "0x%x", mode);
4775                                        if (!p) {
4776                                                errno = ENOMEM;
4777                                                return -1;
4778                                        }
4779                                        n = strlen(p);
4780                                } else {
4781                                        n = snprintf(buf, bufsize,
4782                                                     "0x%x", mode);
4783                                }
4784                        }
4785
4786                        if (!determine_size && n > bufsize) {
4787                                errno = ERANGE;
4788                                return -1;
4789                        }
4790                        buf += n;
4791                        n_used += n;
4792                        bufsize -= n;
4793                }
4794
4795                if (! exclude_dos_size) {
4796                        if (all || all_dos) {
4797                                if (determine_size) {
4798                                        p = talloc_asprintf(
4799                                                ctx,
4800                                                ",SIZE:%.0f",
4801                                                (double)size);
4802                                        if (!p) {
4803                                                errno = ENOMEM;
4804                                                return -1;
4805                                        }
4806                                        n = strlen(p);
4807                                } else {
4808                                        n = snprintf(buf, bufsize,
4809                                                     ",SIZE:%.0f",
4810                                                     (double)size);
4811                                }
4812                        } else if (StrCaseCmp(name, "size") == 0) {
4813                                if (determine_size) {
4814                                        p = talloc_asprintf(
4815                                                ctx,
4816                                                "%.0f",
4817                                                (double)size);
4818                                        if (!p) {
4819                                                errno = ENOMEM;
4820                                                return -1;
4821                                        }
4822                                        n = strlen(p);
4823                                } else {
4824                                        n = snprintf(buf, bufsize,
4825                                                     "%.0f",
4826                                                     (double)size);
4827                                }
4828                        }
4829
4830                        if (!determine_size && n > bufsize) {
4831                                errno = ERANGE;
4832                                return -1;
4833                        }
4834                        buf += n;
4835                        n_used += n;
4836                        bufsize -= n;
4837                }
4838
4839                if (! exclude_dos_create_time &&
4840                    attr_strings.create_time_attr != NULL) {
4841                        if (all || all_dos) {
4842                                if (determine_size) {
4843                                        p = talloc_asprintf(ctx,
4844                                                            ",%s:%lu",
4845                                                            attr_strings.create_time_attr,
4846                                                            create_time);
4847                                        if (!p) {
4848                                                errno = ENOMEM;
4849                                                return -1;
4850                                        }
4851                                        n = strlen(p);
4852                                } else {
4853                                        n = snprintf(buf, bufsize,
4854                                                     ",%s:%lu",
4855                                                     attr_strings.create_time_attr,
4856                                                     create_time);
4857                                }
4858                        } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
4859                                if (determine_size) {
4860                                        p = talloc_asprintf(ctx, "%lu", create_time);
4861                                        if (!p) {
4862                                                errno = ENOMEM;
4863                                                return -1;
4864                                        }
4865                                        n = strlen(p);
4866                                } else {
4867                                        n = snprintf(buf, bufsize,
4868                                                     "%lu", create_time);
4869                                }
4870                        }
4871
4872                        if (!determine_size && n > bufsize) {
4873                                errno = ERANGE;
4874                                return -1;
4875                        }
4876                        buf += n;
4877                        n_used += n;
4878                        bufsize -= n;
4879                }
4880
4881                if (! exclude_dos_access_time) {
4882                        if (all || all_dos) {
4883                                if (determine_size) {
4884                                        p = talloc_asprintf(ctx,
4885                                                            ",%s:%lu",
4886                                                            attr_strings.access_time_attr,
4887                                                            access_time);
4888                                        if (!p) {
4889                                                errno = ENOMEM;
4890                                                return -1;
4891                                        }
4892                                        n = strlen(p);
4893                                } else {
4894                                        n = snprintf(buf, bufsize,
4895                                                     ",%s:%lu",
4896                                                     attr_strings.access_time_attr,
4897                                                     access_time);
4898                                }
4899                        } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
4900                                if (determine_size) {
4901                                        p = talloc_asprintf(ctx, "%lu", access_time);
4902                                        if (!p) {
4903                                                errno = ENOMEM;
4904                                                return -1;
4905                                        }
4906                                        n = strlen(p);
4907                                } else {
4908                                        n = snprintf(buf, bufsize,
4909                                                     "%lu", access_time);
4910                                }
4911                        }
4912
4913                        if (!determine_size && n > bufsize) {
4914                                errno = ERANGE;
4915                                return -1;
4916                        }
4917                        buf += n;
4918                        n_used += n;
4919                        bufsize -= n;
4920                }
4921
4922                if (! exclude_dos_write_time) {
4923                        if (all || all_dos) {
4924                                if (determine_size) {
4925                                        p = talloc_asprintf(ctx,
4926                                                            ",%s:%lu",
4927                                                            attr_strings.write_time_attr,
4928                                                            write_time);
4929                                        if (!p) {
4930                                                errno = ENOMEM;
4931                                                return -1;
4932                                        }
4933                                        n = strlen(p);
4934                                } else {
4935                                        n = snprintf(buf, bufsize,
4936                                                     ",%s:%lu",
4937                                                     attr_strings.write_time_attr,
4938                                                     write_time);
4939                                }
4940                        } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
4941                                if (determine_size) {
4942                                        p = talloc_asprintf(ctx, "%lu", write_time);
4943                                        if (!p) {
4944                                                errno = ENOMEM;
4945                                                return -1;
4946                                        }
4947                                        n = strlen(p);
4948                                } else {
4949                                        n = snprintf(buf, bufsize,
4950                                                     "%lu", write_time);
4951                                }
4952                        }
4953
4954                        if (!determine_size && n > bufsize) {
4955                                errno = ERANGE;
4956                                return -1;
4957                        }
4958                        buf += n;
4959                        n_used += n;
4960                        bufsize -= n;
4961                }
4962
4963                if (! exclude_dos_change_time) {
4964                        if (all || all_dos) {
4965                                if (determine_size) {
4966                                        p = talloc_asprintf(ctx,
4967                                                            ",%s:%lu",
4968                                                            attr_strings.change_time_attr,
4969                                                            change_time);
4970                                        if (!p) {
4971                                                errno = ENOMEM;
4972                                                return -1;
4973                                        }
4974                                        n = strlen(p);
4975                                } else {
4976                                        n = snprintf(buf, bufsize,
4977                                                     ",%s:%lu",
4978                                                     attr_strings.change_time_attr,
4979                                                     change_time);
4980                                }
4981                        } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
4982                                if (determine_size) {
4983                                        p = talloc_asprintf(ctx, "%lu", change_time);
4984                                        if (!p) {
4985                                                errno = ENOMEM;
4986                                                return -1;
4987                                        }
4988                                        n = strlen(p);
4989                                } else {
4990                                        n = snprintf(buf, bufsize,
4991                                                     "%lu", change_time);
4992                                }
4993                        }
4994
4995                        if (!determine_size && n > bufsize) {
4996                                errno = ERANGE;
4997                                return -1;
4998                        }
4999                        buf += n;
5000                        n_used += n;
5001                        bufsize -= n;
5002                }
5003
5004                if (! exclude_dos_inode) {
5005                        if (all || all_dos) {
5006                                if (determine_size) {
5007                                        p = talloc_asprintf(
5008                                                ctx,
5009                                                ",INODE:%.0f",
5010                                                (double)ino);
5011                                        if (!p) {
5012                                                errno = ENOMEM;
5013                                                return -1;
5014                                        }
5015                                        n = strlen(p);
5016                                } else {
5017                                        n = snprintf(buf, bufsize,
5018                                                     ",INODE:%.0f",
5019                                                     (double) ino);
5020                                }
5021                        } else if (StrCaseCmp(name, "inode") == 0) {
5022                                if (determine_size) {
5023                                        p = talloc_asprintf(
5024                                                ctx,
5025                                                "%.0f",
5026                                                (double) ino);
5027                                        if (!p) {
5028                                                errno = ENOMEM;
5029                                                return -1;
5030                                        }
5031                                        n = strlen(p);
5032                                } else {
5033                                        n = snprintf(buf, bufsize,
5034                                                     "%.0f",
5035                                                     (double) ino);
5036                                }
5037                        }
5038
5039                        if (!determine_size && n > bufsize) {
5040                                errno = ERANGE;
5041                                return -1;
5042                        }
5043                        buf += n;
5044                        n_used += n;
5045                        bufsize -= n;
5046                }
5047
5048                /* Restore name pointer to its original value */
5049                name -= 16;
5050        }
5051
5052        if (n_used == 0) {
5053                errno = ENOATTR;
5054                return -1;
5055        }
5056
5057	return n_used;
5058}
5059
5060
5061/*****************************************************
5062set the ACLs on a file given an ascii description
5063*******************************************************/
5064static int
5065cacl_set(TALLOC_CTX *ctx,
5066         struct cli_state *cli,
5067         struct cli_state *ipc_cli,
5068         POLICY_HND *pol,
5069         const char *filename,
5070         const char *the_acl,
5071         int mode,
5072         int flags)
5073{
5074	int fnum;
5075        int err = 0;
5076	SEC_DESC *sd = NULL, *old;
5077        SEC_ACL *dacl = NULL;
5078	DOM_SID *owner_sid = NULL;
5079	DOM_SID *group_sid = NULL;
5080	uint32 i, j;
5081	size_t sd_size;
5082	int ret = 0;
5083        char *p;
5084        BOOL numeric = True;
5085
5086        /* the_acl will be null for REMOVE_ALL operations */
5087        if (the_acl) {
5088                numeric = ((p = strchr(the_acl, ':')) != NULL &&
5089                           p > the_acl &&
5090                           p[-1] != '+');
5091
5092                /* if this is to set the entire ACL... */
5093                if (*the_acl == '*') {
5094                        /* ... then increment past the first colon */
5095                        the_acl = p + 1;
5096                }
5097
5098                sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
5099                                    CONST_DISCARD(char *, the_acl));
5100
5101                if (!sd) {
5102			errno = EINVAL;
5103			return -1;
5104                }
5105        }
5106
5107	/* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
5108	   that doesn't deref sd */
5109
5110	if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
5111		errno = EINVAL;
5112		return -1;
5113	}
5114
5115	/* The desired access below is the only one I could find that works
5116	   with NT4, W2KP and Samba */
5117
5118	fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
5119
5120	if (fnum == -1) {
5121                DEBUG(5, ("cacl_set failed to open %s: %s\n",
5122                          filename, cli_errstr(cli)));
5123                errno = 0;
5124		return -1;
5125	}
5126
5127	old = cli_query_secdesc(cli, fnum, ctx);
5128
5129	if (!old) {
5130                DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
5131                errno = 0;
5132		return -1;
5133	}
5134
5135	cli_close(cli, fnum);
5136
5137	switch (mode) {
5138	case SMBC_XATTR_MODE_REMOVE_ALL:
5139                old->dacl->num_aces = 0;
5140                SAFE_FREE(old->dacl->aces);
5141                SAFE_FREE(old->dacl);
5142                old->dacl = NULL;
5143                dacl = old->dacl;
5144                break;
5145
5146        case SMBC_XATTR_MODE_REMOVE:
5147		for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5148			BOOL found = False;
5149
5150			for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5151                                if (sec_ace_equal(&sd->dacl->aces[i],
5152                                                  &old->dacl->aces[j])) {
5153					uint32 k;
5154					for (k=j; k<old->dacl->num_aces-1;k++) {
5155						old->dacl->aces[k] =
5156                                                        old->dacl->aces[k+1];
5157					}
5158					old->dacl->num_aces--;
5159					if (old->dacl->num_aces == 0) {
5160						SAFE_FREE(old->dacl->aces);
5161						SAFE_FREE(old->dacl);
5162						old->dacl = NULL;
5163					}
5164					found = True;
5165                                        dacl = old->dacl;
5166					break;
5167				}
5168			}
5169
5170			if (!found) {
5171                                err = ENOATTR;
5172                                ret = -1;
5173                                goto failed;
5174			}
5175		}
5176		break;
5177
5178	case SMBC_XATTR_MODE_ADD:
5179		for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5180			BOOL found = False;
5181
5182			for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
5183				if (sid_equal(&sd->dacl->aces[i].trustee,
5184					      &old->dacl->aces[j].trustee)) {
5185                                        if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
5186                                                err = EEXIST;
5187                                                ret = -1;
5188                                                goto failed;
5189                                        }
5190                                        old->dacl->aces[j] = sd->dacl->aces[i];
5191                                        ret = -1;
5192					found = True;
5193				}
5194			}
5195
5196			if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
5197                                err = ENOATTR;
5198                                ret = -1;
5199                                goto failed;
5200			}
5201
5202                        for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
5203                                add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
5204                        }
5205		}
5206                dacl = old->dacl;
5207		break;
5208
5209	case SMBC_XATTR_MODE_SET:
5210 		old = sd;
5211                owner_sid = old->owner_sid;
5212                group_sid = old->group_sid;
5213                dacl = old->dacl;
5214		break;
5215
5216        case SMBC_XATTR_MODE_CHOWN:
5217                owner_sid = sd->owner_sid;
5218                break;
5219
5220        case SMBC_XATTR_MODE_CHGRP:
5221                group_sid = sd->group_sid;
5222                break;
5223	}
5224
5225	/* Denied ACE entries must come before allowed ones */
5226	sort_acl(old->dacl);
5227
5228	/* Create new security descriptor and set it */
5229	sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
5230			   owner_sid, group_sid, NULL, dacl, &sd_size);
5231
5232	fnum = cli_nt_create(cli, filename,
5233                             WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
5234
5235	if (fnum == -1) {
5236		DEBUG(5, ("cacl_set failed to open %s: %s\n",
5237                          filename, cli_errstr(cli)));
5238                errno = 0;
5239		return -1;
5240	}
5241
5242	if (!cli_set_secdesc(cli, fnum, sd)) {
5243		DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli)));
5244		ret = -1;
5245	}
5246
5247	/* Clean up */
5248
5249 failed:
5250	cli_close(cli, fnum);
5251
5252        if (err != 0) {
5253                errno = err;
5254        }
5255
5256	return ret;
5257}
5258
5259
5260static int
5261smbc_setxattr_ctx(SMBCCTX *context,
5262                  const char *fname,
5263                  const char *name,
5264                  const void *value,
5265                  size_t size,
5266                  int flags)
5267{
5268        int ret;
5269        int ret2;
5270        SMBCSRV *srv;
5271        SMBCSRV *ipc_srv;
5272	fstring server;
5273        fstring share;
5274        fstring user;
5275        fstring password;
5276        fstring workgroup;
5277	pstring path;
5278        TALLOC_CTX *ctx;
5279        POLICY_HND pol;
5280        DOS_ATTR_DESC *dad;
5281        struct {
5282                const char * create_time_attr;
5283                const char * access_time_attr;
5284                const char * write_time_attr;
5285                const char * change_time_attr;
5286        } attr_strings;
5287
5288	if (!context || !context->internal ||
5289	    !context->internal->_initialized) {
5290
5291		errno = EINVAL;  /* Best I can think of ... */
5292		return -1;
5293
5294	}
5295
5296	if (!fname) {
5297
5298		errno = EINVAL;
5299		return -1;
5300
5301	}
5302
5303	DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
5304                  fname, name, (int) size, (const char*)value));
5305
5306	if (smbc_parse_path(context, fname,
5307                            workgroup, sizeof(workgroup),
5308                            server, sizeof(server),
5309                            share, sizeof(share),
5310                            path, sizeof(path),
5311                            user, sizeof(user),
5312                            password, sizeof(password),
5313                            NULL, 0)) {
5314                errno = EINVAL;
5315                return -1;
5316        }
5317
5318	if (user[0] == (char)0) fstrcpy(user, context->user);
5319
5320	srv = smbc_server(context, True,
5321                          server, share, workgroup, user, password);
5322	if (!srv) {
5323		return -1;  /* errno set by smbc_server */
5324	}
5325
5326        if (! srv->no_nt_session) {
5327                ipc_srv = smbc_attr_server(context, server, share,
5328                                           workgroup, user, password,
5329                                           &pol);
5330                if (! ipc_srv) {
5331                        srv->no_nt_session = True;
5332                }
5333        } else {
5334                ipc_srv = NULL;
5335        }
5336
5337        ctx = talloc_init("smbc_setxattr");
5338        if (!ctx) {
5339                errno = ENOMEM;
5340                return -1;
5341        }
5342
5343        /*
5344         * Are they asking to set the entire set of known attributes?
5345         */
5346        if (StrCaseCmp(name, "system.*") == 0 ||
5347            StrCaseCmp(name, "system.*+") == 0) {
5348                /* Yup. */
5349                char *namevalue =
5350                        talloc_asprintf(ctx, "%s:%s",
5351                                        name+7, (const char *) value);
5352                if (! namevalue) {
5353                        errno = ENOMEM;
5354                        ret = -1;
5355                        return -1;
5356                }
5357
5358                if (ipc_srv) {
5359                        ret = cacl_set(ctx, srv->cli,
5360                                       ipc_srv->cli, &pol, path,
5361                                       namevalue,
5362                                       (*namevalue == '*'
5363                                        ? SMBC_XATTR_MODE_SET
5364                                        : SMBC_XATTR_MODE_ADD),
5365                                       flags);
5366                } else {
5367                        ret = 0;
5368                }
5369
5370                /* get a DOS Attribute Descriptor with current attributes */
5371                dad = dos_attr_query(context, ctx, path, srv);
5372                if (dad) {
5373                        /* Overwrite old with new, using what was provided */
5374                        dos_attr_parse(context, dad, srv, namevalue);
5375
5376                        /* Set the new DOS attributes */
5377                        if (! smbc_setatr(context, srv, path,
5378                                          dad->create_time,
5379                                          dad->access_time,
5380                                          dad->write_time,
5381                                          dad->change_time,
5382                                          dad->mode)) {
5383
5384                                /* cause failure if NT failed too */
5385                                dad = NULL;
5386                        }
5387                }
5388
5389                /* we only fail if both NT and DOS sets failed */
5390                if (ret < 0 && ! dad) {
5391                        ret = -1; /* in case dad was null */
5392                }
5393                else {
5394                        ret = 0;
5395                }
5396
5397                talloc_destroy(ctx);
5398                return ret;
5399        }
5400
5401        /*
5402         * Are they asking to set an access control element or to set
5403         * the entire access control list?
5404         */
5405        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5406            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5407            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5408            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5409            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5410
5411                /* Yup. */
5412                char *namevalue =
5413                        talloc_asprintf(ctx, "%s:%s",
5414                                        name+19, (const char *) value);
5415
5416                if (! ipc_srv) {
5417                        ret = -1; /* errno set by smbc_server() */
5418                }
5419                else if (! namevalue) {
5420                        errno = ENOMEM;
5421                        ret = -1;
5422                } else {
5423                        ret = cacl_set(ctx, srv->cli,
5424                                       ipc_srv->cli, &pol, path,
5425                                       namevalue,
5426                                       (*namevalue == '*'
5427                                        ? SMBC_XATTR_MODE_SET
5428                                        : SMBC_XATTR_MODE_ADD),
5429                                       flags);
5430                }
5431                talloc_destroy(ctx);
5432                return ret;
5433        }
5434
5435        /*
5436         * Are they asking to set the owner?
5437         */
5438        if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5439            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
5440
5441                /* Yup. */
5442                char *namevalue =
5443                        talloc_asprintf(ctx, "%s:%s",
5444                                        name+19, (const char *) value);
5445
5446                if (! ipc_srv) {
5447
5448                        ret = -1; /* errno set by smbc_server() */
5449                }
5450                else if (! namevalue) {
5451                        errno = ENOMEM;
5452                        ret = -1;
5453                } else {
5454                        ret = cacl_set(ctx, srv->cli,
5455                                       ipc_srv->cli, &pol, path,
5456                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5457                }
5458                talloc_destroy(ctx);
5459                return ret;
5460        }
5461
5462        /*
5463         * Are they asking to set the group?
5464         */
5465        if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5466            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
5467
5468                /* Yup. */
5469                char *namevalue =
5470                        talloc_asprintf(ctx, "%s:%s",
5471                                        name+19, (const char *) value);
5472
5473                if (! ipc_srv) {
5474                        /* errno set by smbc_server() */
5475                        ret = -1;
5476                }
5477                else if (! namevalue) {
5478                        errno = ENOMEM;
5479                        ret = -1;
5480                } else {
5481                        ret = cacl_set(ctx, srv->cli,
5482                                       ipc_srv->cli, &pol, path,
5483                                       namevalue, SMBC_XATTR_MODE_CHOWN, 0);
5484                }
5485                talloc_destroy(ctx);
5486                return ret;
5487        }
5488
5489        /* Determine whether to use old-style or new-style attribute names */
5490        if (context->internal->_full_time_names) {
5491                /* new-style names */
5492                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5493                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5494                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5495                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5496        } else {
5497                /* old-style names */
5498                attr_strings.create_time_attr = NULL;
5499                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5500                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5501                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5502        }
5503
5504        /*
5505         * Are they asking to set a DOS attribute?
5506         */
5507        if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5508            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5509            (attr_strings.create_time_attr != NULL &&
5510             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5511            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5512            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5513            StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
5514
5515                /* get a DOS Attribute Descriptor with current attributes */
5516                dad = dos_attr_query(context, ctx, path, srv);
5517                if (dad) {
5518                        char *namevalue =
5519                                talloc_asprintf(ctx, "%s:%s",
5520                                                name+16, (const char *) value);
5521                        if (! namevalue) {
5522                                errno = ENOMEM;
5523                                ret = -1;
5524                        } else {
5525                                /* Overwrite old with provided new params */
5526                                dos_attr_parse(context, dad, srv, namevalue);
5527
5528                                /* Set the new DOS attributes */
5529                                ret2 = smbc_setatr(context, srv, path,
5530                                                   dad->create_time,
5531                                                   dad->access_time,
5532                                                   dad->write_time,
5533                                                   dad->change_time,
5534                                                   dad->mode);
5535
5536                                /* ret2 has True (success) / False (failure) */
5537                                if (ret2) {
5538                                        ret = 0;
5539                                } else {
5540                                        ret = -1;
5541                                }
5542                        }
5543                } else {
5544                        ret = -1;
5545                }
5546
5547                talloc_destroy(ctx);
5548                return ret;
5549        }
5550
5551        /* Unsupported attribute name */
5552        talloc_destroy(ctx);
5553        errno = EINVAL;
5554        return -1;
5555}
5556
5557static int
5558smbc_getxattr_ctx(SMBCCTX *context,
5559                  const char *fname,
5560                  const char *name,
5561                  const void *value,
5562                  size_t size)
5563{
5564        int ret;
5565        SMBCSRV *srv;
5566        SMBCSRV *ipc_srv;
5567        fstring server;
5568        fstring share;
5569        fstring user;
5570        fstring password;
5571        fstring workgroup;
5572        pstring path;
5573        TALLOC_CTX *ctx;
5574        POLICY_HND pol;
5575        struct {
5576                const char * create_time_attr;
5577                const char * access_time_attr;
5578                const char * write_time_attr;
5579                const char * change_time_attr;
5580        } attr_strings;
5581
5582
5583        if (!context || !context->internal ||
5584            !context->internal->_initialized) {
5585
5586                errno = EINVAL;  /* Best I can think of ... */
5587                return -1;
5588
5589        }
5590
5591        if (!fname) {
5592
5593                errno = EINVAL;
5594                return -1;
5595
5596        }
5597
5598        DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
5599
5600        if (smbc_parse_path(context, fname,
5601                            workgroup, sizeof(workgroup),
5602                            server, sizeof(server),
5603                            share, sizeof(share),
5604                            path, sizeof(path),
5605                            user, sizeof(user),
5606                            password, sizeof(password),
5607                            NULL, 0)) {
5608                errno = EINVAL;
5609                return -1;
5610        }
5611
5612        if (user[0] == (char)0) fstrcpy(user, context->user);
5613
5614        srv = smbc_server(context, True,
5615                          server, share, workgroup, user, password);
5616        if (!srv) {
5617                return -1;  /* errno set by smbc_server */
5618        }
5619
5620        if (! srv->no_nt_session) {
5621                ipc_srv = smbc_attr_server(context, server, share,
5622                                           workgroup, user, password,
5623                                           &pol);
5624                if (! ipc_srv) {
5625                        srv->no_nt_session = True;
5626                }
5627        } else {
5628                ipc_srv = NULL;
5629        }
5630
5631        ctx = talloc_init("smbc:getxattr");
5632        if (!ctx) {
5633                errno = ENOMEM;
5634                return -1;
5635        }
5636
5637        /* Determine whether to use old-style or new-style attribute names */
5638        if (context->internal->_full_time_names) {
5639                /* new-style names */
5640                attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
5641                attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
5642                attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
5643                attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
5644        } else {
5645                /* old-style names */
5646                attr_strings.create_time_attr = NULL;
5647                attr_strings.access_time_attr = "system.dos_attr.A_TIME";
5648                attr_strings.write_time_attr = "system.dos_attr.M_TIME";
5649                attr_strings.change_time_attr = "system.dos_attr.C_TIME";
5650        }
5651
5652        /* Are they requesting a supported attribute? */
5653        if (StrCaseCmp(name, "system.*") == 0 ||
5654            StrnCaseCmp(name, "system.*!", 9) == 0 ||
5655            StrCaseCmp(name, "system.*+") == 0 ||
5656            StrnCaseCmp(name, "system.*+!", 10) == 0 ||
5657            StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5658            StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
5659            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
5660            StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
5661            StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5662            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5663            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5664            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5665            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5666            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5667            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
5668            StrCaseCmp(name, "system.dos_attr.*") == 0 ||
5669            StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
5670            StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
5671            StrCaseCmp(name, "system.dos_attr.size") == 0 ||
5672            (attr_strings.create_time_attr != NULL &&
5673             StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
5674            StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
5675            StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
5676            StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
5677            StrCaseCmp(name, "system.dos_attr.inode") == 0) {
5678
5679                /* Yup. */
5680                ret = cacl_get(context, ctx, srv,
5681                               ipc_srv == NULL ? NULL : ipc_srv->cli,
5682                               &pol, path,
5683                               CONST_DISCARD(char *, name),
5684                               CONST_DISCARD(char *, value), size);
5685                if (ret < 0 && errno == 0) {
5686                        errno = smbc_errno(context, srv->cli);
5687                }
5688                talloc_destroy(ctx);
5689                return ret;
5690        }
5691
5692        /* Unsupported attribute name */
5693        talloc_destroy(ctx);
5694        errno = EINVAL;
5695        return -1;
5696}
5697
5698
5699static int
5700smbc_removexattr_ctx(SMBCCTX *context,
5701                     const char *fname,
5702                     const char *name)
5703{
5704        int ret;
5705        SMBCSRV *srv;
5706        SMBCSRV *ipc_srv;
5707        fstring server;
5708        fstring share;
5709        fstring user;
5710        fstring password;
5711        fstring workgroup;
5712        pstring path;
5713        TALLOC_CTX *ctx;
5714        POLICY_HND pol;
5715
5716        if (!context || !context->internal ||
5717            !context->internal->_initialized) {
5718
5719                errno = EINVAL;  /* Best I can think of ... */
5720                return -1;
5721
5722        }
5723
5724        if (!fname) {
5725
5726                errno = EINVAL;
5727                return -1;
5728
5729        }
5730
5731        DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
5732
5733        if (smbc_parse_path(context, fname,
5734                            workgroup, sizeof(workgroup),
5735                            server, sizeof(server),
5736                            share, sizeof(share),
5737                            path, sizeof(path),
5738                            user, sizeof(user),
5739                            password, sizeof(password),
5740                            NULL, 0)) {
5741                errno = EINVAL;
5742                return -1;
5743        }
5744
5745        if (user[0] == (char)0) fstrcpy(user, context->user);
5746
5747        srv = smbc_server(context, True,
5748                          server, share, workgroup, user, password);
5749        if (!srv) {
5750                return -1;  /* errno set by smbc_server */
5751        }
5752
5753        if (! srv->no_nt_session) {
5754                ipc_srv = smbc_attr_server(context, server, share,
5755                                           workgroup, user, password,
5756                                           &pol);
5757                if (! ipc_srv) {
5758                        srv->no_nt_session = True;
5759                }
5760        } else {
5761                ipc_srv = NULL;
5762        }
5763
5764        if (! ipc_srv) {
5765                return -1; /* errno set by smbc_attr_server */
5766        }
5767
5768        ctx = talloc_init("smbc_removexattr");
5769        if (!ctx) {
5770                errno = ENOMEM;
5771                return -1;
5772        }
5773
5774        /* Are they asking to set the entire ACL? */
5775        if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
5776            StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
5777
5778                /* Yup. */
5779                ret = cacl_set(ctx, srv->cli,
5780                               ipc_srv->cli, &pol, path,
5781                               NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
5782                talloc_destroy(ctx);
5783                return ret;
5784        }
5785
5786        /*
5787         * Are they asking to remove one or more spceific security descriptor
5788         * attributes?
5789         */
5790        if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
5791            StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
5792            StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
5793            StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
5794            StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
5795            StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
5796            StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
5797
5798                /* Yup. */
5799                ret = cacl_set(ctx, srv->cli,
5800                               ipc_srv->cli, &pol, path,
5801                               name + 19, SMBC_XATTR_MODE_REMOVE, 0);
5802                talloc_destroy(ctx);
5803                return ret;
5804        }
5805
5806        /* Unsupported attribute name */
5807        talloc_destroy(ctx);
5808        errno = EINVAL;
5809        return -1;
5810}
5811
5812static int
5813smbc_listxattr_ctx(SMBCCTX *context,
5814                   const char *fname,
5815                   char *list,
5816                   size_t size)
5817{
5818        /*
5819         * This isn't quite what listxattr() is supposed to do.  This returns
5820         * the complete set of attribute names, always, rather than only those
5821         * attribute names which actually exist for a file.  Hmmm...
5822         */
5823        const char supported_old[] =
5824                "system.*\0"
5825                "system.*+\0"
5826                "system.nt_sec_desc.revision\0"
5827                "system.nt_sec_desc.owner\0"
5828                "system.nt_sec_desc.owner+\0"
5829                "system.nt_sec_desc.group\0"
5830                "system.nt_sec_desc.group+\0"
5831                "system.nt_sec_desc.acl.*\0"
5832                "system.nt_sec_desc.acl\0"
5833                "system.nt_sec_desc.acl+\0"
5834                "system.nt_sec_desc.*\0"
5835                "system.nt_sec_desc.*+\0"
5836                "system.dos_attr.*\0"
5837                "system.dos_attr.mode\0"
5838                "system.dos_attr.c_time\0"
5839                "system.dos_attr.a_time\0"
5840                "system.dos_attr.m_time\0"
5841                ;
5842        const char supported_new[] =
5843                "system.*\0"
5844                "system.*+\0"
5845                "system.nt_sec_desc.revision\0"
5846                "system.nt_sec_desc.owner\0"
5847                "system.nt_sec_desc.owner+\0"
5848                "system.nt_sec_desc.group\0"
5849                "system.nt_sec_desc.group+\0"
5850                "system.nt_sec_desc.acl.*\0"
5851                "system.nt_sec_desc.acl\0"
5852                "system.nt_sec_desc.acl+\0"
5853                "system.nt_sec_desc.*\0"
5854                "system.nt_sec_desc.*+\0"
5855                "system.dos_attr.*\0"
5856                "system.dos_attr.mode\0"
5857                "system.dos_attr.create_time\0"
5858                "system.dos_attr.access_time\0"
5859                "system.dos_attr.write_time\0"
5860                "system.dos_attr.change_time\0"
5861                ;
5862        const char * supported;
5863
5864        if (context->internal->_full_time_names) {
5865                supported = supported_new;
5866        } else {
5867                supported = supported_old;
5868        }
5869
5870        if (size == 0) {
5871                return sizeof(supported);
5872        }
5873
5874        if (sizeof(supported) > size) {
5875                errno = ERANGE;
5876                return -1;
5877        }
5878
5879        /* this can't be strcpy() because there are embedded null characters */
5880        memcpy(list, supported, sizeof(supported));
5881        return sizeof(supported);
5882}
5883
5884
5885/*
5886 * Open a print file to be written to by other calls
5887 */
5888
5889static SMBCFILE *
5890smbc_open_print_job_ctx(SMBCCTX *context,
5891                        const char *fname)
5892{
5893        fstring server;
5894        fstring share;
5895        fstring user;
5896        fstring password;
5897        pstring path;
5898
5899        if (!context || !context->internal ||
5900            !context->internal->_initialized) {
5901
5902                errno = EINVAL;
5903                return NULL;
5904
5905        }
5906
5907        if (!fname) {
5908
5909                errno = EINVAL;
5910                return NULL;
5911
5912        }
5913
5914        DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
5915
5916        if (smbc_parse_path(context, fname,
5917                            NULL, 0,
5918                            server, sizeof(server),
5919                            share, sizeof(share),
5920                            path, sizeof(path),
5921                            user, sizeof(user),
5922                            password, sizeof(password),
5923                            NULL, 0)) {
5924                errno = EINVAL;
5925                return NULL;
5926        }
5927
5928        /* What if the path is empty, or the file exists? */
5929
5930        return context->open(context, fname, O_WRONLY, 666);
5931
5932}
5933
5934/*
5935 * Routine to print a file on a remote server ...
5936 *
5937 * We open the file, which we assume to be on a remote server, and then
5938 * copy it to a print file on the share specified by printq.
5939 */
5940
5941static int
5942smbc_print_file_ctx(SMBCCTX *c_file,
5943                    const char *fname,
5944                    SMBCCTX *c_print,
5945                    const char *printq)
5946{
5947        SMBCFILE *fid1;
5948        SMBCFILE *fid2;
5949        int bytes;
5950        int saverr;
5951        int tot_bytes = 0;
5952        char buf[4096];
5953
5954        if (!c_file || !c_file->internal->_initialized || !c_print ||
5955            !c_print->internal->_initialized) {
5956
5957                errno = EINVAL;
5958                return -1;
5959
5960        }
5961
5962        if (!fname && !printq) {
5963
5964                errno = EINVAL;
5965                return -1;
5966
5967        }
5968
5969        /* Try to open the file for reading ... */
5970
5971        if ((long)(fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
5972
5973                DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
5974                return -1;  /* smbc_open sets errno */
5975
5976        }
5977
5978        /* Now, try to open the printer file for writing */
5979
5980        if ((long)(fid2 = c_print->open_print_job(c_print, printq)) < 0) {
5981
5982                saverr = errno;  /* Save errno */
5983                c_file->close_fn(c_file, fid1);
5984                errno = saverr;
5985                return -1;
5986
5987        }
5988
5989        while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
5990
5991                tot_bytes += bytes;
5992
5993                if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
5994
5995                        saverr = errno;
5996                        c_file->close_fn(c_file, fid1);
5997                        c_print->close_fn(c_print, fid2);
5998                        errno = saverr;
5999
6000                }
6001
6002        }
6003
6004        saverr = errno;
6005
6006        c_file->close_fn(c_file, fid1);  /* We have to close these anyway */
6007        c_print->close_fn(c_print, fid2);
6008
6009        if (bytes < 0) {
6010
6011                errno = saverr;
6012                return -1;
6013
6014        }
6015
6016        return tot_bytes;
6017
6018}
6019
6020/*
6021 * Routine to list print jobs on a printer share ...
6022 */
6023
6024static int
6025smbc_list_print_jobs_ctx(SMBCCTX *context,
6026                         const char *fname,
6027                         smbc_list_print_job_fn fn)
6028{
6029        SMBCSRV *srv;
6030        fstring server;
6031        fstring share;
6032        fstring user;
6033        fstring password;
6034        fstring workgroup;
6035        pstring path;
6036
6037        if (!context || !context->internal ||
6038            !context->internal->_initialized) {
6039
6040                errno = EINVAL;
6041                return -1;
6042
6043        }
6044
6045        if (!fname) {
6046
6047                errno = EINVAL;
6048                return -1;
6049
6050        }
6051
6052        DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
6053
6054        if (smbc_parse_path(context, fname,
6055                            workgroup, sizeof(workgroup),
6056                            server, sizeof(server),
6057                            share, sizeof(share),
6058                            path, sizeof(path),
6059                            user, sizeof(user),
6060                            password, sizeof(password),
6061                            NULL, 0)) {
6062                errno = EINVAL;
6063                return -1;
6064        }
6065
6066        if (user[0] == (char)0) fstrcpy(user, context->user);
6067
6068        srv = smbc_server(context, True,
6069                          server, share, workgroup, user, password);
6070
6071        if (!srv) {
6072
6073                return -1;  /* errno set by smbc_server */
6074
6075        }
6076
6077        if (cli_print_queue(srv->cli,
6078                            (void (*)(struct print_job_info *))fn) < 0) {
6079
6080                errno = smbc_errno(context, srv->cli);
6081                return -1;
6082
6083        }
6084
6085        return 0;
6086
6087}
6088
6089/*
6090 * Delete a print job from a remote printer share
6091 */
6092
6093static int
6094smbc_unlink_print_job_ctx(SMBCCTX *context,
6095                          const char *fname,
6096                          int id)
6097{
6098        SMBCSRV *srv;
6099        fstring server;
6100        fstring share;
6101        fstring user;
6102        fstring password;
6103        fstring workgroup;
6104        pstring path;
6105        int err;
6106
6107        if (!context || !context->internal ||
6108            !context->internal->_initialized) {
6109
6110                errno = EINVAL;
6111                return -1;
6112
6113        }
6114
6115        if (!fname) {
6116
6117                errno = EINVAL;
6118                return -1;
6119
6120        }
6121
6122        DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
6123
6124        if (smbc_parse_path(context, fname,
6125                            workgroup, sizeof(workgroup),
6126                            server, sizeof(server),
6127                            share, sizeof(share),
6128                            path, sizeof(path),
6129                            user, sizeof(user),
6130                            password, sizeof(password),
6131                            NULL, 0)) {
6132                errno = EINVAL;
6133                return -1;
6134        }
6135
6136        if (user[0] == (char)0) fstrcpy(user, context->user);
6137
6138        srv = smbc_server(context, True,
6139                          server, share, workgroup, user, password);
6140
6141        if (!srv) {
6142
6143                return -1;  /* errno set by smbc_server */
6144
6145        }
6146
6147        if ((err = cli_printjob_del(srv->cli, id)) != 0) {
6148
6149                if (err < 0)
6150                        errno = smbc_errno(context, srv->cli);
6151                else if (err == ERRnosuchprintjob)
6152                        errno = EINVAL;
6153                return -1;
6154
6155        }
6156
6157        return 0;
6158
6159}
6160
6161/*
6162 * Get a new empty handle to fill in with your own info
6163 */
6164SMBCCTX *
6165smbc_new_context(void)
6166{
6167        SMBCCTX *context;
6168
6169        context = SMB_MALLOC_P(SMBCCTX);
6170        if (!context) {
6171                errno = ENOMEM;
6172                return NULL;
6173        }
6174
6175        ZERO_STRUCTP(context);
6176
6177        context->internal = SMB_MALLOC_P(struct smbc_internal_data);
6178        if (!context->internal) {
6179		SAFE_FREE(context);
6180                errno = ENOMEM;
6181                return NULL;
6182        }
6183
6184        ZERO_STRUCTP(context->internal);
6185
6186
6187        /* ADD REASONABLE DEFAULTS */
6188        context->debug            = 0;
6189        context->timeout          = 20000; /* 20 seconds */
6190
6191	context->options.browse_max_lmb_count      = 3;    /* # LMBs to query */
6192	context->options.urlencode_readdir_entries = False;/* backward compat */
6193	context->options.one_share_per_server      = False;/* backward compat */
6194        context->internal->_share_mode             = SMBC_SHAREMODE_DENY_NONE;
6195                                /* backward compat */
6196
6197        context->open                              = smbc_open_ctx;
6198        context->creat                             = smbc_creat_ctx;
6199        context->read                              = smbc_read_ctx;
6200        context->write                             = smbc_write_ctx;
6201        context->close_fn                          = smbc_close_ctx;
6202        context->unlink                            = smbc_unlink_ctx;
6203        context->rename                            = smbc_rename_ctx;
6204        context->lseek                             = smbc_lseek_ctx;
6205        context->stat                              = smbc_stat_ctx;
6206        context->fstat                             = smbc_fstat_ctx;
6207        context->opendir                           = smbc_opendir_ctx;
6208        context->closedir                          = smbc_closedir_ctx;
6209        context->readdir                           = smbc_readdir_ctx;
6210        context->getdents                          = smbc_getdents_ctx;
6211        context->mkdir                             = smbc_mkdir_ctx;
6212        context->rmdir                             = smbc_rmdir_ctx;
6213        context->telldir                           = smbc_telldir_ctx;
6214        context->lseekdir                          = smbc_lseekdir_ctx;
6215        context->fstatdir                          = smbc_fstatdir_ctx;
6216        context->chmod                             = smbc_chmod_ctx;
6217        context->utimes                            = smbc_utimes_ctx;
6218        context->setxattr                          = smbc_setxattr_ctx;
6219        context->getxattr                          = smbc_getxattr_ctx;
6220        context->removexattr                       = smbc_removexattr_ctx;
6221        context->listxattr                         = smbc_listxattr_ctx;
6222        context->open_print_job                    = smbc_open_print_job_ctx;
6223        context->print_file                        = smbc_print_file_ctx;
6224        context->list_print_jobs                   = smbc_list_print_jobs_ctx;
6225        context->unlink_print_job                  = smbc_unlink_print_job_ctx;
6226
6227        context->callbacks.check_server_fn         = smbc_check_server;
6228        context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
6229
6230        smbc_default_cache_functions(context);
6231
6232        return context;
6233}
6234
6235/*
6236 * Free a context
6237 *
6238 * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
6239 * and thus you'll be leaking memory if not handled properly.
6240 *
6241 */
6242int
6243smbc_free_context(SMBCCTX *context,
6244                  int shutdown_ctx)
6245{
6246        if (!context) {
6247                errno = EBADF;
6248                return 1;
6249        }
6250
6251        if (shutdown_ctx) {
6252                SMBCFILE * f;
6253                DEBUG(1,("Performing aggressive shutdown.\n"));
6254
6255                f = context->internal->_files;
6256                while (f) {
6257                        context->close_fn(context, f);
6258                        f = f->next;
6259                }
6260                context->internal->_files = NULL;
6261
6262                /* First try to remove the servers the nice way. */
6263                if (context->callbacks.purge_cached_fn(context)) {
6264                        SMBCSRV * s;
6265                        SMBCSRV * next;
6266                        DEBUG(1, ("Could not purge all servers, "
6267                                  "Nice way shutdown failed.\n"));
6268                        s = context->internal->_servers;
6269                        while (s) {
6270                                DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
6271                                          s, s->cli->fd));
6272                                cli_shutdown(s->cli);
6273                                context->callbacks.remove_cached_srv_fn(context,
6274                                                                        s);
6275                                next = s->next;
6276                                DLIST_REMOVE(context->internal->_servers, s);
6277                                SAFE_FREE(s);
6278                                s = next;
6279                        }
6280                        context->internal->_servers = NULL;
6281                }
6282        }
6283        else {
6284                /* This is the polite way */
6285                if (context->callbacks.purge_cached_fn(context)) {
6286                        DEBUG(1, ("Could not purge all servers, "
6287                                  "free_context failed.\n"));
6288                        errno = EBUSY;
6289                        return 1;
6290                }
6291                if (context->internal->_servers) {
6292                        DEBUG(1, ("Active servers in context, "
6293                                  "free_context failed.\n"));
6294                        errno = EBUSY;
6295                        return 1;
6296                }
6297                if (context->internal->_files) {
6298                        DEBUG(1, ("Active files in context, "
6299                                  "free_context failed.\n"));
6300                        errno = EBUSY;
6301                        return 1;
6302                }
6303        }
6304
6305        /* Things we have to clean up */
6306        SAFE_FREE(context->workgroup);
6307        SAFE_FREE(context->netbios_name);
6308        SAFE_FREE(context->user);
6309
6310        DEBUG(3, ("Context %p succesfully freed\n", context));
6311        SAFE_FREE(context->internal);
6312        SAFE_FREE(context);
6313        return 0;
6314}
6315
6316
6317/*
6318 * Each time the context structure is changed, we have binary backward
6319 * compatibility issues.  Instead of modifying the public portions of the
6320 * context structure to add new options, instead, we put them in the internal
6321 * portion of the context structure and provide a set function for these new
6322 * options.
6323 */
6324void
6325smbc_option_set(SMBCCTX *context,
6326                char *option_name,
6327                ... /* option_value */)
6328{
6329        va_list ap;
6330        union {
6331                int i;
6332                BOOL b;
6333                smbc_get_auth_data_with_context_fn auth_fn;
6334                void *v;
6335        } option_value;
6336
6337        va_start(ap, option_name);
6338
6339        if (strcmp(option_name, "debug_to_stderr") == 0) {
6340                /*
6341                 * Log to standard error instead of standard output.
6342                 */
6343                option_value.b = (BOOL) va_arg(ap, int);
6344                context->internal->_debug_stderr = option_value.b;
6345
6346        } else if (strcmp(option_name, "full_time_names") == 0) {
6347                /*
6348                 * Use new-style time attribute names, e.g. WRITE_TIME rather
6349                 * than the old-style names such as M_TIME.  This allows also
6350                 * setting/getting CREATE_TIME which was previously
6351                 * unimplemented.  (Note that the old C_TIME was supposed to
6352                 * be CHANGE_TIME but was confused and sometimes referred to
6353                 * CREATE_TIME.)
6354                 */
6355                option_value.b = (BOOL) va_arg(ap, int);
6356                context->internal->_full_time_names = option_value.b;
6357
6358        } else if (strcmp(option_name, "open_share_mode") == 0) {
6359                /*
6360                 * The share mode to use for files opened with
6361                 * smbc_open_ctx().  The default is SMBC_SHAREMODE_DENY_NONE.
6362                 */
6363                option_value.i = va_arg(ap, int);
6364                context->internal->_share_mode =
6365                        (smbc_share_mode) option_value.i;
6366
6367        } else if (strcmp(option_name, "auth_function") == 0) {
6368                /*
6369                 * Use the new-style authentication function which includes
6370                 * the context.
6371                 */
6372                option_value.auth_fn =
6373                        va_arg(ap, smbc_get_auth_data_with_context_fn);
6374                context->internal->_auth_fn_with_context =
6375                        option_value.auth_fn;
6376        } else if (strcmp(option_name, "user_data") == 0) {
6377                /*
6378                 * Save a user data handle which may be retrieved by the user
6379                 * with smbc_option_get()
6380                 */
6381                option_value.v = va_arg(ap, void *);
6382                context->internal->_user_data = option_value.v;
6383        }
6384
6385        va_end(ap);
6386}
6387
6388
6389/*
6390 * Retrieve the current value of an option
6391 */
6392void *
6393smbc_option_get(SMBCCTX *context,
6394                char *option_name)
6395{
6396        if (strcmp(option_name, "debug_stderr") == 0) {
6397                /*
6398                 * Log to standard error instead of standard output.
6399                 */
6400#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6401		return (void *) (intptr_t) context->internal->_debug_stderr;
6402#else
6403		return (void *) context->internal->_debug_stderr;
6404#endif
6405        } else if (strcmp(option_name, "full_time_names") == 0) {
6406                /*
6407                 * Use new-style time attribute names, e.g. WRITE_TIME rather
6408                 * than the old-style names such as M_TIME.  This allows also
6409                 * setting/getting CREATE_TIME which was previously
6410                 * unimplemented.  (Note that the old C_TIME was supposed to
6411                 * be CHANGE_TIME but was confused and sometimes referred to
6412                 * CREATE_TIME.)
6413                 */
6414#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
6415		return (void *) (intptr_t) context->internal->_full_time_names;
6416#else
6417		return (void *) context->internal->_full_time_names;
6418#endif
6419
6420        } else if (strcmp(option_name, "auth_function") == 0) {
6421                /*
6422                 * Use the new-style authentication function which includes
6423                 * the context.
6424                 */
6425                return (void *) context->internal->_auth_fn_with_context;
6426        } else if (strcmp(option_name, "user_data") == 0) {
6427                /*
6428                 * Save a user data handle which may be retrieved by the user
6429                 * with smbc_option_get()
6430                 */
6431                return context->internal->_user_data;
6432        }
6433
6434        return NULL;
6435}
6436
6437
6438/*
6439 * Initialise the library etc
6440 *
6441 * We accept a struct containing handle information.
6442 * valid values for info->debug from 0 to 100,
6443 * and insist that info->fn must be non-null.
6444 */
6445SMBCCTX *
6446smbc_init_context(SMBCCTX *context)
6447{
6448        pstring conf;
6449        int pid;
6450        char *user = NULL;
6451        char *home = NULL;
6452
6453        if (!context || !context->internal) {
6454                errno = EBADF;
6455                return NULL;
6456        }
6457
6458        /* Do not initialise the same client twice */
6459        if (context->internal->_initialized) {
6460                return 0;
6461        }
6462
6463        if ((!context->callbacks.auth_fn &&
6464             !context->internal->_auth_fn_with_context) ||
6465            context->debug < 0 ||
6466            context->debug > 100) {
6467
6468                errno = EINVAL;
6469                return NULL;
6470
6471        }
6472
6473        if (!smbc_initialized) {
6474                /*
6475                 * Do some library-wide intializations the first time we get
6476                 * called
6477                 */
6478		BOOL conf_loaded = False;
6479
6480                /* Set this to what the user wants */
6481                DEBUGLEVEL = context->debug;
6482
6483                load_case_tables();
6484
6485                setup_logging("libsmbclient", True);
6486                if (context->internal->_debug_stderr) {
6487                        dbf = x_stderr;
6488                        x_setbuf(x_stderr, NULL);
6489                }
6490
6491                /* Here we would open the smb.conf file if needed ... */
6492
6493                in_client = True; /* FIXME, make a param */
6494
6495                home = getenv("HOME");
6496		if (home) {
6497			slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
6498			if (lp_load(conf, True, False, False, True)) {
6499				conf_loaded = True;
6500			} else {
6501                                DEBUG(5, ("Could not load config file: %s\n",
6502                                          conf));
6503			}
6504               	}
6505
6506		if (!conf_loaded) {
6507                        /*
6508                         * Well, if that failed, try the dyn_CONFIGFILE
6509                         * Which points to the standard locn, and if that
6510                         * fails, silently ignore it and use the internal
6511                         * defaults ...
6512                         */
6513
6514                        if (!lp_load(dyn_CONFIGFILE, True, False, False, False)) {
6515                                DEBUG(5, ("Could not load config file: %s\n",
6516                                          dyn_CONFIGFILE));
6517                        } else if (home) {
6518                                /*
6519                                 * We loaded the global config file.  Now lets
6520                                 * load user-specific modifications to the
6521                                 * global config.
6522                                 */
6523                                slprintf(conf, sizeof(conf),
6524                                         "%s/.smb/smb.conf.append", home);
6525                                if (!lp_load(conf, True, False, False, False)) {
6526                                        DEBUG(10,
6527                                              ("Could not append config file: "
6528                                               "%s\n",
6529                                               conf));
6530                                }
6531                        }
6532                }
6533
6534                load_interfaces();  /* Load the list of interfaces ... */
6535
6536                reopen_logs();  /* Get logging working ... */
6537
6538                /*
6539                 * Block SIGPIPE (from lib/util_sock.c: write())
6540                 * It is not needed and should not stop execution
6541                 */
6542                BlockSignals(True, SIGPIPE);
6543
6544                /* Done with one-time initialisation */
6545                smbc_initialized = 1;
6546
6547        }
6548
6549        if (!context->user) {
6550                /*
6551                 * FIXME: Is this the best way to get the user info?
6552                 */
6553                user = getenv("USER");
6554                /* walk around as "guest" if no username can be found */
6555                if (!user) context->user = SMB_STRDUP("guest");
6556                else context->user = SMB_STRDUP(user);
6557        }
6558
6559        if (!context->netbios_name) {
6560                /*
6561                 * We try to get our netbios name from the config. If that
6562                 * fails we fall back on constructing our netbios name from
6563                 * our hostname etc
6564                 */
6565                if (global_myname()) {
6566                        context->netbios_name = SMB_STRDUP(global_myname());
6567                }
6568                else {
6569                        /*
6570                         * Hmmm, I want to get hostname as well, but I am too
6571                         * lazy for the moment
6572                         */
6573                        pid = sys_getpid();
6574                        context->netbios_name = (char *)SMB_MALLOC(17);
6575                        if (!context->netbios_name) {
6576                                errno = ENOMEM;
6577                                return NULL;
6578                        }
6579                        slprintf(context->netbios_name, 16,
6580                                 "smbc%s%d", context->user, pid);
6581                }
6582        }
6583
6584        DEBUG(1, ("Using netbios name %s.\n", context->netbios_name));
6585
6586        if (!context->workgroup) {
6587                if (lp_workgroup()) {
6588                        context->workgroup = SMB_STRDUP(lp_workgroup());
6589                }
6590                else {
6591                        /* TODO: Think about a decent default workgroup */
6592                        context->workgroup = SMB_STRDUP("samba");
6593                }
6594        }
6595
6596        DEBUG(1, ("Using workgroup %s.\n", context->workgroup));
6597
6598        /* shortest timeout is 1 second */
6599        if (context->timeout > 0 && context->timeout < 1000)
6600                context->timeout = 1000;
6601
6602        /*
6603         * FIXME: Should we check the function pointers here?
6604         */
6605
6606        context->internal->_initialized = True;
6607
6608        return context;
6609}
6610
6611
6612/* Return the verion of samba, and thus libsmbclient */
6613const char *
6614smbc_version(void)
6615{
6616        return samba_version_string();
6617}
6618