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