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