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