updated the 3.0 branch from the head branch - ready for alpha18
[kai/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    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 #include "libsmbclient.h"
27
28 /*
29  * Functions exported by libsmb_cache.c that we need here
30  */
31 int smbc_default_cache_functions(SMBCCTX *context);
32
33 /* 
34  * check if an element is part of the list. 
35  * FIXME: Does not belong here !  
36  * Can anyone put this in a macro in dlinklist.h ?
37  * -- Tom
38  */
39 static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) {
40         if (!p || !list) return False;
41         do {
42                 if (p == list) return True;
43                 list = list->next;
44         } while (list);
45         return False;
46 }
47
48 extern BOOL in_client;
49 extern pstring global_myname;
50
51 /*
52  * Is the logging working / configfile read ? 
53  */
54 static int smbc_initialized = 0;
55
56 /*
57  * Function to parse a path and turn it into components
58  *
59  * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]]
60  * 
61  * smb://       means show all the workgroups
62  * smb://name/  means, if name<1D> or name<1B> exists, list servers in workgroup,
63  *              else, if name<20> exists, list all shares for server ...
64  */
65
66 static const char *smbc_prefix = "smb:";
67
68 static int
69 smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share, char *path,
70                 char *user, char *password) /* FIXME, lengths of strings */
71 {
72         static pstring s;
73         pstring userinfo;
74         char *p;
75         char *q, *r;
76         int len;
77
78         server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
79         pstrcpy(s, fname);
80
81         /*  clean_fname(s);  causing problems ... */
82
83         /* see if it has the right prefix */
84         len = strlen(smbc_prefix);
85         if (strncmp(s,smbc_prefix,len) || 
86             (s[len] != '/' && s[len] != 0)) return -1; /* What about no smb: ? */
87
88         p = s + len;
89
90         /* Watch the test below, we are testing to see if we should exit */
91
92         if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
93
94                 return -1;
95
96         }
97
98         p += 2;  /* Skip the // or \\  */
99
100         if (*p == (char)0)
101                 return 0;
102
103         if (*p == '/') {
104
105                 strncpy(server, context->workgroup, 
106                         (strlen(context->workgroup) < 16)?strlen(context->workgroup):16);
107                 return 0;
108                 
109         }
110
111         /*
112          * ok, its for us. Now parse out the server, share etc. 
113          *
114          * However, we want to parse out [[domain;]user[:password]@] if it
115          * exists ...
116          */
117
118         /* check that '@' occurs before '/', if '/' exists at all */
119         q = strchr_m(p, '@');
120         r = strchr_m(p, '/');
121         if (q && (!r || q < r)) {
122                 pstring username, passwd, domain;
123                 char *u = userinfo;
124
125                 next_token(&p, userinfo, "@", sizeof(fstring));
126
127                 username[0] = passwd[0] = domain[0] = 0;
128
129                 if (strchr_m(u, ';')) {
130       
131                         next_token(&u, domain, ";", sizeof(fstring));
132
133                 }
134
135                 if (strchr_m(u, ':')) {
136
137                         next_token(&u, username, ":", sizeof(fstring));
138
139                         pstrcpy(passwd, u);
140
141                 }
142                 else {
143
144                         pstrcpy(username, u);
145
146                 }
147
148                 if (username[0])
149                         strncpy(user, username, sizeof(fstring));  /* FIXME, size and domain */
150
151                 if (passwd[0])
152                         strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */
153
154         }
155
156         if (!next_token(&p, server, "/", sizeof(fstring))) {
157
158                 return -1;
159
160         }
161
162         if (*p == (char)0) return 0;  /* That's it ... */
163   
164         if (!next_token(&p, share, "/", sizeof(fstring))) {
165
166                 return -1;
167
168         }
169
170         pstrcpy(path, p);
171   
172         all_string_sub(path, "/", "\\", 0);
173
174         return 0;
175 }
176
177 /*
178  * Convert an SMB error into a UNIX error ...
179  */
180
181 static int smbc_errno(SMBCCTX *context, struct cli_state *c)
182 {
183         int ret;
184
185         if (cli_is_dos_error(c)) {
186                 uint8 eclass;
187                 uint32 ecode;
188
189                 cli_dos_error(c, &eclass, &ecode);
190                 ret = cli_errno_from_dos(eclass, ecode);
191                 
192                 DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", 
193                          (int)eclass, (int)ecode, (int)ecode, ret));
194         } else {
195                 NTSTATUS status;
196
197                 status = cli_nt_error(c);
198                 ret = cli_errno_from_nt(status);
199
200                 DEBUG(3,("smbc errno %s -> %d\n",
201                          get_nt_error_msg(status), ret));
202         }
203
204         return ret;
205 }
206
207 /* 
208  * Check a server_fd.
209  * returns 0 if the server is in shape. Returns 1 on error 
210  * 
211  * Also useable outside libsmbclient to enable external cache
212  * to do some checks too.
213  */
214 int smbc_check_server(SMBCCTX * context, SMBCSRV * server) 
215 {
216         if ( cli_send_keepalive(&server->cli) == False )
217                 return 1;
218
219         /* connection is ok */
220         return 0;
221 }
222
223 /* 
224  * Remove a server from the list server_table if it's unused.
225  * On success, 0 is returned. 1 is returned if the server could not be removed.
226  * 
227  * Also useable outside libsmbclient
228  */
229 int smbc_remove_unused_server(SMBCCTX * context, SMBCSRV * srv)
230 {
231         SMBCFILE * file;
232
233         /* are we being fooled ? */
234         if (!context || !context->_initialized || !srv) return 1;
235
236         
237         /* Check all open files/directories for a relation with this server */
238         for (file = context->_files; file; file=file->next) {
239                 if (file->srv == srv) {
240                         /* Still used */
241                         DEBUG(3, ("smbc_remove_usused_server: %p still used by %p.\n", 
242                                   srv, file));
243                         return 1;
244                 }
245         }
246
247         DLIST_REMOVE(context->_servers, srv);
248
249         cli_shutdown(&srv->cli);
250
251         DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
252
253         context->callbacks.remove_cached_srv_fn(context, srv);
254         
255         SAFE_FREE(srv);
256         
257         return 0;
258 }
259
260 /*
261  * Connect to a server, possibly on an existing connection
262  *
263  * Here, what we want to do is: If the server and username
264  * match an existing connection, reuse that, otherwise, establish a 
265  * new connection.
266  *
267  * If we have to create a new connection, call the auth_fn to get the
268  * info we need, unless the username and password were passed in.
269  */
270
271 SMBCSRV *smbc_server(SMBCCTX *context,
272                      char *server, char *share, 
273                      char *workgroup, char *username, 
274                      char *password)
275 {
276         SMBCSRV *srv=NULL;
277         int auth_called = 0;
278         struct cli_state c;
279         struct nmb_name called, calling;
280         char *p, *server_n = server;
281         fstring group;
282         pstring ipenv;
283         struct in_addr ip;
284         int tried_reverse = 0;
285   
286         zero_ip(&ip);
287         ZERO_STRUCT(c);
288
289         if (server[0] == 0) {
290                 errno = EPERM;
291                 return NULL;
292         }
293
294  check_server_cache:
295
296         srv = context->callbacks.get_cached_srv_fn(context, server, share, 
297                                                    workgroup, username);
298         
299         if (!auth_called && !srv && (!username[0] || !password[0])) {
300                 context->callbacks.auth_fn(server, share, workgroup, sizeof(fstring),
301                              username, sizeof(fstring), password, sizeof(fstring));
302                 /* 
303                  * However, smbc_auth_fn may have picked up info relating to an 
304                  * existing connection, so try for an existing connection again ...
305                  */
306                 auth_called = 1;
307                 goto check_server_cache;
308                 
309         }
310         
311         if (srv) {
312                 if (context->callbacks.check_server_fn(context, srv)) {
313                         /* 
314                          * This server is no good anymore 
315                          * Try to remove it and check for more possible servers in the cache 
316                          */
317                         if (context->callbacks.remove_unused_server_fn(context, srv)) { 
318                                 /* 
319                                  * We could not remove the server completely, remove it from the cache
320                                  * so we will not get it again. It will be removed when the last file/dir
321                                  * is closed.
322                                  */
323                                 context->callbacks.remove_cached_srv_fn(context, srv);
324                         }
325                         
326                         /* 
327                          * Maybe there are more cached connections to this server 
328                          */
329                         goto check_server_cache; 
330                 }
331                 return srv;
332         }
333
334         make_nmb_name(&calling, context->netbios_name, 0x0);
335         make_nmb_name(&called , server, 0x20);
336
337         DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
338   
339         if ((p=strchr_m(server_n,'#')) && 
340             (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
341     
342                 fstrcpy(group, server_n);
343                 p = strchr_m(group,'#');
344                 *p = 0;
345                 
346         }
347
348         DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
349
350  again:
351         slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n);
352
353         zero_ip(&ip);
354
355         /* have to open a new connection */
356         if (!cli_initialise(&c)) {
357                 errno = ENOENT;
358                 return NULL;
359         }
360
361         c.timeout = context->timeout;
362
363         if (!cli_connect(&c, server_n, &ip)) {
364                 cli_shutdown(&c);
365                 errno = ENOENT;
366                 return NULL;
367         }
368
369         if (!cli_session_request(&c, &calling, &called)) {
370                 cli_shutdown(&c);
371                 if (strcmp(called.name, "*SMBSERVER")) {
372                         make_nmb_name(&called , "*SMBSERVER", 0x20);
373                         goto again;
374                 }
375                 else {  /* Try one more time, but ensure we don't loop */
376
377                   /* Only try this if server is an IP address ... */
378
379                   if (is_ipaddress(server) && !tried_reverse) {
380                     fstring remote_name;
381                     struct in_addr rem_ip;
382
383                     if (!inet_aton(server, &rem_ip)) {
384                       DEBUG(4, ("Could not convert IP address %s to struct in_addr\n", server));
385                       errno = ENOENT;
386                       return NULL;
387                     }
388
389                     tried_reverse++; /* Yuck */
390
391                     if (name_status_find("*", 0, 0, rem_ip, remote_name)) {
392                       make_nmb_name(&called, remote_name, 0x20);
393                       goto again;
394                     }
395
396
397                   }
398                 }
399                 errno = ENOENT;
400                 return NULL;
401         }
402   
403         DEBUG(4,(" session request ok\n"));
404   
405         if (!cli_negprot(&c)) {
406                 cli_shutdown(&c);
407                 errno = ENOENT;
408                 return NULL;
409         }
410
411         if (!cli_session_setup(&c, username, 
412                                password, strlen(password),
413                                password, strlen(password),
414                                workgroup) &&
415             /* try an anonymous login if it failed */
416             !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) {
417                 cli_shutdown(&c);
418                 errno = EPERM;
419                 return NULL;
420         }
421
422         DEBUG(4,(" session setup ok\n"));
423
424         if (!cli_send_tconX(&c, share, "?????",
425                             password, strlen(password)+1)) {
426                 errno = smbc_errno(context, &c);
427                 cli_shutdown(&c);
428                 return NULL;
429         }
430   
431         DEBUG(4,(" tconx ok\n"));
432   
433         /*
434          * Ok, we have got a nice connection
435          * Let's find a free server_fd 
436          */
437
438         srv = (SMBCSRV *)malloc(sizeof(*srv));
439         if (!srv) {
440                 errno = ENOMEM;
441                 goto failed;
442         }
443
444         ZERO_STRUCTP(srv);
445         srv->cli = c;
446         srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
447
448         /* now add it to the cache (internal or external) */
449         if (context->callbacks.add_cached_srv_fn(context, srv, server, share, workgroup, username)) {
450                 DEBUG(3, (" Failed to add server to cache\n"));
451                 goto failed;
452         }
453
454         
455         DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
456                   server, share, srv));
457
458         return srv;
459
460  failed:
461         cli_shutdown(&c);
462         if (!srv) return NULL;
463   
464         SAFE_FREE(srv);
465         return NULL;
466 }
467
468 /*
469  * Routine to open() a file ...
470  */
471
472 static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, mode_t mode)
473 {
474         fstring server, share, user, password, workgroup;
475         pstring path;
476         SMBCSRV *srv   = NULL;
477         SMBCFILE *file = NULL;
478         int fd;
479
480         if (!context || !context->_initialized) {
481
482                 errno = EINVAL;  /* Best I can think of ... */
483                 return NULL;
484
485         }
486
487         if (!fname) {
488
489                 errno = EINVAL;
490                 return NULL;
491
492         }
493
494         smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
495
496         if (user[0] == (char)0) pstrcpy(user, context->user);
497
498         pstrcpy(workgroup, context->workgroup);
499
500         srv = smbc_server(context, server, share, workgroup, user, password);
501
502         if (!srv) {
503
504                 if (errno == EPERM) errno = EACCES;
505                 return NULL;  /* smbc_server sets errno */
506     
507         }
508
509         /* Hmmm, the test for a directory is suspect here ... FIXME */
510
511         if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
512     
513                 fd = -1;
514
515         }
516         else {
517           
518                 file = malloc(sizeof(SMBCFILE));
519
520                 if (!file) {
521
522                         errno = ENOMEM;
523                         return NULL;
524
525                 }
526
527                 ZERO_STRUCTP(file);
528
529                 if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) {
530
531                         /* Handle the error ... */
532
533                         SAFE_FREE(file);
534                         errno = smbc_errno(context, &srv->cli);
535                         return NULL;
536
537                 }
538
539                 /* Fill in file struct */
540
541                 file->cli_fd  = fd;
542                 file->fname   = strdup(fname);
543                 file->srv     = srv;
544                 file->offset  = 0;
545                 file->file    = True;
546
547                 DLIST_ADD(context->_files, file);
548                 return file;
549
550         }
551
552         /* Check if opendir needed ... */
553
554         if (fd == -1) {
555                 int eno = 0;
556
557                 eno = smbc_errno(context, &srv->cli);
558                 file = context->opendir(context, fname);
559                 if (!file) errno = eno;
560                 return file;
561
562         }
563
564         errno = EINVAL; /* FIXME, correct errno ? */
565         return NULL;
566
567 }
568
569 /*
570  * Routine to create a file 
571  */
572
573 static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */
574
575 static SMBCFILE *smbc_creat_ctx(SMBCCTX *context, const char *path, mode_t mode)
576 {
577
578         if (!context || !context->_initialized) {
579
580                 errno = EINVAL;
581                 return NULL;
582
583         }
584
585         return smbc_open_ctx(context, path, creat_bits, mode);
586 }
587
588 /*
589  * Routine to read() a file ...
590  */
591
592 static ssize_t smbc_read_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
593 {
594         int ret;
595
596         if (!context || !context->_initialized) {
597
598                 errno = EINVAL;
599                 return -1;
600
601         }
602
603         DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
604
605         if (!file || !DLIST_CONTAINS(context->_files, file)) {
606
607                 errno = EBADF;
608                 return -1;
609
610         }
611
612         /* Check that the buffer exists ... */
613
614         if (buf == NULL) {
615
616                 errno = EINVAL;
617                 return -1;
618
619         }
620
621         ret = cli_read(&file->srv->cli, file->cli_fd, buf, file->offset, count);
622
623         if (ret < 0) {
624
625                 errno = smbc_errno(context, &file->srv->cli);
626                 return -1;
627
628         }
629
630         file->offset += ret;
631
632         DEBUG(4, ("  --> %d\n", ret));
633
634         return ret;  /* Success, ret bytes of data ... */
635
636 }
637
638 /*
639  * Routine to write() a file ...
640  */
641
642 static ssize_t smbc_write_ctx(SMBCCTX *context, SMBCFILE *file, void *buf, size_t count)
643 {
644         int ret;
645
646         if (!context || context->_initialized) {
647
648                 errno = EINVAL;
649                 return -1;
650
651         }
652
653         if (!file || !DLIST_CONTAINS(context->_files, file)) {
654
655                 errno = EBADF;
656                 return -1;
657     
658         }
659
660         /* Check that the buffer exists ... */
661
662         if (buf == NULL) {
663
664                 errno = EINVAL;
665                 return -1;
666
667         }
668
669         ret = cli_write(&file->srv->cli, file->cli_fd, 0, buf, file->offset, count);
670
671         if (ret <= 0) {
672
673                 errno = smbc_errno(context, &file->srv->cli);
674                 return -1;
675
676         }
677
678         file->offset += ret;
679
680         return ret;  /* Success, 0 bytes of data ... */
681 }
682  
683 /*
684  * Routine to close() a file ...
685  */
686
687 static int smbc_close_ctx(SMBCCTX *context, SMBCFILE *file)
688 {
689         SMBCSRV *srv; 
690
691         if (!context || !context->_initialized) {
692
693                 errno = EINVAL;
694                 return -1;
695
696         }
697
698         if (!file || !DLIST_CONTAINS(context->_files, file)) {
699    
700                 errno = EBADF;
701                 return -1;
702
703         }
704
705         /* IS a dir ... */
706         if (!file->file) {
707                 
708                 return context->closedir(context, file);
709
710         }
711
712         if (!cli_close(&file->srv->cli, file->cli_fd)) {
713
714                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
715                           file->fname));
716                 /* Deallocate slot and remove the server 
717                  * from the server cache if unused */
718                 errno = smbc_errno(context, &file->srv->cli);  
719                 srv = file->srv;
720                 DLIST_REMOVE(context->_files, file);
721                 SAFE_FREE(file->fname);
722                 SAFE_FREE(file);
723                 context->callbacks.remove_unused_server_fn(context, srv);
724
725                 return -1;
726
727         }
728
729         if (!file->file) {
730
731                 return context->closedir(context, file);
732
733         }
734
735         if (!cli_close(&file->srv->cli, file->cli_fd)) {
736                 DEBUG(3, ("cli_close failed on %s. purging server.\n", 
737                           file->fname));
738                 /* Deallocate slot and remove the server 
739                  * from the server cache if unused */
740                 errno = smbc_errno(context, &file->srv->cli);  
741                 srv = file->srv;
742                 DLIST_REMOVE(context->_files, file);
743                 SAFE_FREE(file->fname);
744                 SAFE_FREE(file);
745                 context->callbacks.remove_unused_server_fn(context, srv);
746
747                 return -1;
748         }
749
750         DLIST_REMOVE(context->_files, file);
751         SAFE_FREE(file->fname);
752         SAFE_FREE(file);
753
754         return 0;
755 }
756
757 /*
758  * Get info from an SMB server on a file. Use a qpathinfo call first
759  * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
760  */
761 static BOOL smbc_getatr(SMBCCTX * context, SMBCSRV *srv, char *path, 
762                  uint16 *mode, size_t *size, 
763                  time_t *c_time, time_t *a_time, time_t *m_time,
764                  SMB_INO_T *ino)
765 {
766
767         if (!context || !context->_initialized) {
768  
769                 errno = EINVAL;
770                 return -1;
771  
772         }
773
774         DEBUG(4,("smbc_getatr: sending qpathinfo\n"));
775   
776         if (!srv->no_pathinfo2 &&
777             cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL,
778                            size, mode, ino)) return True;
779
780         /* if this is NT then don't bother with the getatr */
781         if (srv->cli.capabilities & CAP_NT_SMBS) return False;
782
783         if (cli_getatr(&srv->cli, path, mode, size, m_time)) {
784                 a_time = c_time = m_time;
785                 srv->no_pathinfo2 = True;
786                 return True;
787         }
788
789         return False;
790
791 }
792
793 /*
794  * Routine to unlink() a file
795  */
796
797 static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
798 {
799         fstring server, share, user, password, workgroup;
800         pstring path;
801         SMBCSRV *srv = NULL;
802
803         if (!context || context->_initialized) {
804
805                 errno = EINVAL;  /* Best I can think of ... */
806                 return -1;
807
808         }
809
810         if (!fname) {
811
812                 errno = EINVAL;
813                 return -1;
814
815         }
816
817         smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
818
819         if (user[0] == (char)0) pstrcpy(user, context->user);
820
821         pstrcpy(workgroup, context->workgroup);
822
823         srv = smbc_server(context, server, share, workgroup, user, password);
824
825         if (!srv) {
826
827                 return -1;  /* smbc_server sets errno */
828
829         }
830
831         /*  if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
832
833     int job = smbc_stat_printjob(srv, path, NULL, NULL);
834     if (job == -1) {
835
836       return -1;
837
838     }
839     if ((err = cli_printjob_del(&srv->cli, job)) != 0) {
840
841     
842       return -1;
843
844     }
845     } else */
846
847         if (!cli_unlink(&srv->cli, path)) {
848
849                 errno = smbc_errno(context, &srv->cli);
850
851                 if (errno == EACCES) { /* Check if the file is a directory */
852
853                         int saverr = errno;
854                         size_t size = 0;
855                         uint16 mode = 0;
856                         time_t m_time = 0, a_time = 0, c_time = 0;
857                         SMB_INO_T ino = 0;
858
859                         if (!smbc_getatr(context, srv, path, &mode, &size,
860                                          &c_time, &a_time, &m_time, &ino)) {
861
862                                 /* Hmmm, bad error ... What? */
863
864                                 errno = smbc_errno(context, &srv->cli);
865                                 return -1;
866
867                         }
868                         else {
869
870                                 if (IS_DOS_DIR(mode))
871                                         errno = EISDIR;
872                                 else
873                                         errno = saverr;  /* Restore this */
874
875                         }
876                 }
877
878                 return -1;
879
880         }
881
882         return 0;  /* Success ... */
883
884 }
885
886 /*
887  * Routine to rename() a file
888  */
889
890 static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname, 
891                            SMBCCTX *ncontext, const char *nname)
892 {
893         fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup;
894         pstring path1, path2;
895         SMBCSRV *srv = NULL;
896
897         if (!ocontext || !ncontext ||
898             !ocontext->_initialized || !ncontext->_initialized) {
899
900                 errno = EINVAL;  /* Best I can think of ... */
901                 return -1;
902
903         }
904         
905         if (!oname || !nname) {
906
907                 errno = EINVAL;
908                 return -1;
909
910         }
911         
912         DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
913
914         smbc_parse_path(ocontext, oname, server1, share1, path1, user1, password1);
915
916         if (user1[0] == (char)0) pstrcpy(user1, ocontext->user);
917
918         smbc_parse_path(ncontext, nname, server2, share2, path2, user2, password2);
919
920         if (user2[0] == (char)0) pstrcpy(user2, ncontext->user);
921
922         if (strcmp(server1, server2) || strcmp(share1, share2) ||
923             strcmp(user1, user2)) {
924
925                 /* Can't rename across file systems, or users?? */
926
927                 errno = EXDEV;
928                 return -1;
929
930         }
931
932         pstrcpy(workgroup, ocontext->workgroup);
933         /* HELP !!! Which workgroup should I use ? Or are they always the same -- Tom */ 
934         srv = smbc_server(ocontext, server1, share1, workgroup, user1, password1);
935         if (!srv) {
936
937                 return -1;
938
939         }
940
941         if (!cli_rename(&srv->cli, path1, path2)) {
942                 int eno = smbc_errno(ocontext, &srv->cli);
943
944                 if (eno != EEXIST ||
945                     !cli_unlink(&srv->cli, path2) ||
946                     !cli_rename(&srv->cli, path1, path2)) {
947
948                         errno = eno;
949                         return -1;
950
951                 }
952         }
953
954         return 0; /* Success */
955
956 }
957
958 /*
959  * A routine to lseek() a file
960  */
961
962 static off_t smbc_lseek_ctx(SMBCCTX *context, SMBCFILE *file, off_t offset, int whence)
963 {
964         size_t size;
965
966         if (!context || !context->_initialized) {
967
968                 errno = EINVAL;
969                 return -1;
970                 
971         }
972
973         if (!file || !DLIST_CONTAINS(context->_files, file)) {
974
975                 errno = EBADF;
976                 return -1;
977
978         }
979
980         if (!file->file) {
981
982                 errno = EINVAL;
983                 return -1;      /* Can't lseek a dir ... */
984
985         }
986
987         switch (whence) {
988         case SEEK_SET:
989                 file->offset = offset;
990                 break;
991
992         case SEEK_CUR:
993                 file->offset += offset;
994                 break;
995
996         case SEEK_END:
997                 if (!cli_qfileinfo(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
998                                    NULL, NULL, NULL) &&
999                     !cli_getattrE(&file->srv->cli, file->cli_fd, NULL, &size, NULL, NULL,
1000                                   NULL)) {
1001
1002                         errno = EINVAL;
1003                         return -1;
1004                 }
1005                 file->offset = size + offset;
1006                 break;
1007
1008         default:
1009                 errno = EINVAL;
1010                 break;
1011
1012         }
1013
1014         return file->offset;
1015
1016 }
1017
1018 /* 
1019  * Generate an inode number from file name for those things that need it
1020  */
1021
1022 static
1023 ino_t smbc_inode(SMBCCTX *context, const char *name)
1024 {
1025
1026         if (!context || !context->_initialized) {
1027
1028                 errno = EINVAL;
1029                 return -1;
1030
1031         }
1032
1033         if (!*name) return 2; /* FIXME, why 2 ??? */
1034         return (ino_t)str_checksum(name);
1035
1036 }
1037
1038 /*
1039  * Routine to put basic stat info into a stat structure ... Used by stat and
1040  * fstat below.
1041  */
1042
1043 static
1044 int smbc_setup_stat(SMBCCTX *context, struct stat *st, char *fname, size_t size, int mode)
1045 {
1046         
1047         st->st_mode = 0;
1048
1049         if (IS_DOS_DIR(mode)) {
1050                 st->st_mode = SMBC_DIR_MODE;
1051         } else {
1052                 st->st_mode = SMBC_FILE_MODE;
1053         }
1054
1055         if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR;
1056         if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP;
1057         if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH;
1058         if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR;
1059
1060         st->st_size = size;
1061         st->st_blksize = 512;
1062         st->st_blocks = (size+511)/512;
1063         st->st_uid = getuid();
1064         st->st_gid = getgid();
1065
1066         if (IS_DOS_DIR(mode)) {
1067                 st->st_nlink = 2;
1068         } else {
1069                 st->st_nlink = 1;
1070         }
1071
1072         if (st->st_ino == 0) {
1073                 st->st_ino = smbc_inode(context, fname);
1074         }
1075         
1076         return True;  /* FIXME: Is this needed ? */
1077
1078 }
1079
1080 /*
1081  * Routine to stat a file given a name
1082  */
1083
1084 static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
1085 {
1086         SMBCSRV *srv;
1087         fstring server, share, user, password, workgroup;
1088         pstring path;
1089         time_t m_time = 0, a_time = 0, c_time = 0;
1090         size_t size = 0;
1091         uint16 mode = 0;
1092         SMB_INO_T ino = 0;
1093
1094         if (!context || !context->_initialized) {
1095
1096                 errno = EINVAL;  /* Best I can think of ... */
1097                 return -1;
1098     
1099         }
1100
1101         if (!fname) {
1102
1103                 errno = EINVAL;
1104                 return -1;
1105
1106         }
1107   
1108         DEBUG(4, ("smbc_stat(%s)\n", fname));
1109
1110         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1111
1112         if (user[0] == (char)0) pstrcpy(user, context->user);
1113
1114         pstrcpy(workgroup, context->workgroup);
1115
1116         srv = smbc_server(context, server, share, workgroup, user, password);
1117
1118         if (!srv) {
1119
1120                 return -1;  /* errno set by smbc_server */
1121
1122         }
1123
1124         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1125
1126            mode = aDIR | aRONLY;
1127
1128            }
1129            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1130            
1131            if (strcmp(path, "\\") == 0) {
1132            
1133            mode = aDIR | aRONLY;
1134
1135            }
1136            else {
1137
1138            mode = aRONLY;
1139            smbc_stat_printjob(srv, path, &size, &m_time);
1140            c_time = a_time = m_time;
1141
1142            }
1143            else { */
1144
1145         if (!smbc_getatr(context, srv, path, &mode, &size, 
1146                          &c_time, &a_time, &m_time, &ino)) {
1147
1148                 errno = smbc_errno(context, &srv->cli);
1149                 return -1;
1150                 
1151         }
1152
1153         st->st_ino = ino;
1154
1155         smbc_setup_stat(context, st, path, size, mode);
1156
1157         st->st_atime = a_time;
1158         st->st_ctime = c_time;
1159         st->st_mtime = m_time;
1160         st->st_dev   = srv->dev;
1161
1162         return 0;
1163
1164 }
1165
1166 /*
1167  * Routine to stat a file given an fd
1168  */
1169
1170 static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
1171 {
1172         time_t c_time, a_time, m_time;
1173         size_t size;
1174         uint16 mode;
1175         SMB_INO_T ino = 0;
1176
1177         if (!context || !context->_initialized) {
1178
1179                 errno = EINVAL;
1180                 return -1;
1181
1182         }
1183
1184         if (!file || !DLIST_CONTAINS(context->_files, file)) {
1185
1186                 errno = EBADF;
1187                 return -1;
1188
1189         }
1190
1191         if (!file->file) {
1192
1193                 return context->fstatdir(context, file, st);
1194
1195         }
1196
1197         if (!cli_qfileinfo(&file->srv->cli, file->cli_fd,
1198                            &mode, &size, &c_time, &a_time, &m_time, NULL, &ino) &&
1199             !cli_getattrE(&file->srv->cli, file->cli_fd,
1200                           &mode, &size, &c_time, &a_time, &m_time)) {
1201
1202                 errno = EINVAL;
1203                 return -1;
1204
1205         }
1206
1207         st->st_ino = ino;
1208
1209         smbc_setup_stat(context, st, file->fname, size, mode);
1210
1211         st->st_atime = a_time;
1212         st->st_ctime = c_time;
1213         st->st_mtime = m_time;
1214         st->st_dev = file->srv->dev;
1215
1216         return 0;
1217
1218 }
1219
1220 /*
1221  * Routine to open a directory
1222  *
1223  * We want to allow:
1224  *
1225  * smb: which should list all the workgroups available
1226  * smb:workgroup
1227  * smb:workgroup//server
1228  * smb://server
1229  * smb://server/share
1230  * smb://<IP-addr> which should list shares on server
1231  * smb://<IP-addr>/share which should list files on share
1232  */
1233
1234 static void smbc_remove_dir(SMBCFILE *dir)
1235 {
1236         struct smbc_dir_list *d,*f;
1237
1238         d = dir->dir_list;
1239         while (d) {
1240
1241                 f = d; d = d->next;
1242
1243                 SAFE_FREE(f->dirent);
1244                 SAFE_FREE(f);
1245
1246         }
1247
1248         dir->dir_list = dir->dir_end = dir->dir_next = NULL;
1249
1250 }
1251
1252 static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint32 type)
1253 {
1254         struct smbc_dirent *dirent;
1255         int size;
1256
1257         /*
1258          * Allocate space for the dirent, which must be increased by the 
1259          * size of the name and the comment and 1 for the null on the comment.
1260          * The null on the name is already accounted for.
1261          */
1262
1263         size = sizeof(struct smbc_dirent) + (name?strlen(name):0) +
1264                 (comment?strlen(comment):0) + 1; 
1265     
1266         dirent = malloc(size);
1267
1268         if (!dirent) {
1269
1270                 dir->dir_error = ENOMEM;
1271                 return -1;
1272
1273         }
1274
1275         ZERO_STRUCTP(dirent);
1276
1277         ZERO_STRUCTP(dirent);
1278
1279
1280         if (dir->dir_list == NULL) {
1281
1282                 dir->dir_list = malloc(sizeof(struct smbc_dir_list));
1283                 if (!dir->dir_list) {
1284
1285                         SAFE_FREE(dirent);
1286                         dir->dir_error = ENOMEM;
1287                         return -1;
1288
1289                 }
1290                 ZERO_STRUCTP(dir->dir_list);
1291
1292                 dir->dir_end = dir->dir_next = dir->dir_list;
1293   
1294         }
1295         else {
1296
1297                 dir->dir_end->next = malloc(sizeof(struct smbc_dir_list));
1298                 
1299                 if (!dir->dir_end->next) {
1300                         
1301                         SAFE_FREE(dirent);
1302                         dir->dir_error = ENOMEM;
1303                         return -1;
1304
1305                 }
1306                 ZERO_STRUCTP(dir->dir_end->next);
1307
1308                 dir->dir_end = dir->dir_end->next;
1309
1310         }
1311
1312         dir->dir_end->next = NULL;
1313         dir->dir_end->dirent = dirent;
1314         
1315         dirent->smbc_type = type;
1316         dirent->namelen = (name?strlen(name):0);
1317         dirent->commentlen = (comment?strlen(comment):0);
1318         dirent->dirlen = size;
1319   
1320         strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
1321
1322         dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
1323         strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
1324
1325         return 0;
1326
1327 }
1328
1329 static void
1330 list_fn(const char *name, uint32 type, const char *comment, void *state)
1331 {
1332         SMBCFILE *dir = (SMBCFILE *)state;
1333         int dirent_type;
1334
1335         /* We need to process the type a little ... */
1336
1337         if (dir->dir_type == SMBC_FILE_SHARE) {
1338                 
1339                 switch (type) {
1340                 case 0: /* Directory tree */
1341                         dirent_type = SMBC_FILE_SHARE;
1342                         break;
1343
1344                 case 1:
1345                         dirent_type = SMBC_PRINTER_SHARE;
1346                         break;
1347
1348                 case 2:
1349                         dirent_type = SMBC_COMMS_SHARE;
1350                         break;
1351
1352                 case 3:
1353                         dirent_type = SMBC_IPC_SHARE;
1354                         break;
1355
1356                 default:
1357                         dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
1358                         break;
1359                 }
1360                 ZERO_STRUCTP(dir->dir_list);
1361
1362         }
1363         else dirent_type = dir->dir_type;
1364
1365         if (add_dirent(dir, name, comment, dirent_type) < 0) {
1366
1367                 /* An error occurred, what do we do? */
1368                 /* FIXME: Add some code here */
1369
1370         }
1371
1372 }
1373
1374 static void
1375 dir_list_fn(file_info *finfo, const char *mask, void *state)
1376 {
1377
1378         if (add_dirent((SMBCFILE *)state, finfo->name, "", 
1379                        (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
1380
1381                 /* Handle an error ... */
1382
1383                 /* FIXME: Add some code ... */
1384
1385         } 
1386
1387 }
1388
1389 static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
1390 {
1391         fstring server, share, user, password, workgroup;
1392         pstring path;
1393         SMBCSRV *srv  = NULL;
1394         SMBCFILE *dir = NULL;
1395         struct in_addr rem_ip;
1396         int slot = 0;
1397
1398         if (!context || !context->_initialized) {
1399
1400                 errno = EINVAL;
1401                 return NULL;
1402
1403         }
1404
1405         if (!fname) {
1406     
1407                 errno = EINVAL;
1408                 return NULL;
1409
1410         }
1411
1412         if (smbc_parse_path(context, fname, server, share, path, user, password)) {
1413
1414                 errno = EINVAL;
1415                 return NULL;
1416
1417         }
1418
1419         if (user[0] == (char)0) pstrcpy(user, context->user);
1420
1421         pstrcpy(workgroup, context->workgroup);
1422
1423         dir = malloc(sizeof(*dir));
1424
1425         if (!dir) {
1426
1427                 errno = ENOMEM;
1428                 return NULL;
1429
1430         }
1431
1432         ZERO_STRUCTP(dir);
1433
1434         dir->cli_fd   = 0;
1435         dir->fname    = strdup(fname);
1436         dir->srv      = NULL;
1437         dir->offset   = 0;
1438         dir->file     = False;
1439         dir->dir_list = dir->dir_next = dir->dir_end = NULL;
1440
1441         if (server[0] == (char)0) {
1442
1443                 if (share[0] != (char)0 || path[0] != (char)0) {
1444     
1445                         errno = EINVAL;
1446                         if (dir) {
1447                                 SAFE_FREE(dir->fname);
1448                                 SAFE_FREE(dir);
1449                         }
1450                         return NULL;
1451
1452                 }
1453
1454                 /* We have server and share and path empty ... so list the workgroups */
1455                 /* first try to get the LMB for our workgroup, and if that fails,     */
1456                 /* try the DMB                                                        */
1457
1458                 if (!(resolve_name(context->workgroup, &rem_ip, 0x1d) ||
1459                       resolve_name(context->workgroup, &rem_ip, 0x1b))) {
1460       
1461                         errno = EINVAL;  /* Something wrong with smb.conf? */
1462                         return NULL;
1463
1464                 }
1465
1466                 dir->dir_type = SMBC_WORKGROUP;
1467
1468                 /* find the name of the server ... */
1469
1470                 if (!name_status_find("*", 0, 0, rem_ip, server)) {
1471
1472                         DEBUG(0,("Could not get the name of local/domain master browser for server %s\n", server));
1473                         errno = EINVAL;
1474                         return NULL;
1475
1476                 }
1477
1478                 /*
1479                  * Get a connection to IPC$ on the server if we do not already have one
1480                  */
1481
1482                 srv = smbc_server(context, server, "IPC$", workgroup, user, password);
1483
1484                 if (!srv) {
1485
1486                         if (dir) {
1487                                 SAFE_FREE(dir->fname);
1488                                 SAFE_FREE(dir);
1489                         }
1490                         
1491                         return NULL;
1492
1493                 }
1494                 ZERO_STRUCTP(dir->dir_end);
1495
1496                 dir->srv = srv;
1497
1498                 /* Now, list the stuff ... */
1499
1500                 if (!cli_NetServerEnum(&srv->cli, workgroup, 0x80000000, list_fn,
1501                                        (void *)dir)) {
1502
1503                         if (dir) {
1504                                 SAFE_FREE(dir->fname);
1505                                 SAFE_FREE(dir);
1506                         }
1507                         errno = cli_errno(&srv->cli);
1508
1509                         return NULL;
1510
1511                 }
1512         }
1513         else { /* Server not an empty string ... Check the rest and see what gives */
1514
1515                 if (share[0] == (char)0) {
1516
1517                         if (path[0] != (char)0) { /* Should not have empty share with path */
1518
1519                                 errno = EINVAL;
1520                                 if (dir) {
1521                                         SAFE_FREE(dir->fname);
1522                                         SAFE_FREE(dir);
1523                                 }
1524                                 return NULL;
1525         
1526                         }
1527
1528                         /* Check to see if <server><1D>, <server><1B>, or <server><20> translates */
1529                         /* However, we check to see if <server> is an IP address first */
1530
1531                         if (!is_ipaddress(server) &&  /* Not an IP addr so check next */
1532                             (resolve_name(server, &rem_ip, 0x1d) ||   /* Found LMB */
1533                                     resolve_name(server, &rem_ip, 0x1b) )) { /* Found DMB */
1534                                 pstring buserver;
1535
1536                                 dir->dir_type = SMBC_SERVER;
1537
1538                                 /*
1539                                  * Get the backup list ...
1540                                  */
1541
1542
1543                                 if (!name_status_find("*", 0, 0, rem_ip, buserver)) {
1544
1545                                         DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
1546                                         errno = EPERM;  /* FIXME, is this correct */
1547                                         return NULL;
1548
1549                                 }
1550
1551                                 /*
1552                                  * Get a connection to IPC$ on the server if we do not already have one
1553                                  */
1554
1555                                 srv = smbc_server(context, buserver, "IPC$", workgroup, user, password);
1556
1557                                 if (!srv) {
1558
1559                                         if (dir) {
1560                                                 SAFE_FREE(dir->fname);
1561                                                 SAFE_FREE(dir);
1562                                         }
1563                                         return NULL;
1564
1565                                 }
1566
1567                                 dir->srv = srv;
1568
1569                                 /* Now, list the servers ... */
1570
1571                                 if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn,
1572                                                        (void *)dir)) {
1573
1574                                         if (dir) {
1575                                                 SAFE_FREE(dir->fname);
1576                                                 SAFE_FREE(dir);
1577                                         }
1578                                         errno = cli_errno(&srv->cli);
1579                                         return NULL;
1580                                         
1581                                 }
1582
1583                         }
1584                         else {
1585
1586                                 if (resolve_name(server, &rem_ip, 0x20)) {
1587
1588                                         /* Now, list the shares ... */
1589
1590                                         dir->dir_type = SMBC_FILE_SHARE;
1591
1592                                         srv = smbc_server(context, server, "IPC$", workgroup, user, password);
1593
1594                                         if (!srv) {
1595
1596                                                 if (dir) {
1597                                                         SAFE_FREE(dir->fname);
1598                                                         SAFE_FREE(dir);
1599                                                 }
1600                                                 return NULL;
1601
1602                                         }
1603
1604                                         dir->srv = srv;
1605
1606                                         /* Now, list the servers ... */
1607
1608                                         if (cli_RNetShareEnum(&srv->cli, list_fn, 
1609                                                               (void *)dir) < 0) {
1610
1611                                                 errno = cli_errno(&srv->cli);
1612                                                 if (dir) {
1613                                                         SAFE_FREE(dir->fname);
1614                                                         SAFE_FREE(dir);
1615                                                 }
1616                                                 return NULL;
1617
1618                                         }
1619
1620                                 }
1621                                 else {
1622
1623                                         errno = ENODEV;   /* Neither the workgroup nor server exists */
1624                                         if (dir) {
1625                                                 SAFE_FREE(dir->fname);
1626                                                 SAFE_FREE(dir);
1627                                         }
1628                                         return NULL;
1629
1630                                 }
1631
1632                         }
1633
1634                 }
1635                 else { /* The server and share are specified ... work from there ... */
1636
1637                         /* Well, we connect to the server and list the directory */
1638
1639                         dir->dir_type = SMBC_FILE_SHARE;
1640
1641                         srv = smbc_server(context, server, share, workgroup, user, password);
1642
1643                         if (!srv) {
1644
1645                                 if (dir) {
1646                                         SAFE_FREE(dir->fname);
1647                                         SAFE_FREE(dir);
1648                                 }
1649                                 return NULL;
1650
1651                         }
1652
1653                         dir->srv = srv;
1654
1655                         /* Now, list the files ... */
1656
1657                         pstrcat(path, "\\*");
1658
1659                         if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, 
1660                                      (void *)dir) < 0) {
1661
1662                                 if (dir) {
1663                                         SAFE_FREE(dir->fname);
1664                                         SAFE_FREE(dir);
1665                                 }
1666                                 errno = smbc_errno(context, &srv->cli);
1667                                 return NULL;
1668
1669                         }
1670                 }
1671
1672         }
1673
1674         DLIST_ADD(context->_files, dir);
1675         return dir;
1676
1677 }
1678
1679 /*
1680  * Routine to close a directory
1681  */
1682
1683 static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
1684 {
1685
1686         if (!context || !context->_initialized) {
1687
1688                 errno = EINVAL;
1689                 return -1;
1690
1691         }
1692
1693         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1694
1695                 errno = EBADF;
1696                 return -1;
1697     
1698         }
1699
1700         smbc_remove_dir(dir); /* Clean it up */
1701
1702         DLIST_REMOVE(context->_files, dir);
1703
1704         if (dir) {
1705
1706                 SAFE_FREE(dir->fname);
1707                 SAFE_FREE(dir);    /* Free the space too */
1708
1709         }
1710
1711         return 0;
1712
1713 }
1714
1715 /*
1716  * Routine to get a directory entry
1717  */
1718
1719 struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
1720 {
1721         struct smbc_dirent *dirp, *dirent;
1722
1723         /* Check that all is ok first ... */
1724
1725         if (!context || !context->_initialized) {
1726
1727                 errno = EINVAL;
1728                 return NULL;
1729
1730         }
1731
1732         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1733
1734                 errno = EBADF;
1735                 return NULL;
1736
1737         }
1738
1739         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1740
1741                 errno = ENOTDIR;
1742                 return NULL;
1743
1744         }
1745
1746         if (!dir->dir_next)
1747                 return NULL;
1748         else {
1749
1750                 dirent = dir->dir_next->dirent;
1751
1752                 if (!dirent) {
1753
1754                         errno = ENOENT;
1755                         return NULL;
1756
1757                 }
1758
1759                 /* Hmmm, do I even need to copy it? */
1760
1761                 memcpy(context->_dirent, dirent, dirent->dirlen); /* Copy the dirent */
1762                 dirp = (struct smbc_dirent *)context->_dirent;
1763                 dirp->comment = (char *)(&dirp->name + dirent->namelen + 1);
1764                 dir->dir_next = dir->dir_next->next;
1765
1766                 return (struct smbc_dirent *)context->_dirent;
1767         }
1768
1769 }
1770
1771 /*
1772  * Routine to get directory entries
1773  */
1774
1775 static int smbc_getdents_ctx(SMBCCTX *context, SMBCFILE *dir, struct smbc_dirent *dirp, int count)
1776 {
1777         struct smbc_dir_list *dirlist;
1778         int rem = count, reqd;
1779         char *ndir = (char *)dirp;
1780
1781         /* Check that all is ok first ... */
1782
1783         if (!context || !context->_initialized) {
1784
1785                 errno = EINVAL;
1786                 return -1;
1787
1788         }
1789
1790         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
1791
1792                 errno = EBADF;
1793                 return -1;
1794     
1795         }
1796
1797         if (dir->file != False) { /* FIXME, should be dir, perhaps */
1798
1799                 errno = ENOTDIR;
1800                 return -1;
1801
1802         }
1803
1804         /* 
1805          * Now, retrieve the number of entries that will fit in what was passed
1806          * We have to figure out if the info is in the list, or we need to 
1807          * send a request to the server to get the info.
1808          */
1809
1810         while ((dirlist = dir->dir_next)) {
1811                 struct smbc_dirent *dirent;
1812
1813                 if (!dirlist->dirent) {
1814
1815                         errno = ENOENT;  /* Bad error */
1816                         return -1;
1817
1818                 }
1819
1820                 if (rem < (reqd = (sizeof(struct smbc_dirent) + dirlist->dirent->namelen + 
1821                                    dirlist->dirent->commentlen + 1))) {
1822
1823                         if (rem < count) { /* We managed to copy something */
1824
1825                                 errno = 0;
1826                                 return count - rem;
1827
1828                         }
1829                         else { /* Nothing copied ... */
1830
1831                                 errno = EINVAL;  /* Not enough space ... */
1832                                 return -1;
1833
1834                         }
1835
1836                 }
1837
1838                 dirent = dirlist->dirent;
1839
1840                 memcpy(ndir, dirent, reqd); /* Copy the data in ... */
1841     
1842                 ((struct smbc_dirent *)ndir)->comment = 
1843                         (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1);
1844
1845                 ndir += reqd;
1846
1847                 rem -= reqd;
1848
1849                 dir->dir_next = dirlist = dirlist -> next;
1850         }
1851
1852         if (rem == count)
1853                 return 0;
1854         else 
1855                 return count - rem;
1856
1857 }
1858
1859 /*
1860  * Routine to create a directory ...
1861  */
1862
1863 static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
1864 {
1865         SMBCSRV *srv;
1866         fstring server, share, user, password, workgroup;
1867         pstring path;
1868
1869         if (!context || !context->_initialized) {
1870
1871                 errno = EINVAL;
1872                 return -1;
1873
1874         }
1875
1876         if (!fname) {
1877
1878                 errno = EINVAL;
1879                 return -1;
1880
1881         }
1882   
1883         DEBUG(4, ("smbc_mkdir(%s)\n", fname));
1884
1885         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1886
1887         if (user[0] == (char)0) pstrcpy(user, context->user);
1888
1889         pstrcpy(workgroup, context->workgroup);
1890
1891         srv = smbc_server(context, server, share, workgroup, user, password);
1892
1893         if (!srv) {
1894
1895                 return -1;  /* errno set by smbc_server */
1896
1897         }
1898
1899         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1900
1901            mode = aDIR | aRONLY;
1902
1903            }
1904            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1905
1906            if (strcmp(path, "\\") == 0) {
1907
1908            mode = aDIR | aRONLY;
1909
1910            }
1911            else {
1912
1913            mode = aRONLY;
1914            smbc_stat_printjob(srv, path, &size, &m_time);
1915            c_time = a_time = m_time;
1916
1917            }
1918            else { */
1919
1920         if (!cli_mkdir(&srv->cli, path)) {
1921
1922                 errno = smbc_errno(context, &srv->cli);
1923                 return -1;
1924
1925         } 
1926
1927         return 0;
1928
1929 }
1930
1931 /*
1932  * Our list function simply checks to see if a directory is not empty
1933  */
1934
1935 static int smbc_rmdir_dirempty = True;
1936
1937 static void rmdir_list_fn(file_info *finfo, const char *mask, void *state)
1938 {
1939
1940         if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0)
1941                 smbc_rmdir_dirempty = False;
1942
1943 }
1944
1945 /*
1946  * Routine to remove a directory
1947  */
1948
1949 static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
1950 {
1951         SMBCSRV *srv;
1952         fstring server, share, user, password, workgroup;
1953         pstring path;
1954
1955         if (!context || !context->_initialized) {
1956
1957                 errno = EINVAL;
1958                 return -1;
1959
1960         }
1961
1962         if (!fname) {
1963
1964                 errno = EINVAL;
1965                 return -1;
1966
1967         }
1968   
1969         DEBUG(4, ("smbc_rmdir(%s)\n", fname));
1970
1971         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
1972
1973         if (user[0] == (char)0) pstrcpy(user, context->user);
1974
1975         pstrcpy(workgroup, context->workgroup);
1976
1977         srv = smbc_server(context, server, share, workgroup, user, password);
1978
1979         if (!srv) {
1980
1981                 return -1;  /* errno set by smbc_server */
1982
1983         }
1984
1985         /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
1986
1987            mode = aDIR | aRONLY;
1988
1989            }
1990            else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
1991
1992            if (strcmp(path, "\\") == 0) {
1993
1994            mode = aDIR | aRONLY;
1995
1996            }
1997            else {
1998
1999            mode = aRONLY;
2000            smbc_stat_printjob(srv, path, &size, &m_time);
2001            c_time = a_time = m_time;
2002            
2003            }
2004            else { */
2005
2006         if (!cli_rmdir(&srv->cli, path)) {
2007
2008                 errno = smbc_errno(context, &srv->cli);
2009
2010                 if (errno == EACCES) {  /* Check if the dir empty or not */
2011
2012                         pstring lpath; /* Local storage to avoid buffer overflows */
2013
2014                         smbc_rmdir_dirempty = True;  /* Make this so ... */
2015
2016                         pstrcpy(lpath, path);
2017                         pstrcat(lpath, "\\*");
2018
2019                         if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn,
2020                                      NULL) < 0) {
2021
2022                                 /* Fix errno to ignore latest error ... */
2023
2024                                 DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n", 
2025                                           smbc_errno(context, &srv->cli)));
2026                                 errno = EACCES;
2027
2028                         }
2029
2030                         if (smbc_rmdir_dirempty)
2031                                 errno = EACCES;
2032                         else
2033                                 errno = ENOTEMPTY;
2034
2035                 }
2036
2037                 return -1;
2038
2039         } 
2040
2041         return 0;
2042
2043 }
2044
2045 /*
2046  * Routine to return the current directory position
2047  */
2048
2049 static off_t smbc_telldir_ctx(SMBCCTX *context, SMBCFILE *dir)
2050 {
2051
2052         if (!context || !context->_initialized) {
2053
2054                 errno = EINVAL;
2055                 return -1;
2056
2057         }
2058
2059         if (!dir || !DLIST_CONTAINS(context->_files, dir)) {
2060
2061                 errno = EBADF;
2062                 return -1;
2063
2064         }
2065
2066         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2067
2068                 errno = ENOTDIR;
2069                 return -1;
2070
2071         }
2072
2073         return (off_t) dir->dir_next;
2074
2075 }
2076
2077 /*
2078  * A routine to run down the list and see if the entry is OK
2079  */
2080
2081 struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, 
2082                                          struct smbc_dirent *dirent)
2083 {
2084
2085         /* Run down the list looking for what we want */
2086
2087         if (dirent) {
2088
2089                 struct smbc_dir_list *tmp = list;
2090
2091                 while (tmp) {
2092
2093                         if (tmp->dirent == dirent)
2094                                 return tmp;
2095
2096                         tmp = tmp->next;
2097
2098                 }
2099
2100         }
2101
2102         return NULL;  /* Not found, or an error */
2103
2104 }
2105
2106
2107 /*
2108  * Routine to seek on a directory
2109  */
2110
2111 static int smbc_lseekdir_ctx(SMBCCTX *context, SMBCFILE *dir, off_t offset)
2112 {
2113         struct smbc_dirent *dirent = (struct smbc_dirent *)offset;
2114         struct smbc_dir_list *list_ent = NULL;
2115
2116         if (!context || !context->_initialized) {
2117
2118                 errno = EINVAL;
2119                 return -1;
2120
2121         }
2122
2123         if (dir->file != False) { /* FIXME, should be dir, perhaps */
2124
2125                 errno = ENOTDIR;
2126                 return -1;
2127
2128         }
2129
2130         /* Now, check what we were passed and see if it is OK ... */
2131
2132         if (dirent == NULL) {  /* Seek to the begining of the list */
2133
2134                 dir->dir_next = dir->dir_list;
2135                 return 0;
2136
2137         }
2138
2139         /* Now, run down the list and make sure that the entry is OK       */
2140         /* This may need to be changed if we change the format of the list */
2141
2142         if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) {
2143
2144                 errno = EINVAL;   /* Bad entry */
2145                 return -1;
2146
2147         }
2148
2149         dir->dir_next = list_ent;
2150
2151         return 0; 
2152
2153 }
2154
2155 /*
2156  * Routine to fstat a dir
2157  */
2158
2159 static int smbc_fstatdir_ctx(SMBCCTX *context, SMBCFILE *dir, struct stat *st)
2160 {
2161
2162         if (context || !context->_initialized) {
2163
2164                 errno = EINVAL;
2165                 return -1;
2166
2167         }
2168
2169         /* No code yet ... */
2170
2171         return 0;
2172
2173 }
2174
2175 /*
2176  * Open a print file to be written to by other calls
2177  */
2178
2179 static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
2180 {
2181         fstring server, share, user, password;
2182         pstring path;
2183         
2184         if (!context || context->_initialized) {
2185
2186                 errno = EINVAL;
2187                 return NULL;
2188     
2189         }
2190
2191         if (!fname) {
2192
2193                 errno = EINVAL;
2194                 return NULL;
2195
2196         }
2197   
2198         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
2199
2200         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2201
2202         /* What if the path is empty, or the file exists? */
2203
2204         return context->open(context, fname, O_WRONLY, 666);
2205
2206 }
2207
2208 /*
2209  * Routine to print a file on a remote server ...
2210  *
2211  * We open the file, which we assume to be on a remote server, and then
2212  * copy it to a print file on the share specified by printq.
2213  */
2214
2215 static int smbc_print_file_ctx(SMBCCTX *c_file, const char *fname, SMBCCTX *c_print, const char *printq)
2216 {
2217         SMBCFILE *fid1, *fid2;
2218         int bytes, saverr, tot_bytes = 0;
2219         char buf[4096];
2220
2221         if (!c_file || !c_file->_initialized || !c_print ||
2222             !c_print->_initialized) {
2223
2224                 errno = EINVAL;
2225                 return -1;
2226
2227         }
2228
2229         if (!fname && !printq) {
2230
2231                 errno = EINVAL;
2232                 return -1;
2233
2234         }
2235
2236         /* Try to open the file for reading ... */
2237
2238         if ((fid1 = c_file->open(c_file, fname, O_RDONLY, 0666)) < 0) {
2239                 
2240                 DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
2241                 return -1;  /* smbc_open sets errno */
2242                 
2243         }
2244
2245         /* Now, try to open the printer file for writing */
2246
2247         if ((fid2 = c_print->open_print_job(c_print, printq)) < 0) {
2248
2249                 saverr = errno;  /* Save errno */
2250                 c_file->close(c_file, fid1);
2251                 errno = saverr;
2252                 return -1;
2253
2254         }
2255
2256         while ((bytes = c_file->read(c_file, fid1, buf, sizeof(buf))) > 0) {
2257
2258                 tot_bytes += bytes;
2259
2260                 if ((c_print->write(c_print, fid2, buf, bytes)) < 0) {
2261
2262                         saverr = errno;
2263                         c_file->close(c_file, fid1);
2264                         c_print->close(c_print, fid2);
2265                         errno = saverr;
2266
2267                 }
2268
2269         }
2270
2271         saverr = errno;
2272
2273         c_file->close(c_file, fid1);  /* We have to close these anyway */
2274         c_print->close(c_print, fid2);
2275
2276         if (bytes < 0) {
2277
2278                 errno = saverr;
2279                 return -1;
2280
2281         }
2282
2283         return tot_bytes;
2284
2285 }
2286
2287 /*
2288  * Routine to list print jobs on a printer share ...
2289  */
2290
2291 static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, void (*fn)(struct print_job_info *))
2292 {
2293         SMBCSRV *srv;
2294         fstring server, share, user, password, workgroup;
2295         pstring path;
2296
2297         if (!context || !context->_initialized) {
2298
2299                 errno = EINVAL;
2300                 return -1;
2301
2302         }
2303
2304         if (!fname) {
2305                 
2306                 errno = EINVAL;
2307                 return -1;
2308
2309         }
2310   
2311         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
2312
2313         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2314
2315         if (user[0] == (char)0) pstrcpy(user, context->user);
2316         
2317         pstrcpy(workgroup, context->workgroup);
2318
2319         srv = smbc_server(context, server, share, workgroup, user, password);
2320
2321         if (!srv) {
2322
2323                 return -1;  /* errno set by smbc_server */
2324
2325         }
2326
2327         if (cli_print_queue(&srv->cli, fn) < 0) {
2328
2329                 errno = smbc_errno(context, &srv->cli);
2330                 return -1;
2331
2332         }
2333         
2334         return 0;
2335
2336 }
2337
2338 /*
2339  * Delete a print job from a remote printer share
2340  */
2341
2342 static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id)
2343 {
2344         SMBCSRV *srv;
2345         fstring server, share, user, password, workgroup;
2346         pstring path;
2347         int err;
2348
2349         if (!context || !context->_initialized) {
2350
2351                 errno = EINVAL;
2352                 return -1;
2353
2354         }
2355
2356         if (!fname) {
2357
2358                 errno = EINVAL;
2359                 return -1;
2360
2361         }
2362   
2363         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
2364
2365         smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
2366
2367         if (user[0] == (char)0) pstrcpy(user, context->user);
2368
2369         pstrcpy(workgroup, context->workgroup);
2370
2371         srv = smbc_server(context, server, share, workgroup, user, password);
2372
2373         if (!srv) {
2374
2375                 return -1;  /* errno set by smbc_server */
2376
2377         }
2378
2379         if ((err = cli_printjob_del(&srv->cli, id)) != 0) {
2380
2381                 if (err < 0)
2382                         errno = smbc_errno(context, &srv->cli);
2383                 else if (err == ERRnosuchprintjob)
2384                         errno = EINVAL;
2385                 return -1;
2386
2387         }
2388
2389         return 0;
2390
2391 }
2392
2393 /*
2394  * Get a new empty handle to fill in with your own info 
2395  */
2396 SMBCCTX * smbc_new_context(void)
2397 {
2398         SMBCCTX * context;
2399
2400         context = malloc(sizeof(*context));
2401         if (!context) {
2402                 errno = ENOMEM;
2403                 return NULL;
2404         }
2405         
2406         ZERO_STRUCTP(context);
2407
2408         /* ADD REASONABLE DEFAULTS */
2409         context->debug            = 0;
2410         context->timeout          = 20000; /* 20 seconds */
2411
2412         context->open             = smbc_open_ctx;
2413         context->creat            = smbc_creat_ctx;
2414         context->read             = smbc_read_ctx;
2415         context->write            = smbc_write_ctx;
2416         context->close            = smbc_close_ctx;
2417         context->unlink           = smbc_unlink_ctx;
2418         context->rename           = smbc_rename_ctx;
2419         context->lseek            = smbc_lseek_ctx;
2420         context->stat             = smbc_stat_ctx;
2421         context->fstat            = smbc_fstat_ctx;
2422         context->opendir          = smbc_opendir_ctx;
2423         context->closedir         = smbc_closedir_ctx;
2424         context->readdir          = smbc_readdir_ctx;
2425         context->getdents         = smbc_getdents_ctx;
2426         context->mkdir            = smbc_mkdir_ctx;
2427         context->rmdir            = smbc_rmdir_ctx;
2428         context->telldir          = smbc_telldir_ctx;
2429         context->lseekdir         = smbc_lseekdir_ctx;
2430         context->fstatdir         = smbc_fstatdir_ctx;
2431         context->open_print_job   = smbc_open_print_job_ctx;
2432         context->print_file       = smbc_print_file_ctx;
2433         context->list_print_jobs  = smbc_list_print_jobs_ctx;
2434         context->unlink_print_job = smbc_unlink_print_job_ctx;
2435
2436         context->callbacks.check_server_fn      = smbc_check_server;
2437         context->callbacks.remove_unused_server_fn = smbc_remove_unused_server;
2438
2439         smbc_default_cache_functions(context);
2440
2441         return context;
2442 }
2443
2444 /* 
2445  * Free a context
2446  *
2447  * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed 
2448  * and thus you'll be leaking memory if not handled properly.
2449  *
2450  */
2451 int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
2452 {
2453         if (!context) {
2454                 errno = EBADF;
2455                 return 1;
2456         }
2457         
2458         if (shutdown_ctx) {
2459                 SMBCFILE * f;
2460                 DEBUG(1,("Performing aggressive shutdown.\n"));
2461                 
2462                 f = context->_files;
2463                 while (f) {
2464                         context->close(context, f);
2465                         f = f->next;
2466                 }
2467                 context->_files = NULL;
2468
2469                 /* First try to remove the servers the nice way. */
2470                 if (context->callbacks.purge_cached_fn(context)) {
2471                         SMBCSRV * s;
2472                         DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
2473                         s = context->_servers;
2474                         while (s) {
2475                                 cli_shutdown(&s->cli);
2476                                 context->callbacks.remove_cached_srv_fn(context, s);
2477                                 SAFE_FREE(s);
2478                                 s = s->next;
2479                         }
2480                         context->_servers = NULL;
2481                 }
2482         }
2483         else {
2484                 /* This is the polite way */    
2485                 if (context->callbacks.purge_cached_fn(context)) {
2486                         DEBUG(1, ("Could not purge all servers, free_context failed.\n"));
2487                         errno = EBUSY;
2488                         return 1;
2489                 }
2490                 if (context->_servers) {
2491                         DEBUG(1, ("Active servers in context, free_context failed.\n"));
2492                         errno = EBUSY;
2493                         return 1;
2494                 }
2495                 if (context->_files) {
2496                         DEBUG(1, ("Active files in context, free_context failed.\n"));
2497                         errno = EBUSY;
2498                         return 1;
2499                 }               
2500         }
2501
2502         /* Things we have to clean up */
2503         SAFE_FREE(context->workgroup);
2504         SAFE_FREE(context->netbios_name);
2505         SAFE_FREE(context->user);
2506         
2507         DEBUG(3, ("Context %p succesfully freed\n", context));
2508         SAFE_FREE(context);
2509         return 0;
2510 }
2511
2512
2513 /*
2514  * Initialise the library etc 
2515  *
2516  * We accept a struct containing handle information.
2517  * valid values for info->debug from 0 to 100,
2518  * and insist that info->fn must be non-null.
2519  */
2520 SMBCCTX * smbc_init_context(SMBCCTX * context)
2521 {
2522         pstring conf;
2523         int pid;
2524         char *user = NULL, *home = NULL;
2525
2526         if (!context) {
2527                 errno = EBADF;
2528                 return NULL;
2529         }
2530
2531         /* Do not initialise the same client twice */
2532         if (context->_initialized) { 
2533                 return 0;
2534         }
2535
2536         if (!context->callbacks.auth_fn || context->debug < 0 || context->debug > 100) {
2537
2538                 errno = EINVAL;
2539                 return NULL;
2540
2541         }
2542
2543         if (!smbc_initialized) {
2544                 /* Do some library wide intialisations the first time we get called */
2545
2546                 /* Do we still need this ? */
2547                 DEBUGLEVEL = 10;
2548                 
2549                 setup_logging( "libsmbclient", False);
2550
2551                 /* Here we would open the smb.conf file if needed ... */
2552                 
2553                 home = getenv("HOME");
2554
2555                 slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home);
2556                 
2557                 load_interfaces();  /* Load the list of interfaces ... */
2558                 
2559                 in_client = True; /* FIXME, make a param */
2560
2561                 if (!lp_load(conf, True, False, False)) {
2562
2563                         /*
2564                          * Hmmm, what the hell do we do here ... we could not parse the
2565                          * config file ... We must return an error ... and keep info around
2566                          * about why we failed
2567                          */
2568                         
2569                         errno = ENOENT; /* FIXME: Figure out the correct error response */
2570                         return NULL;
2571                 }
2572
2573                 reopen_logs();  /* Get logging working ... */
2574         
2575                 /* 
2576                  * Block SIGPIPE (from lib/util_sock.c: write())  
2577                  * It is not needed and should not stop execution 
2578                  */
2579                 BlockSignals(True, SIGPIPE);
2580                 
2581                 /* Done with one-time initialisation */
2582                 smbc_initialized = 1; 
2583
2584         }
2585         
2586         if (!context->user) {
2587                 /*
2588                  * FIXME: Is this the best way to get the user info? 
2589                  */
2590                 user = getenv("USER");
2591                 /* walk around as "guest" if no username can be found */
2592                 if (!user) context->user = strdup("guest");
2593                 else context->user = strdup(user);
2594         }
2595
2596         if (!context->netbios_name) {
2597                 /*
2598                  * We try to get our netbios name from the config. If that fails we fall
2599                  * back on constructing our netbios name from our hostname etc
2600                  */
2601                 if (global_myname) {
2602                         context->netbios_name = strdup(global_myname);
2603                 }
2604                 else {
2605                         /*
2606                          * Hmmm, I want to get hostname as well, but I am too lazy for the moment
2607                          */
2608                         pid = sys_getpid();
2609                         context->netbios_name = malloc(17);
2610                         if (!context->netbios_name) {
2611                                 errno = ENOMEM;
2612                                 return NULL;
2613                         }
2614                         slprintf(context->netbios_name, 16, "smbc%s%d", context->user, pid);
2615                 }
2616         }
2617         DEBUG(0,("Using netbios name %s.\n", context->netbios_name));
2618         
2619
2620         if (!context->workgroup) {
2621                 if (lp_workgroup()) {
2622                         context->workgroup = strdup(lp_workgroup());
2623                 }
2624                 else {
2625                         /* TODO: Think about a decent default workgroup */
2626                         context->workgroup = strdup("samba");
2627                 }
2628         }
2629         DEBUG(0,("Using workgroup %s.\n", context->workgroup));
2630                                         
2631         /* shortest timeout is 1 second */
2632         if (context->timeout > 0 && context->timeout < 1000) 
2633                 context->timeout = 1000;
2634
2635         /*
2636          * FIXME: Should we check the function pointers here? 
2637          */
2638
2639         context->_initialized = 1;
2640         
2641         return context;
2642 }