r14860: create libcli/security/security.h
[ira/wip.git] / source4 / client / client.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB client
4    Copyright (C) Andrew Tridgell 1994-1998
5    Copyright (C) Simo Sorce 2001-2002
6    Copyright (C) Jelmer Vernooij 2003-2004
7    Copyright (C) James J Myers   2003 <myersjj@samba.org>
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 #include "version.h"
26 #include "libcli/libcli.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "librpc/gen_ndr/ndr_srvsvc_c.h"
29 #include "librpc/gen_ndr/ndr_lsa.h"
30 #include "libcli/raw/libcliraw.h"
31 #include "libcli/util/clilsa.h"
32 #include "system/dir.h"
33 #include "system/filesys.h"
34 #include "dlinklist.h"
35 #include "system/readline.h"
36 #include "auth/gensec/gensec.h"
37 #include "system/time.h" /* needed by some systems for asctime() */
38 #include "libcli/resolve/resolve.h"
39 #include "libcli/security/security.h"
40 #include "lib/replace/readline.h"
41 #include "librpc/gen_ndr/ndr_nbt.h"
42 #include "librpc/gen_ndr/ndr_security.h"
43
44 static int io_bufsize = 64512;
45
46 struct smbclient_context {
47         char *remote_cur_dir;
48         struct smbcli_state *cli;
49         char *fileselection;
50         time_t newer_than;
51         BOOL prompt;
52         BOOL recurse;
53         int archive_level;
54         BOOL lowercase;
55         int printmode;
56         BOOL translation;
57 };
58
59 /* timing globals */
60 static uint64_t get_total_size = 0;
61 static uint_t get_total_time_ms = 0;
62 static uint64_t put_total_size = 0;
63 static uint_t put_total_time_ms = 0;
64
65 /* Unfortunately, there is no way to pass the a context to the completion function as an argument */
66 static struct smbclient_context *rl_ctx; 
67
68 /* totals globals */
69 static double dir_total;
70
71 /*******************************************************************
72  Reduce a file name, removing .. elements.
73 ********************************************************************/
74 void dos_clean_name(char *s)
75 {
76         char *p=NULL,*r;
77
78         DEBUG(3,("dos_clean_name [%s]\n",s));
79
80         /* remove any double slashes */
81         all_string_sub(s, "\\\\", "\\", 0);
82
83         while ((p = strstr(s,"\\..\\")) != NULL) {
84                 *p = '\0';
85                 if ((r = strrchr(s,'\\')) != NULL)
86                         memmove(r,p+3,strlen(p+3)+1);
87         }
88
89         trim_string(s,NULL,"\\..");
90
91         all_string_sub(s, "\\.\\", "\\", 0);
92 }
93
94 /****************************************************************************
95 write to a local file with CR/LF->LF translation if appropriate. return the 
96 number taken from the buffer. This may not equal the number written.
97 ****************************************************************************/
98 static int writefile(int f, const void *_b, int n, BOOL translation)
99 {
100         const uint8_t *b = _b;
101         int i;
102
103         if (!translation) {
104                 return write(f,b,n);
105         }
106
107         i = 0;
108         while (i < n) {
109                 if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') {
110                         b++;i++;
111                 }
112                 if (write(f, b, 1) != 1) {
113                         break;
114                 }
115                 b++;
116                 i++;
117         }
118   
119         return(i);
120 }
121
122 /****************************************************************************
123   read from a file with LF->CR/LF translation if appropriate. return the 
124   number read. read approx n bytes.
125 ****************************************************************************/
126 static int readfile(void *_b, int n, XFILE *f, BOOL translation)
127 {
128         uint8_t *b = _b;
129         int i;
130         int c;
131
132         if (!translation)
133                 return x_fread(b,1,n,f);
134   
135         i = 0;
136         while (i < (n - 1)) {
137                 if ((c = x_getc(f)) == EOF) {
138                         break;
139                 }
140       
141                 if (c == '\n') { /* change all LFs to CR/LF */
142                         b[i++] = '\r';
143                 }
144       
145                 b[i++] = c;
146         }
147   
148         return(i);
149 }
150  
151
152 /****************************************************************************
153 send a message
154 ****************************************************************************/
155 static void send_message(struct smbcli_state *cli, const char *desthost)
156 {
157         char msg[1600];
158         int total_len = 0;
159         int grp_id;
160
161         if (!smbcli_message_start(cli->tree, desthost, cli_credentials_get_username(cmdline_credentials), &grp_id)) {
162                 d_printf("message start: %s\n", smbcli_errstr(cli->tree));
163                 return;
164         }
165
166
167         d_printf("Connected. Type your message, ending it with a Control-D\n");
168
169         while (!feof(stdin) && total_len < 1600) {
170                 int maxlen = MIN(1600 - total_len,127);
171                 int l=0;
172                 int c;
173
174                 for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
175                         if (c == '\n')
176                                 msg[l++] = '\r';
177                         msg[l] = c;   
178                 }
179
180                 if (!smbcli_message_text(cli->tree, msg, l, grp_id)) {
181                         d_printf("SMBsendtxt failed (%s)\n",smbcli_errstr(cli->tree));
182                         return;
183                 }      
184                 
185                 total_len += l;
186         }
187
188         if (total_len >= 1600)
189                 d_printf("the message was truncated to 1600 bytes\n");
190         else
191                 d_printf("sent %d bytes\n",total_len);
192
193         if (!smbcli_message_end(cli->tree, grp_id)) {
194                 d_printf("SMBsendend failed (%s)\n",smbcli_errstr(cli->tree));
195                 return;
196         }      
197 }
198
199
200
201 /****************************************************************************
202 check the space on a device
203 ****************************************************************************/
204 static int do_dskattr(struct smbclient_context *ctx)
205 {
206         int total, bsize, avail;
207
208         if (NT_STATUS_IS_ERR(smbcli_dskattr(ctx->cli->tree, &bsize, &total, &avail))) {
209                 d_printf("Error in dskattr: %s\n",smbcli_errstr(ctx->cli->tree)); 
210                 return 1;
211         }
212
213         d_printf("\n\t\t%d blocks of size %d. %d blocks available\n",
214                  total, bsize, avail);
215
216         return 0;
217 }
218
219 /****************************************************************************
220 show cd/pwd
221 ****************************************************************************/
222 static int cmd_pwd(struct smbclient_context *ctx, const char **args)
223 {
224         d_printf("Current directory is %s\n", ctx->remote_cur_dir);
225         return 0;
226 }
227
228 /*
229   convert a string to dos format
230 */
231 static void dos_format(char *s)
232 {
233         string_replace(s, '/', '\\');
234 }
235
236 /****************************************************************************
237 change directory - inner section
238 ****************************************************************************/
239 static int do_cd(struct smbclient_context *ctx, const char *newdir)
240 {
241         char *dname;
242       
243         /* Save the current directory in case the
244            new directory is invalid */
245         if (newdir[0] == '\\')
246                 dname = talloc_strdup(NULL, newdir);
247         else
248                 dname = talloc_asprintf(NULL, "%s\\%s", ctx->remote_cur_dir, newdir);
249
250         dos_format(dname);
251
252         if (*(dname+strlen(dname)-1) != '\\') {
253                 dname = talloc_append_string(NULL, dname, "\\");
254         }
255         dos_clean_name(dname);
256         
257         if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, dname))) {
258                 d_printf("cd %s: %s\n", dname, smbcli_errstr(ctx->cli->tree));
259                 talloc_free(dname);
260         } else {
261                 ctx->remote_cur_dir = dname;
262         }
263         
264         return 0;
265 }
266
267 /****************************************************************************
268 change directory
269 ****************************************************************************/
270 static int cmd_cd(struct smbclient_context *ctx, const char **args)
271 {
272         int rc = 0;
273
274         if (args[1]) 
275                 rc = do_cd(ctx, args[1]);
276         else
277                 d_printf("Current directory is %s\n",ctx->remote_cur_dir);
278
279         return rc;
280 }
281
282
283 BOOL mask_match(struct smbcli_state *c, const char *string, const char *pattern, 
284                 BOOL is_case_sensitive)
285 {
286         char *p2, *s2;
287         BOOL ret;
288
289         if (strcmp(string,"..") == 0)
290                 string = ".";
291         if (strcmp(pattern,".") == 0)
292                 return False;
293         
294         if (is_case_sensitive)
295                 return ms_fnmatch(pattern, string, 
296                                   c->transport->negotiate.protocol) == 0;
297
298         p2 = strlower_talloc(NULL, pattern);
299         s2 = strlower_talloc(NULL, string);
300         ret = ms_fnmatch(p2, s2, c->transport->negotiate.protocol) == 0;
301         talloc_free(p2);
302         talloc_free(s2);
303
304         return ret;
305 }
306
307
308
309 /*******************************************************************
310   decide if a file should be operated on
311   ********************************************************************/
312 static BOOL do_this_one(struct smbclient_context *ctx, struct clilist_file_info *finfo)
313 {
314         if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) return(True);
315
316         if (ctx->fileselection && 
317             !mask_match(ctx->cli, finfo->name,ctx->fileselection,False)) {
318                 DEBUG(3,("mask_match %s failed\n", finfo->name));
319                 return False;
320         }
321
322         if (ctx->newer_than && finfo->mtime < ctx->newer_than) {
323                 DEBUG(3,("newer_than %s failed\n", finfo->name));
324                 return(False);
325         }
326
327         if ((ctx->archive_level==1 || ctx->archive_level==2) && !(finfo->attrib & FILE_ATTRIBUTE_ARCHIVE)) {
328                 DEBUG(3,("archive %s failed\n", finfo->name));
329                 return(False);
330         }
331         
332         return(True);
333 }
334
335 /****************************************************************************
336   display info about a file
337   ****************************************************************************/
338 static void display_finfo(struct smbclient_context *ctx, struct clilist_file_info *finfo)
339 {
340         if (do_this_one(ctx, finfo)) {
341                 time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
342                 char *astr = attrib_string(NULL, finfo->attrib);
343                 d_printf("  %-30s%7.7s %8.0f  %s",
344                          finfo->name,
345                          astr,
346                          (double)finfo->size,
347                          asctime(localtime(&t)));
348                 dir_total += finfo->size;
349                 talloc_free(astr);
350         }
351 }
352
353
354 /****************************************************************************
355    accumulate size of a file
356   ****************************************************************************/
357 static void do_du(struct smbclient_context *ctx, struct clilist_file_info *finfo)
358 {
359         if (do_this_one(ctx, finfo)) {
360                 dir_total += finfo->size;
361         }
362 }
363
364 static BOOL do_list_recurse;
365 static BOOL do_list_dirs;
366 static char *do_list_queue = 0;
367 static long do_list_queue_size = 0;
368 static long do_list_queue_start = 0;
369 static long do_list_queue_end = 0;
370 static void (*do_list_fn)(struct smbclient_context *, struct clilist_file_info *);
371
372 /****************************************************************************
373 functions for do_list_queue
374   ****************************************************************************/
375
376 /*
377  * The do_list_queue is a NUL-separated list of strings stored in a
378  * char*.  Since this is a FIFO, we keep track of the beginning and
379  * ending locations of the data in the queue.  When we overflow, we
380  * double the size of the char*.  When the start of the data passes
381  * the midpoint, we move everything back.  This is logically more
382  * complex than a linked list, but easier from a memory management
383  * angle.  In any memory error condition, do_list_queue is reset.
384  * Functions check to ensure that do_list_queue is non-NULL before
385  * accessing it.
386  */
387 static void reset_do_list_queue(void)
388 {
389         SAFE_FREE(do_list_queue);
390         do_list_queue_size = 0;
391         do_list_queue_start = 0;
392         do_list_queue_end = 0;
393 }
394
395 static void init_do_list_queue(void)
396 {
397         reset_do_list_queue();
398         do_list_queue_size = 1024;
399         do_list_queue = malloc(do_list_queue_size);
400         if (do_list_queue == 0) { 
401                 d_printf("malloc fail for size %d\n",
402                          (int)do_list_queue_size);
403                 reset_do_list_queue();
404         } else {
405                 memset(do_list_queue, 0, do_list_queue_size);
406         }
407 }
408
409 static void adjust_do_list_queue(void)
410 {
411         /*
412          * If the starting point of the queue is more than half way through,
413          * move everything toward the beginning.
414          */
415         if (do_list_queue && (do_list_queue_start == do_list_queue_end))
416         {
417                 DEBUG(4,("do_list_queue is empty\n"));
418                 do_list_queue_start = do_list_queue_end = 0;
419                 *do_list_queue = '\0';
420         }
421         else if (do_list_queue_start > (do_list_queue_size / 2))
422         {
423                 DEBUG(4,("sliding do_list_queue backward\n"));
424                 memmove(do_list_queue,
425                         do_list_queue + do_list_queue_start,
426                         do_list_queue_end - do_list_queue_start);
427                 do_list_queue_end -= do_list_queue_start;
428                 do_list_queue_start = 0;
429         }
430            
431 }
432
433 static void add_to_do_list_queue(const char* entry)
434 {
435         char *dlq;
436         long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
437         while (new_end > do_list_queue_size)
438         {
439                 do_list_queue_size *= 2;
440                 DEBUG(4,("enlarging do_list_queue to %d\n",
441                          (int)do_list_queue_size));
442                 dlq = realloc_p(do_list_queue, char, do_list_queue_size);
443                 if (! dlq) {
444                         d_printf("failure enlarging do_list_queue to %d bytes\n",
445                                  (int)do_list_queue_size);
446                         reset_do_list_queue();
447                 }
448                 else
449                 {
450                         do_list_queue = dlq;
451                         memset(do_list_queue + do_list_queue_size / 2,
452                                0, do_list_queue_size / 2);
453                 }
454         }
455         if (do_list_queue)
456         {
457                 safe_strcpy(do_list_queue + do_list_queue_end, entry, 
458                             do_list_queue_size - do_list_queue_end - 1);
459                 do_list_queue_end = new_end;
460                 DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n",
461                          entry, (int)do_list_queue_start, (int)do_list_queue_end));
462         }
463 }
464
465 static char *do_list_queue_head(void)
466 {
467         return do_list_queue + do_list_queue_start;
468 }
469
470 static void remove_do_list_queue_head(void)
471 {
472         if (do_list_queue_end > do_list_queue_start)
473         {
474                 do_list_queue_start += strlen(do_list_queue_head()) + 1;
475                 adjust_do_list_queue();
476                 DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n",
477                          (int)do_list_queue_start, (int)do_list_queue_end));
478         }
479 }
480
481 static int do_list_queue_empty(void)
482 {
483         return (! (do_list_queue && *do_list_queue));
484 }
485
486 /****************************************************************************
487 a helper for do_list
488   ****************************************************************************/
489 static void do_list_helper(struct clilist_file_info *f, const char *mask, void *state)
490 {
491         struct smbclient_context *ctx = state;
492
493         if (f->attrib & FILE_ATTRIBUTE_DIRECTORY) {
494                 if (do_list_dirs && do_this_one(ctx, f)) {
495                         do_list_fn(ctx, f);
496                 }
497                 if (do_list_recurse && 
498                     !strequal(f->name,".") && 
499                     !strequal(f->name,"..")) {
500                         char *mask2;
501                         char *p;
502
503                         mask2 = talloc_strdup(NULL, mask);
504                         p = strrchr_m(mask2,'\\');
505                         if (!p) return;
506                         p[1] = 0;
507                         mask2 = talloc_asprintf_append(mask2, "%s\\*", f->name);
508                         add_to_do_list_queue(mask2);
509                 }
510                 return;
511         }
512
513         if (do_this_one(ctx, f)) {
514                 do_list_fn(ctx, f);
515         }
516 }
517
518
519 /****************************************************************************
520 a wrapper around smbcli_list that adds recursion
521   ****************************************************************************/
522 static void do_list(struct smbclient_context *ctx, const char *mask,uint16_t attribute,
523              void (*fn)(struct smbclient_context *, struct clilist_file_info *),BOOL rec, BOOL dirs)
524 {
525         static int in_do_list = 0;
526
527         if (in_do_list && rec)
528         {
529                 fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
530                 exit(1);
531         }
532
533         in_do_list = 1;
534
535         do_list_recurse = rec;
536         do_list_dirs = dirs;
537         do_list_fn = fn;
538
539         if (rec)
540         {
541                 init_do_list_queue();
542                 add_to_do_list_queue(mask);
543                 
544                 while (! do_list_queue_empty())
545                 {
546                         /*
547                          * Need to copy head so that it doesn't become
548                          * invalid inside the call to smbcli_list.  This
549                          * would happen if the list were expanded
550                          * during the call.
551                          * Fix from E. Jay Berkenbilt (ejb@ql.org)
552                          */
553                         char *head;
554                         head = do_list_queue_head();
555                         smbcli_list(ctx->cli->tree, head, attribute, do_list_helper, ctx);
556                         remove_do_list_queue_head();
557                         if ((! do_list_queue_empty()) && (fn == display_finfo))
558                         {
559                                 char* next_file = do_list_queue_head();
560                                 char* save_ch = 0;
561                                 if ((strlen(next_file) >= 2) &&
562                                     (next_file[strlen(next_file) - 1] == '*') &&
563                                     (next_file[strlen(next_file) - 2] == '\\'))
564                                 {
565                                         save_ch = next_file +
566                                                 strlen(next_file) - 2;
567                                         *save_ch = '\0';
568                                 }
569                                 d_printf("\n%s\n",next_file);
570                                 if (save_ch)
571                                 {
572                                         *save_ch = '\\';
573                                 }
574                         }
575                 }
576         }
577         else
578         {
579                 if (smbcli_list(ctx->cli->tree, mask, attribute, do_list_helper, ctx) == -1)
580                 {
581                         d_printf("%s listing %s\n", smbcli_errstr(ctx->cli->tree), mask);
582                 }
583         }
584
585         in_do_list = 0;
586         reset_do_list_queue();
587 }
588
589 /****************************************************************************
590   get a directory listing
591   ****************************************************************************/
592 static int cmd_dir(struct smbclient_context *ctx, const char **args)
593 {
594         uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
595         char *mask;
596         int rc;
597         
598         dir_total = 0;
599         
600         mask = talloc_strdup(ctx, ctx->remote_cur_dir);
601         if(mask[strlen(mask)-1]!='\\')
602                 mask = talloc_append_string(ctx, mask,"\\");
603         
604         if (args[1]) {
605                 mask = talloc_strdup(ctx, args[1]);
606                 if (mask[0] != '\\')
607                         mask = talloc_append_string(ctx, mask, "\\");
608                 dos_format(mask);
609         }
610         else {
611                 if (ctx->cli->tree->session->transport->negotiate.protocol <= 
612                     PROTOCOL_LANMAN1) { 
613                         mask = talloc_append_string(ctx, mask, "*.*");
614                 } else {
615                         mask = talloc_append_string(ctx, mask, "*");
616                 }
617         }
618
619         do_list(ctx, mask, attribute, display_finfo, ctx->recurse, True);
620
621         rc = do_dskattr(ctx);
622
623         DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
624
625         return rc;
626 }
627
628
629 /****************************************************************************
630   get a directory listing
631   ****************************************************************************/
632 static int cmd_du(struct smbclient_context *ctx, const char **args)
633 {
634         uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
635         int rc;
636         char *mask;
637         
638         dir_total = 0;
639         
640         if (args[1]) {
641                 if (args[1][0] == '\\')
642                         mask = talloc_strdup(ctx, args[1]);
643                 else
644                         mask = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
645                 dos_format(mask);
646         } else {
647                 mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
648         }
649
650         do_list(ctx, mask, attribute, do_du, ctx->recurse, True);
651
652         talloc_free(mask);
653
654         rc = do_dskattr(ctx);
655
656         d_printf("Total number of bytes: %.0f\n", dir_total);
657
658         return rc;
659 }
660
661
662 /****************************************************************************
663   get a file from rname to lname
664   ****************************************************************************/
665 static int do_get(struct smbclient_context *ctx, char *rname, const char *lname, BOOL reget)
666 {  
667         int handle = 0, fnum;
668         BOOL newhandle = False;
669         uint8_t *data;
670         struct timeval tp_start;
671         int read_size = io_bufsize;
672         uint16_t attr;
673         size_t size;
674         off_t start = 0;
675         off_t nread = 0;
676         int rc = 0;
677
678         GetTimeOfDay(&tp_start);
679
680         if (ctx->lowercase) {
681                 strlower(discard_const_p(char, lname));
682         }
683
684         fnum = smbcli_open(ctx->cli->tree, rname, O_RDONLY, DENY_NONE);
685
686         if (fnum == -1) {
687                 d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
688                 return 1;
689         }
690
691         if(!strcmp(lname,"-")) {
692                 handle = fileno(stdout);
693         } else {
694                 if (reget) {
695                         handle = open(lname, O_WRONLY|O_CREAT, 0644);
696                         if (handle >= 0) {
697                                 start = lseek(handle, 0, SEEK_END);
698                                 if (start == -1) {
699                                         d_printf("Error seeking local file\n");
700                                         return 1;
701                                 }
702                         }
703                 } else {
704                         handle = open(lname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
705                 }
706                 newhandle = True;
707         }
708         if (handle < 0) {
709                 d_printf("Error opening local file %s\n",lname);
710                 return 1;
711         }
712
713
714         if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum, 
715                            &attr, &size, NULL, NULL, NULL, NULL, NULL)) &&
716             NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum, 
717                           &attr, &size, NULL, NULL, NULL))) {
718                 d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
719                 return 1;
720         }
721
722         DEBUG(2,("getting file %s of size %.0f as %s ", 
723                  rname, (double)size, lname));
724
725         if(!(data = (uint8_t *)malloc(read_size))) { 
726                 d_printf("malloc fail for size %d\n", read_size);
727                 smbcli_close(ctx->cli->tree, fnum);
728                 return 1;
729         }
730
731         while (1) {
732                 int n = smbcli_read(ctx->cli->tree, fnum, data, nread + start, read_size);
733
734                 if (n <= 0) break;
735  
736                 if (writefile(handle,data, n, ctx->translation) != n) {
737                         d_printf("Error writing local file\n");
738                         rc = 1;
739                         break;
740                 }
741       
742                 nread += n;
743         }
744
745         if (nread + start < size) {
746                 DEBUG (0, ("Short read when getting file %s. Only got %ld bytes.\n",
747                             rname, (long)nread));
748
749                 rc = 1;
750         }
751
752         SAFE_FREE(data);
753         
754         if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
755                 d_printf("Error %s closing remote file\n",smbcli_errstr(ctx->cli->tree));
756                 rc = 1;
757         }
758
759         if (newhandle) {
760                 close(handle);
761         }
762
763         if (ctx->archive_level >= 2 && (attr & FILE_ATTRIBUTE_ARCHIVE)) {
764                 smbcli_setatr(ctx->cli->tree, rname, attr & ~(uint16_t)FILE_ATTRIBUTE_ARCHIVE, 0);
765         }
766
767         {
768                 struct timeval tp_end;
769                 int this_time;
770                 
771                 GetTimeOfDay(&tp_end);
772                 this_time = 
773                         (tp_end.tv_sec - tp_start.tv_sec)*1000 +
774                         (tp_end.tv_usec - tp_start.tv_usec)/1000;
775                 get_total_time_ms += this_time;
776                 get_total_size += nread;
777                 
778                 DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
779                          nread / (1.024*this_time + 1.0e-4),
780                          get_total_size / (1.024*get_total_time_ms)));
781         }
782         
783         return rc;
784 }
785
786
787 /****************************************************************************
788   get a file
789   ****************************************************************************/
790 static int cmd_get(struct smbclient_context *ctx, const char **args)
791 {
792         const char *lname;
793         char *rname;
794
795         if (!args[1]) {
796                 d_printf("get <filename>\n");
797                 return 1;
798         }
799
800         rname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
801
802         if (args[2]) 
803                 lname = args[2];
804         else 
805                 lname = args[1];
806         
807         dos_clean_name(rname);
808         
809         return do_get(ctx, rname, lname, False);
810 }
811
812 /****************************************************************************
813  Put up a yes/no prompt.
814 ****************************************************************************/
815 static BOOL yesno(char *p)
816 {
817         char ans[4];
818         printf("%s",p);
819
820         if (!fgets(ans,sizeof(ans)-1,stdin))
821                 return(False);
822
823         if (*ans == 'y' || *ans == 'Y')
824                 return(True);
825
826         return(False);
827 }
828
829 /****************************************************************************
830   do a mget operation on one file
831   ****************************************************************************/
832 static void do_mget(struct smbclient_context *ctx, struct clilist_file_info *finfo)
833 {
834         char *rname;
835         char *quest;
836         char *mget_mask;
837         char *saved_curdir;
838
839         if (strequal(finfo->name,".") || strequal(finfo->name,".."))
840                 return;
841
842         if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)
843                 asprintf(&quest, "Get directory %s? ",finfo->name);
844         else
845                 asprintf(&quest, "Get file %s? ",finfo->name);
846
847         if (ctx->prompt && !yesno(quest)) return;
848
849         SAFE_FREE(quest);
850
851         if (!(finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)) {
852                 asprintf(&rname, "%s%s",ctx->remote_cur_dir,finfo->name);
853                 do_get(ctx, rname, finfo->name, False);
854                 SAFE_FREE(rname);
855                 return;
856         }
857
858         /* handle directories */
859         saved_curdir = talloc_strdup(NULL, ctx->remote_cur_dir);
860
861         ctx->remote_cur_dir = talloc_asprintf_append(NULL, "%s\\", finfo->name);
862
863         string_replace(discard_const_p(char, finfo->name), '\\', '/');
864         if (ctx->lowercase) {
865                 strlower(discard_const_p(char, finfo->name));
866         }
867         
868         if (!directory_exist(finfo->name) && 
869             mkdir(finfo->name,0777) != 0) {
870                 d_printf("failed to create directory %s\n",finfo->name);
871                 return;
872         }
873         
874         if (chdir(finfo->name) != 0) {
875                 d_printf("failed to chdir to directory %s\n",finfo->name);
876                 return;
877         }
878
879         mget_mask = talloc_asprintf(NULL, "%s*", ctx->remote_cur_dir);
880         
881         do_list(ctx, mget_mask, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY,do_mget,False, True);
882         chdir("..");
883         talloc_free(ctx->remote_cur_dir);
884
885         ctx->remote_cur_dir = saved_curdir;
886 }
887
888
889 /****************************************************************************
890 view the file using the pager
891 ****************************************************************************/
892 static int cmd_more(struct smbclient_context *ctx, const char **args)
893 {
894         char *rname;
895         char *pager_cmd;
896         char *lname;
897         char *pager;
898         int fd;
899         int rc = 0;
900
901         lname = talloc_asprintf(ctx, "%s/smbmore.XXXXXX",tmpdir());
902         fd = mkstemp(lname);
903         if (fd == -1) {
904                 d_printf("failed to create temporary file for more\n");
905                 return 1;
906         }
907         close(fd);
908
909         if (!args[1]) {
910                 d_printf("more <filename>\n");
911                 unlink(lname);
912                 return 1;
913         }
914         rname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
915         dos_clean_name(rname);
916
917         rc = do_get(ctx, rname, lname, False);
918
919         pager=getenv("PAGER");
920
921         pager_cmd = talloc_asprintf(ctx, "%s %s",(pager? pager:PAGER), lname);
922         system(pager_cmd);
923         unlink(lname);
924         
925         return rc;
926 }
927
928
929
930 /****************************************************************************
931 do a mget command
932 ****************************************************************************/
933 static int cmd_mget(struct smbclient_context *ctx, const char **args)
934 {
935         uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
936         char *mget_mask;
937         int i;
938
939         if (ctx->recurse)
940                 attribute |= FILE_ATTRIBUTE_DIRECTORY;
941         
942         for (i = 1; args[i]; i++) {
943                 mget_mask = talloc_strdup(ctx,ctx->remote_cur_dir);
944                 if(mget_mask[strlen(mget_mask)-1]!='\\')
945                         mget_mask = talloc_append_string(ctx, mget_mask, "\\");
946                 
947                 mget_mask = talloc_strdup(ctx, args[i]);
948                 if (mget_mask[0] != '\\')
949                         mget_mask = talloc_append_string(ctx, mget_mask, "\\");
950                 do_list(ctx, mget_mask, attribute,do_mget,False,True);
951         }
952
953         if (!*mget_mask) {
954                 mget_mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
955                 do_list(ctx, mget_mask, attribute,do_mget,False,True);
956         }
957
958         talloc_free(mget_mask);
959         
960         return 0;
961 }
962
963
964 /****************************************************************************
965 make a directory of name "name"
966 ****************************************************************************/
967 static NTSTATUS do_mkdir(struct smbclient_context *ctx, char *name)
968 {
969         NTSTATUS status;
970
971         if (NT_STATUS_IS_ERR(status = smbcli_mkdir(ctx->cli->tree, name))) {
972                 d_printf("%s making remote directory %s\n",
973                          smbcli_errstr(ctx->cli->tree),name);
974                 return status;
975         }
976
977         return status;
978 }
979
980
981 /****************************************************************************
982  Exit client.
983 ****************************************************************************/
984 static int cmd_quit(struct smbclient_context *ctx, const char **args)
985 {
986         talloc_free(ctx);
987         exit(0);
988         /* NOTREACHED */
989         return 0;
990 }
991
992
993 /****************************************************************************
994   make a directory
995   ****************************************************************************/
996 static int cmd_mkdir(struct smbclient_context *ctx, const char **args)
997 {
998         char *mask, *p;
999   
1000         if (!args[1]) {
1001                 if (!ctx->recurse)
1002                         d_printf("mkdir <dirname>\n");
1003                 return 1;
1004         }
1005
1006         mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir,args[1]);
1007
1008         if (ctx->recurse) {
1009                 dos_clean_name(mask);
1010
1011                 trim_string(mask,".",NULL);
1012                 for (p = strtok(mask,"/\\"); p; p = strtok(p, "/\\")) {
1013                         char *parent = talloc_strndup(ctx, mask, PTR_DIFF(p, mask));
1014                         
1015                         if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, parent))) { 
1016                                 do_mkdir(ctx, parent);
1017                         }
1018
1019                         talloc_free(parent);
1020                 }        
1021         } else {
1022                 do_mkdir(ctx, mask);
1023         }
1024         
1025         return 0;
1026 }
1027
1028 /****************************************************************************
1029 show 8.3 name of a file
1030 ****************************************************************************/
1031 static int cmd_altname(struct smbclient_context *ctx, const char **args)
1032 {
1033         const char *altname;
1034         char *name;
1035   
1036         if (!args[1]) {
1037                 d_printf("altname <file>\n");
1038                 return 1;
1039         }
1040
1041         name = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
1042
1043         if (!NT_STATUS_IS_OK(smbcli_qpathinfo_alt_name(ctx->cli->tree, name, &altname))) {
1044                 d_printf("%s getting alt name for %s\n",
1045                          smbcli_errstr(ctx->cli->tree),name);
1046                 return(False);
1047         }
1048         d_printf("%s\n", altname);
1049
1050         return 0;
1051 }
1052
1053
1054 /****************************************************************************
1055   put a single file
1056   ****************************************************************************/
1057 static int do_put(struct smbclient_context *ctx, char *rname, char *lname, BOOL reput)
1058 {
1059         int fnum;
1060         XFILE *f;
1061         size_t start = 0;
1062         off_t nread = 0;
1063         uint8_t *buf = NULL;
1064         int maxwrite = io_bufsize;
1065         int rc = 0;
1066         
1067         struct timeval tp_start;
1068         GetTimeOfDay(&tp_start);
1069
1070         if (reput) {
1071                 fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT, DENY_NONE);
1072                 if (fnum >= 0) {
1073                         if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL)) &&
1074                             NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL))) {
1075                                 d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
1076                                 return 1;
1077                         }
1078                 }
1079         } else {
1080                 fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT|O_TRUNC, 
1081                                 DENY_NONE);
1082         }
1083   
1084         if (fnum == -1) {
1085                 d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
1086                 return 1;
1087         }
1088
1089         /* allow files to be piped into smbclient
1090            jdblair 24.jun.98
1091
1092            Note that in this case this function will exit(0) rather
1093            than returning. */
1094         if (!strcmp(lname, "-")) {
1095                 f = x_stdin;
1096                 /* size of file is not known */
1097         } else {
1098                 f = x_fopen(lname,O_RDONLY, 0);
1099                 if (f && reput) {
1100                         if (x_tseek(f, start, SEEK_SET) == -1) {
1101                                 d_printf("Error seeking local file\n");
1102                                 return 1;
1103                         }
1104                 }
1105         }
1106
1107         if (!f) {
1108                 d_printf("Error opening local file %s\n",lname);
1109                 return 1;
1110         }
1111
1112   
1113         DEBUG(1,("putting file %s as %s ",lname,
1114                  rname));
1115   
1116         buf = (uint8_t *)malloc(maxwrite);
1117         if (!buf) {
1118                 d_printf("ERROR: Not enough memory!\n");
1119                 return 1;
1120         }
1121         while (!x_feof(f)) {
1122                 int n = maxwrite;
1123                 int ret;
1124
1125                 if ((n = readfile(buf,n,f,ctx->translation)) < 1) {
1126                         if((n == 0) && x_feof(f))
1127                                 break; /* Empty local file. */
1128
1129                         d_printf("Error reading local file: %s\n", strerror(errno));
1130                         rc = 1;
1131                         break;
1132                 }
1133
1134                 ret = smbcli_write(ctx->cli->tree, fnum, 0, buf, nread + start, n);
1135
1136                 if (n != ret) {
1137                         d_printf("Error writing file: %s\n", smbcli_errstr(ctx->cli->tree));
1138                         rc = 1;
1139                         break;
1140                 } 
1141
1142                 nread += n;
1143         }
1144
1145         if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
1146                 d_printf("%s closing remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
1147                 x_fclose(f);
1148                 SAFE_FREE(buf);
1149                 return 1;
1150         }
1151
1152         
1153         if (f != x_stdin) {
1154                 x_fclose(f);
1155         }
1156
1157         SAFE_FREE(buf);
1158
1159         {
1160                 struct timeval tp_end;
1161                 int this_time;
1162                 
1163                 GetTimeOfDay(&tp_end);
1164                 this_time = 
1165                         (tp_end.tv_sec - tp_start.tv_sec)*1000 +
1166                         (tp_end.tv_usec - tp_start.tv_usec)/1000;
1167                 put_total_time_ms += this_time;
1168                 put_total_size += nread;
1169                 
1170                 DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
1171                          nread / (1.024*this_time + 1.0e-4),
1172                          put_total_size / (1.024*put_total_time_ms)));
1173         }
1174
1175         if (f == x_stdin) {
1176                 talloc_free(ctx);
1177                 exit(0);
1178         }
1179         
1180         return rc;
1181 }
1182
1183  
1184
1185 /****************************************************************************
1186   put a file
1187   ****************************************************************************/
1188 static int cmd_put(struct smbclient_context *ctx, const char **args)
1189 {
1190         char *lname;
1191         char *rname;
1192         
1193         if (!args[1]) {
1194                 d_printf("put <filename>\n");
1195                 return 1;
1196         }
1197
1198         lname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
1199   
1200         if (args[2])
1201                 rname = talloc_strdup(ctx, args[2]);
1202         else
1203                 rname = talloc_strdup(ctx, lname);
1204         
1205         dos_clean_name(rname);
1206
1207         /* allow '-' to represent stdin
1208            jdblair, 24.jun.98 */
1209         if (!file_exist(lname) && (strcmp(lname,"-"))) {
1210                 d_printf("%s does not exist\n",lname);
1211                 return 1;
1212         }
1213
1214         return do_put(ctx, rname, lname, False);
1215 }
1216
1217 /*************************************
1218   File list structure
1219 *************************************/
1220
1221 static struct file_list {
1222         struct file_list *prev, *next;
1223         char *file_path;
1224         BOOL isdir;
1225 } *file_list;
1226
1227 /****************************************************************************
1228   Free a file_list structure
1229 ****************************************************************************/
1230
1231 static void free_file_list (struct file_list * list)
1232 {
1233         struct file_list *tmp;
1234         
1235         while (list)
1236         {
1237                 tmp = list;
1238                 DLIST_REMOVE(list, list);
1239                 SAFE_FREE(tmp->file_path);
1240                 SAFE_FREE(tmp);
1241         }
1242 }
1243
1244 /****************************************************************************
1245   seek in a directory/file list until you get something that doesn't start with
1246   the specified name
1247   ****************************************************************************/
1248 static BOOL seek_list(struct file_list *list, char *name)
1249 {
1250         while (list) {
1251                 trim_string(list->file_path,"./","\n");
1252                 if (strncmp(list->file_path, name, strlen(name)) != 0) {
1253                         return(True);
1254                 }
1255                 list = list->next;
1256         }
1257       
1258         return(False);
1259 }
1260
1261 /****************************************************************************
1262   set the file selection mask
1263   ****************************************************************************/
1264 static int cmd_select(struct smbclient_context *ctx, const char **args)
1265 {
1266         talloc_free(ctx->fileselection);
1267         ctx->fileselection = talloc_strdup(NULL, args[1]);
1268
1269         return 0;
1270 }
1271
1272 /*******************************************************************
1273   A readdir wrapper which just returns the file name.
1274  ********************************************************************/
1275 static const char *readdirname(DIR *p)
1276 {
1277         struct dirent *ptr;
1278         char *dname;
1279
1280         if (!p)
1281                 return(NULL);
1282   
1283         ptr = (struct dirent *)readdir(p);
1284         if (!ptr)
1285                 return(NULL);
1286
1287         dname = ptr->d_name;
1288
1289 #ifdef NEXT2
1290         if (telldir(p) < 0)
1291                 return(NULL);
1292 #endif
1293
1294 #ifdef HAVE_BROKEN_READDIR
1295         /* using /usr/ucb/cc is BAD */
1296         dname = dname - 2;
1297 #endif
1298
1299         {
1300                 static char *buf;
1301                 int len = NAMLEN(ptr);
1302                 buf = talloc_strndup(NULL, dname, len);
1303                 dname = buf;
1304         }
1305
1306         return(dname);
1307 }
1308
1309 /****************************************************************************
1310   Recursive file matching function act as find
1311   match must be always set to True when calling this function
1312 ****************************************************************************/
1313 static int file_find(struct smbclient_context *ctx, struct file_list **list, const char *directory, 
1314                       const char *expression, BOOL match)
1315 {
1316         DIR *dir;
1317         struct file_list *entry;
1318         struct stat statbuf;
1319         int ret;
1320         char *path;
1321         BOOL isdir;
1322         const char *dname;
1323
1324         dir = opendir(directory);
1325         if (!dir) return -1;
1326         
1327         while ((dname = readdirname(dir))) {
1328                 if (!strcmp("..", dname)) continue;
1329                 if (!strcmp(".", dname)) continue;
1330                 
1331                 if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
1332                         continue;
1333                 }
1334
1335                 isdir = False;
1336                 if (!match || !gen_fnmatch(expression, dname)) {
1337                         if (ctx->recurse) {
1338                                 ret = stat(path, &statbuf);
1339                                 if (ret == 0) {
1340                                         if (S_ISDIR(statbuf.st_mode)) {
1341                                                 isdir = True;
1342                                                 ret = file_find(ctx, list, path, expression, False);
1343                                         }
1344                                 } else {
1345                                         d_printf("file_find: cannot stat file %s\n", path);
1346                                 }
1347                                 
1348                                 if (ret == -1) {
1349                                         SAFE_FREE(path);
1350                                         closedir(dir);
1351                                         return -1;
1352                                 }
1353                         }
1354                         entry = malloc_p(struct file_list);
1355                         if (!entry) {
1356                                 d_printf("Out of memory in file_find\n");
1357                                 closedir(dir);
1358                                 return -1;
1359                         }
1360                         entry->file_path = path;
1361                         entry->isdir = isdir;
1362                         DLIST_ADD(*list, entry);
1363                 } else {
1364                         SAFE_FREE(path);
1365                 }
1366         }
1367
1368         closedir(dir);
1369         return 0;
1370 }
1371
1372 /****************************************************************************
1373   mput some files
1374   ****************************************************************************/
1375 static int cmd_mput(struct smbclient_context *ctx, const char **args)
1376 {
1377         int i;
1378         
1379         for (i = 1; args[i]; i++) {
1380                 int ret;
1381                 struct file_list *temp_list;
1382                 char *quest, *lname, *rname;
1383
1384                 printf("%s\n", args[i]);
1385         
1386                 file_list = NULL;
1387
1388                 ret = file_find(ctx, &file_list, ".", args[i], True);
1389                 if (ret) {
1390                         free_file_list(file_list);
1391                         continue;
1392                 }
1393                 
1394                 quest = NULL;
1395                 lname = NULL;
1396                 rname = NULL;
1397                                 
1398                 for (temp_list = file_list; temp_list; 
1399                      temp_list = temp_list->next) {
1400
1401                         SAFE_FREE(lname);
1402                         if (asprintf(&lname, "%s/", temp_list->file_path) <= 0)
1403                                 continue;
1404                         trim_string(lname, "./", "/");
1405                         
1406                         /* check if it's a directory */
1407                         if (temp_list->isdir) {
1408                                 /* if (!recurse) continue; */
1409                                 
1410                                 SAFE_FREE(quest);
1411                                 if (asprintf(&quest, "Put directory %s? ", lname) < 0) break;
1412                                 if (ctx->prompt && !yesno(quest)) { /* No */
1413                                         /* Skip the directory */
1414                                         lname[strlen(lname)-1] = '/';
1415                                         if (!seek_list(temp_list, lname))
1416                                                 break;              
1417                                 } else { /* Yes */
1418                                         SAFE_FREE(rname);
1419                                         if(asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
1420                                         dos_format(rname);
1421                                         if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, rname)) && 
1422                                             NT_STATUS_IS_ERR(do_mkdir(ctx, rname))) {
1423                                                 DEBUG (0, ("Unable to make dir, skipping..."));
1424                                                 /* Skip the directory */
1425                                                 lname[strlen(lname)-1] = '/';
1426                                                 if (!seek_list(temp_list, lname))
1427                                                         break;
1428                                         }
1429                                 }
1430                                 continue;
1431                         } else {
1432                                 SAFE_FREE(quest);
1433                                 if (asprintf(&quest,"Put file %s? ", lname) < 0) break;
1434                                 if (ctx->prompt && !yesno(quest)) /* No */
1435                                         continue;
1436                                 
1437                                 /* Yes */
1438                                 SAFE_FREE(rname);
1439                                 if (asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
1440                         }
1441
1442                         dos_format(rname);
1443
1444                         do_put(ctx, rname, lname, False);
1445                 }
1446                 free_file_list(file_list);
1447                 SAFE_FREE(quest);
1448                 SAFE_FREE(lname);
1449                 SAFE_FREE(rname);
1450         }
1451
1452         return 0;
1453 }
1454
1455
1456 /****************************************************************************
1457   print a file
1458   ****************************************************************************/
1459 static int cmd_print(struct smbclient_context *ctx, const char **args)
1460 {
1461         char *lname, *rname;
1462         char *p;
1463
1464         if (!args[1]) {
1465                 d_printf("print <filename>\n");
1466                 return 1;
1467         }
1468
1469         lname = talloc_strdup(ctx, args[1]);
1470
1471         rname = talloc_strdup(ctx, lname);
1472         p = strrchr_m(rname,'/');
1473         if (p) {
1474                 slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)getpid());
1475         }
1476
1477         if (strequal(lname,"-")) {
1478                 slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)getpid());
1479         }
1480
1481         return do_put(ctx, rname, lname, False);
1482 }
1483
1484
1485 static int cmd_rewrite(struct smbclient_context *ctx, const char **args)
1486 {
1487         d_printf("REWRITE: command not implemented (FIXME!)\n");
1488         
1489         return 0;
1490 }
1491
1492 /****************************************************************************
1493 delete some files
1494 ****************************************************************************/
1495 static int cmd_del(struct smbclient_context *ctx, const char **args)
1496 {
1497         char *mask;
1498         uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
1499
1500         if (ctx->recurse)
1501                 attribute |= FILE_ATTRIBUTE_DIRECTORY;
1502         
1503         if (!args[1]) {
1504                 d_printf("del <filename>\n");
1505                 return 1;
1506         }
1507         mask = talloc_asprintf(ctx,"%s%s", ctx->remote_cur_dir, args[1]);
1508
1509         if (NT_STATUS_IS_ERR(smbcli_unlink(ctx->cli->tree, mask))) {
1510                 d_printf("%s deleting remote file %s\n",smbcli_errstr(ctx->cli->tree),mask);
1511         }
1512         
1513         return 0;
1514 }
1515
1516
1517 /****************************************************************************
1518 delete a whole directory tree
1519 ****************************************************************************/
1520 static int cmd_deltree(struct smbclient_context *ctx, const char **args)
1521 {
1522         char *dname;
1523         int ret;
1524
1525         if (!args[1]) {
1526                 d_printf("deltree <dirname>\n");
1527                 return 1;
1528         }
1529
1530         dname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
1531         
1532         ret = smbcli_deltree(ctx->cli->tree, dname);
1533
1534         if (ret == -1) {
1535                 printf("Failed to delete tree %s - %s\n", dname, smbcli_errstr(ctx->cli->tree));
1536                 return -1;
1537         }
1538
1539         printf("Deleted %d files in %s\n", ret, dname);
1540         
1541         return 0;
1542 }
1543
1544 typedef struct {
1545         const char  *level_name;
1546         enum smb_fsinfo_level level;
1547 } fsinfo_level_t;
1548
1549 fsinfo_level_t fsinfo_levels[] = {
1550         {"dskattr", RAW_QFS_DSKATTR},
1551         {"allocation", RAW_QFS_ALLOCATION},
1552         {"volume", RAW_QFS_VOLUME},
1553         {"volumeinfo", RAW_QFS_VOLUME_INFO},
1554         {"sizeinfo", RAW_QFS_SIZE_INFO},
1555         {"deviceinfo", RAW_QFS_DEVICE_INFO},
1556         {"attributeinfo", RAW_QFS_ATTRIBUTE_INFO},
1557         {"unixinfo", RAW_QFS_UNIX_INFO},
1558         {"volume-information", RAW_QFS_VOLUME_INFORMATION},
1559         {"size-information", RAW_QFS_SIZE_INFORMATION},
1560         {"device-information", RAW_QFS_DEVICE_INFORMATION},
1561         {"attribute-information", RAW_QFS_ATTRIBUTE_INFORMATION},
1562         {"quota-information", RAW_QFS_QUOTA_INFORMATION},
1563         {"fullsize-information", RAW_QFS_FULL_SIZE_INFORMATION},
1564         {"objectid", RAW_QFS_OBJECTID_INFORMATION},
1565         {NULL, RAW_QFS_GENERIC}
1566 };
1567
1568
1569 static int cmd_fsinfo(struct smbclient_context *ctx, const char **args)
1570 {
1571         union smb_fsinfo fsinfo;
1572         NTSTATUS status;
1573         fsinfo_level_t *fsinfo_level;
1574         
1575         if (!args[1]) {
1576                 d_printf("fsinfo <level>, where level is one of following:\n");
1577                 fsinfo_level = fsinfo_levels;
1578                 while(fsinfo_level->level_name) {
1579                         d_printf("%s\n", fsinfo_level->level_name);
1580                         fsinfo_level++;
1581                 }
1582                 return 1;
1583         }
1584         
1585         fsinfo_level = fsinfo_levels;
1586         while(fsinfo_level->level_name && !strequal(args[1],fsinfo_level->level_name)) {
1587                 fsinfo_level++;
1588         }
1589   
1590         if (!fsinfo_level->level_name) {
1591                 d_printf("wrong level name!\n");
1592                 return 1;
1593         }
1594   
1595         fsinfo.generic.level = fsinfo_level->level;
1596         status = smb_raw_fsinfo(ctx->cli->tree, ctx, &fsinfo);
1597         if (!NT_STATUS_IS_OK(status)) {
1598                 d_printf("fsinfo-level-%s - %s\n", fsinfo_level->level_name, nt_errstr(status));
1599                 return 1;
1600         }
1601
1602         d_printf("fsinfo-level-%s:\n", fsinfo_level->level_name);
1603         switch(fsinfo.generic.level) {
1604         case RAW_QFS_DSKATTR:
1605                 d_printf("\tunits_total:                %hu\n", 
1606                          (unsigned short) fsinfo.dskattr.out.units_total);
1607                 d_printf("\tblocks_per_unit:            %hu\n", 
1608                          (unsigned short) fsinfo.dskattr.out.blocks_per_unit);
1609                 d_printf("\tblocks_size:                %hu\n", 
1610                          (unsigned short) fsinfo.dskattr.out.block_size);
1611                 d_printf("\tunits_free:                 %hu\n", 
1612                          (unsigned short) fsinfo.dskattr.out.units_free);
1613                 break;
1614         case RAW_QFS_ALLOCATION:
1615                 d_printf("\tfs_id:                      %lu\n", 
1616                          (unsigned long) fsinfo.allocation.out.fs_id);
1617                 d_printf("\tsectors_per_unit:           %lu\n", 
1618                          (unsigned long) fsinfo.allocation.out.sectors_per_unit);
1619                 d_printf("\ttotal_alloc_units:          %lu\n", 
1620                          (unsigned long) fsinfo.allocation.out.total_alloc_units);
1621                 d_printf("\tavail_alloc_units:          %lu\n", 
1622                          (unsigned long) fsinfo.allocation.out.avail_alloc_units);
1623                 d_printf("\tbytes_per_sector:           %hu\n", 
1624                          (unsigned short) fsinfo.allocation.out.bytes_per_sector);
1625                 break;
1626         case RAW_QFS_VOLUME:
1627                 d_printf("\tserial_number:              %lu\n", 
1628                          (unsigned long) fsinfo.volume.out.serial_number);
1629                 d_printf("\tvolume_name:                %s\n", fsinfo.volume.out.volume_name.s);
1630                 break;
1631         case RAW_QFS_VOLUME_INFO:
1632         case RAW_QFS_VOLUME_INFORMATION:
1633                 d_printf("\tcreate_time:                %s\n",
1634                          nt_time_string(ctx,fsinfo.volume_info.out.create_time));
1635                 d_printf("\tserial_number:              %lu\n", 
1636                          (unsigned long) fsinfo.volume_info.out.serial_number);
1637                 d_printf("\tvolume_name:                %s\n", fsinfo.volume_info.out.volume_name.s);
1638                 break;
1639         case RAW_QFS_SIZE_INFO:
1640         case RAW_QFS_SIZE_INFORMATION:
1641                 d_printf("\ttotal_alloc_units:          %llu\n", 
1642                          (unsigned long long) fsinfo.size_info.out.total_alloc_units);
1643                 d_printf("\tavail_alloc_units:          %llu\n", 
1644                          (unsigned long long) fsinfo.size_info.out.avail_alloc_units);
1645                 d_printf("\tsectors_per_unit:           %lu\n", 
1646                          (unsigned long) fsinfo.size_info.out.sectors_per_unit);
1647                 d_printf("\tbytes_per_sector:           %lu\n", 
1648                          (unsigned long) fsinfo.size_info.out.bytes_per_sector);
1649                 break;
1650         case RAW_QFS_DEVICE_INFO:
1651         case RAW_QFS_DEVICE_INFORMATION:
1652                 d_printf("\tdevice_type:                %lu\n", 
1653                          (unsigned long) fsinfo.device_info.out.device_type);
1654                 d_printf("\tcharacteristics:            0x%lx\n", 
1655                          (unsigned long) fsinfo.device_info.out.characteristics);
1656                 break;
1657         case RAW_QFS_ATTRIBUTE_INFORMATION:
1658         case RAW_QFS_ATTRIBUTE_INFO:
1659                 d_printf("\tfs_attr:                    0x%lx\n", 
1660                          (unsigned long) fsinfo.attribute_info.out.fs_attr);
1661                 d_printf("\tmax_file_component_length:  %lu\n", 
1662                          (unsigned long) fsinfo.attribute_info.out.max_file_component_length);
1663                 d_printf("\tfs_type:                    %s\n", fsinfo.attribute_info.out.fs_type.s);
1664                 break;
1665         case RAW_QFS_UNIX_INFO:
1666                 d_printf("\tmajor_version:              %hu\n", 
1667                          (unsigned short) fsinfo.unix_info.out.major_version);
1668                 d_printf("\tminor_version:              %hu\n", 
1669                          (unsigned short) fsinfo.unix_info.out.minor_version);
1670                 d_printf("\tcapability:                 0x%llx\n", 
1671                          (unsigned long long) fsinfo.unix_info.out.capability);
1672                 break;
1673         case RAW_QFS_QUOTA_INFORMATION:
1674                 d_printf("\tunknown[3]:                 [%llu,%llu,%llu]\n", 
1675                          (unsigned long long) fsinfo.quota_information.out.unknown[0],
1676                          (unsigned long long) fsinfo.quota_information.out.unknown[1],
1677                          (unsigned long long) fsinfo.quota_information.out.unknown[2]);
1678                 d_printf("\tquota_soft:                 %llu\n", 
1679                          (unsigned long long) fsinfo.quota_information.out.quota_soft);
1680                 d_printf("\tquota_hard:                 %llu\n", 
1681                          (unsigned long long) fsinfo.quota_information.out.quota_hard);
1682                 d_printf("\tquota_flags:                0x%llx\n", 
1683                          (unsigned long long) fsinfo.quota_information.out.quota_flags);
1684                 break;
1685         case RAW_QFS_FULL_SIZE_INFORMATION:
1686                 d_printf("\ttotal_alloc_units:          %llu\n", 
1687                          (unsigned long long) fsinfo.full_size_information.out.total_alloc_units);
1688                 d_printf("\tcall_avail_alloc_units:     %llu\n", 
1689                          (unsigned long long) fsinfo.full_size_information.out.call_avail_alloc_units);
1690                 d_printf("\tactual_avail_alloc_units:   %llu\n", 
1691                          (unsigned long long) fsinfo.full_size_information.out.actual_avail_alloc_units);
1692                 d_printf("\tsectors_per_unit:           %lu\n", 
1693                          (unsigned long) fsinfo.full_size_information.out.sectors_per_unit);
1694                 d_printf("\tbytes_per_sector:           %lu\n", 
1695                          (unsigned long) fsinfo.full_size_information.out.bytes_per_sector);
1696                 break;
1697         case RAW_QFS_OBJECTID_INFORMATION:
1698                 d_printf("\tGUID:                       %s\n", 
1699                          GUID_string(ctx,&fsinfo.objectid_information.out.guid));
1700                 d_printf("\tunknown[6]:                 [%llu,%llu,%llu,%llu,%llu,%llu]\n", 
1701                          (unsigned long long) fsinfo.objectid_information.out.unknown[0],
1702                          (unsigned long long) fsinfo.objectid_information.out.unknown[2],
1703                          (unsigned long long) fsinfo.objectid_information.out.unknown[3],
1704                          (unsigned long long) fsinfo.objectid_information.out.unknown[4],
1705                          (unsigned long long) fsinfo.objectid_information.out.unknown[5],
1706                          (unsigned long long) fsinfo.objectid_information.out.unknown[6] );
1707                 break;
1708         case RAW_QFS_GENERIC:
1709                 d_printf("\twrong level returned\n");
1710                 break;
1711         }
1712   
1713         return 0;
1714 }
1715
1716 /****************************************************************************
1717 show as much information as possible about a file
1718 ****************************************************************************/
1719 static int cmd_allinfo(struct smbclient_context *ctx, const char **args)
1720 {
1721         char *fname;
1722         union smb_fileinfo finfo;
1723         NTSTATUS status;
1724
1725         if (!args[1]) {
1726                 d_printf("allinfo <filename>\n");
1727                 return 1;
1728         }
1729         fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
1730
1731         /* first a ALL_INFO QPATHINFO */
1732         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
1733         finfo.generic.in.file.path = fname;
1734         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1735         if (!NT_STATUS_IS_OK(status)) {
1736                 d_printf("%s - %s\n", fname, nt_errstr(status));
1737                 return 1;
1738         }
1739
1740         d_printf("\tcreate_time:    %s\n", nt_time_string(ctx, finfo.all_info.out.create_time));
1741         d_printf("\taccess_time:    %s\n", nt_time_string(ctx, finfo.all_info.out.access_time));
1742         d_printf("\twrite_time:     %s\n", nt_time_string(ctx, finfo.all_info.out.write_time));
1743         d_printf("\tchange_time:    %s\n", nt_time_string(ctx, finfo.all_info.out.change_time));
1744         d_printf("\tattrib:         0x%x\n", finfo.all_info.out.attrib);
1745         d_printf("\talloc_size:     %lu\n", (unsigned long)finfo.all_info.out.alloc_size);
1746         d_printf("\tsize:           %lu\n", (unsigned long)finfo.all_info.out.size);
1747         d_printf("\tnlink:          %u\n", finfo.all_info.out.nlink);
1748         d_printf("\tdelete_pending: %u\n", finfo.all_info.out.delete_pending);
1749         d_printf("\tdirectory:      %u\n", finfo.all_info.out.directory);
1750         d_printf("\tea_size:        %u\n", finfo.all_info.out.ea_size);
1751         d_printf("\tfname:          '%s'\n", finfo.all_info.out.fname.s);
1752
1753         /* 8.3 name if any */
1754         finfo.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
1755         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1756         if (NT_STATUS_IS_OK(status)) {
1757                 d_printf("\talt_name:       %s\n", finfo.alt_name_info.out.fname.s);
1758         }
1759
1760         /* file_id if available */
1761         finfo.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
1762         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1763         if (NT_STATUS_IS_OK(status)) {
1764                 d_printf("\tfile_id         %.0f\n", 
1765                          (double)finfo.internal_information.out.file_id);
1766         }
1767
1768         /* the EAs, if any */
1769         finfo.generic.level = RAW_FILEINFO_ALL_EAS;
1770         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1771         if (NT_STATUS_IS_OK(status)) {
1772                 int i;
1773                 for (i=0;i<finfo.all_eas.out.num_eas;i++) {
1774                         d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
1775                                  finfo.all_eas.out.eas[i].flags,
1776                                  (int)finfo.all_eas.out.eas[i].value.length,
1777                                  finfo.all_eas.out.eas[i].name.s);
1778                 }
1779         }
1780
1781         /* streams, if available */
1782         finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
1783         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1784         if (NT_STATUS_IS_OK(status)) {
1785                 int i;
1786                 for (i=0;i<finfo.stream_info.out.num_streams;i++) {
1787                         d_printf("\tstream %d:\n", i);
1788                         d_printf("\t\tsize       %ld\n", 
1789                                  (long)finfo.stream_info.out.streams[i].size);
1790                         d_printf("\t\talloc size %ld\n", 
1791                                  (long)finfo.stream_info.out.streams[i].alloc_size);
1792                         d_printf("\t\tname       %s\n", finfo.stream_info.out.streams[i].stream_name.s);
1793                 }
1794         }       
1795
1796         /* dev/inode if available */
1797         finfo.generic.level = RAW_FILEINFO_COMPRESSION_INFORMATION;
1798         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1799         if (NT_STATUS_IS_OK(status)) {
1800                 d_printf("\tcompressed size %ld\n", (long)finfo.compression_info.out.compressed_size);
1801                 d_printf("\tformat          %ld\n", (long)finfo.compression_info.out.format);
1802                 d_printf("\tunit_shift      %ld\n", (long)finfo.compression_info.out.unit_shift);
1803                 d_printf("\tchunk_shift     %ld\n", (long)finfo.compression_info.out.chunk_shift);
1804                 d_printf("\tcluster_shift   %ld\n", (long)finfo.compression_info.out.cluster_shift);
1805         }
1806
1807         return 0;
1808 }
1809
1810
1811 /****************************************************************************
1812 shows EA contents
1813 ****************************************************************************/
1814 static int cmd_eainfo(struct smbclient_context *ctx, const char **args)
1815 {
1816         char *fname;
1817         union smb_fileinfo finfo;
1818         NTSTATUS status;
1819         int i;
1820
1821         if (!args[1]) {
1822                 d_printf("eainfo <filename>\n");
1823                 return 1;
1824         }
1825         fname = talloc_strdup(ctx, args[1]);
1826
1827         finfo.generic.level = RAW_FILEINFO_ALL_EAS;
1828         finfo.generic.in.file.path = fname;
1829         status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
1830         
1831         if (!NT_STATUS_IS_OK(status)) {
1832                 d_printf("RAW_FILEINFO_ALL_EAS - %s\n", nt_errstr(status));
1833                 return 1;
1834         }
1835
1836         d_printf("%s has %d EAs\n", fname, finfo.all_eas.out.num_eas);
1837
1838         for (i=0;i<finfo.all_eas.out.num_eas;i++) {
1839                 d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
1840                          finfo.all_eas.out.eas[i].flags,
1841                          (int)finfo.all_eas.out.eas[i].value.length,
1842                          finfo.all_eas.out.eas[i].name.s);
1843                 fflush(stdout);
1844                 dump_data(0, 
1845                           finfo.all_eas.out.eas[i].value.data,
1846                           finfo.all_eas.out.eas[i].value.length);
1847         }
1848
1849         return 0;
1850 }
1851
1852
1853 /****************************************************************************
1854 show any ACL on a file
1855 ****************************************************************************/
1856 static int cmd_acl(struct smbclient_context *ctx, const char **args)
1857 {
1858         char *fname;
1859         union smb_fileinfo query;
1860         NTSTATUS status;
1861         int fnum;
1862
1863         if (!args[1]) {
1864                 d_printf("acl <filename>\n");
1865                 return 1;
1866         }
1867         fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
1868
1869         fnum = smbcli_nt_create_full(ctx->cli->tree, fname, 0, 
1870                                      SEC_STD_READ_CONTROL,
1871                                      0,
1872                                      NTCREATEX_SHARE_ACCESS_DELETE|
1873                                      NTCREATEX_SHARE_ACCESS_READ|
1874                                      NTCREATEX_SHARE_ACCESS_WRITE, 
1875                                      NTCREATEX_DISP_OPEN,
1876                                      0, 0);
1877         if (fnum == -1) {
1878                 d_printf("%s - %s\n", fname, smbcli_errstr(ctx->cli->tree));
1879                 return -1;
1880         }
1881
1882         query.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
1883         query.query_secdesc.in.file.fnum = fnum;
1884         query.query_secdesc.in.secinfo_flags = 0x7;
1885
1886         status = smb_raw_fileinfo(ctx->cli->tree, ctx, &query);
1887         if (!NT_STATUS_IS_OK(status)) {
1888                 d_printf("%s - %s\n", fname, nt_errstr(status));
1889                 return 1;
1890         }
1891
1892         NDR_PRINT_DEBUG(security_descriptor, query.query_secdesc.out.sd);
1893
1894         return 0;
1895 }
1896
1897 /****************************************************************************
1898 lookup a name or sid
1899 ****************************************************************************/
1900 static int cmd_lookup(struct smbclient_context *ctx, const char **args)
1901 {
1902         NTSTATUS status;
1903         struct dom_sid *sid;
1904
1905         if (!args[1]) {
1906                 d_printf("lookup <sid|name>\n");
1907                 return 1;
1908         }
1909
1910         sid = dom_sid_parse_talloc(ctx, args[1]);
1911         if (sid == NULL) {
1912                 const char *sidstr;
1913                 status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sidstr);
1914                 if (!NT_STATUS_IS_OK(status)) {
1915                         d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
1916                         return 1;
1917                 }
1918
1919                 d_printf("%s\n", sidstr);
1920         } else {
1921                 const char *name;
1922                 status = smblsa_lookup_sid(ctx->cli, args[1], ctx, &name);
1923                 if (!NT_STATUS_IS_OK(status)) {
1924                         d_printf("lsa_LookupSids - %s\n", nt_errstr(status));
1925                         return 1;
1926                 }
1927
1928                 d_printf("%s\n", name);
1929         }
1930
1931         return 0;
1932 }
1933
1934 /****************************************************************************
1935 show privileges for a user
1936 ****************************************************************************/
1937 static int cmd_privileges(struct smbclient_context *ctx, const char **args)
1938 {
1939         NTSTATUS status;
1940         struct dom_sid *sid;
1941         struct lsa_RightSet rights;
1942         unsigned i;
1943
1944         if (!args[1]) {
1945                 d_printf("privileges <sid|name>\n");
1946                 return 1;
1947         }
1948
1949         sid = dom_sid_parse_talloc(ctx, args[1]);
1950         if (sid == NULL) {
1951                 const char *sid_str;
1952                 status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
1953                 if (!NT_STATUS_IS_OK(status)) {
1954                         d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
1955                         return 1;
1956                 }
1957                 sid = dom_sid_parse_talloc(ctx, sid_str);
1958         }
1959
1960         status = smblsa_sid_privileges(ctx->cli, sid, ctx, &rights);
1961         if (!NT_STATUS_IS_OK(status)) {
1962                 d_printf("lsa_EnumAccountRights - %s\n", nt_errstr(status));
1963                 return 1;
1964         }
1965
1966         for (i=0;i<rights.count;i++) {
1967                 d_printf("\t%s\n", rights.names[i].string);
1968         }
1969
1970         return 0;
1971 }
1972
1973
1974 /****************************************************************************
1975 add privileges for a user
1976 ****************************************************************************/
1977 static int cmd_addprivileges(struct smbclient_context *ctx, const char **args)
1978 {
1979         NTSTATUS status;
1980         struct dom_sid *sid;
1981         struct lsa_RightSet rights;
1982         int i;
1983
1984         if (!args[1]) {
1985                 d_printf("addprivileges <sid|name> <privilege...>\n");
1986                 return 1;
1987         }
1988
1989         sid = dom_sid_parse_talloc(ctx, args[1]);
1990         if (sid == NULL) {
1991                 const char *sid_str;
1992                 status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
1993                 if (!NT_STATUS_IS_OK(status)) {
1994                         d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
1995                         return 1;
1996                 }
1997                 sid = dom_sid_parse_talloc(ctx, sid_str);
1998         }
1999
2000         ZERO_STRUCT(rights);
2001         for (i = 2; args[i]; i++) {
2002                 rights.names = talloc_realloc(ctx, rights.names, 
2003                                               struct lsa_StringLarge, rights.count+1);
2004                 rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
2005                 rights.count++;
2006         }
2007
2008
2009         status = smblsa_sid_add_privileges(ctx->cli, sid, ctx, &rights);
2010         if (!NT_STATUS_IS_OK(status)) {
2011                 d_printf("lsa_AddAccountRights - %s\n", nt_errstr(status));
2012                 return 1;
2013         }
2014
2015         return 0;
2016 }
2017
2018 /****************************************************************************
2019 delete privileges for a user
2020 ****************************************************************************/
2021 static int cmd_delprivileges(struct smbclient_context *ctx, const char **args)
2022 {
2023         NTSTATUS status;
2024         struct dom_sid *sid;
2025         struct lsa_RightSet rights;
2026         int i;
2027
2028         if (!args[1]) {
2029                 d_printf("delprivileges <sid|name> <privilege...>\n");
2030                 return 1;
2031         }
2032
2033         sid = dom_sid_parse_talloc(ctx, args[1]);
2034         if (sid == NULL) {
2035                 const char *sid_str;
2036                 status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
2037                 if (!NT_STATUS_IS_OK(status)) {
2038                         d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
2039                         return 1;
2040                 }
2041                 sid = dom_sid_parse_talloc(ctx, sid_str);
2042         }
2043
2044         ZERO_STRUCT(rights);
2045         for (i = 2; args[i]; i++) {
2046                 rights.names = talloc_realloc(ctx, rights.names, 
2047                                               struct lsa_StringLarge, rights.count+1);
2048                 rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
2049                 rights.count++;
2050         }
2051
2052
2053         status = smblsa_sid_del_privileges(ctx->cli, sid, ctx, &rights);
2054         if (!NT_STATUS_IS_OK(status)) {
2055                 d_printf("lsa_RemoveAccountRights - %s\n", nt_errstr(status));
2056                 return 1;
2057         }
2058
2059         return 0;
2060 }
2061
2062
2063 /****************************************************************************
2064 ****************************************************************************/
2065 static int cmd_open(struct smbclient_context *ctx, const char **args)
2066 {
2067         char *mask;
2068         
2069         if (!args[1]) {
2070                 d_printf("open <filename>\n");
2071                 return 1;
2072         }
2073         mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
2074
2075         smbcli_open(ctx->cli->tree, mask, O_RDWR, DENY_ALL);
2076
2077         return 0;
2078 }
2079
2080
2081 /****************************************************************************
2082 remove a directory
2083 ****************************************************************************/
2084 static int cmd_rmdir(struct smbclient_context *ctx, const char **args)
2085 {
2086         char *mask;
2087   
2088         if (!args[1]) {
2089                 d_printf("rmdir <dirname>\n");
2090                 return 1;
2091         }
2092         mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
2093
2094         if (NT_STATUS_IS_ERR(smbcli_rmdir(ctx->cli->tree, mask))) {
2095                 d_printf("%s removing remote directory file %s\n",
2096                          smbcli_errstr(ctx->cli->tree),mask);
2097         }
2098         
2099         return 0;
2100 }
2101
2102 /****************************************************************************
2103  UNIX hardlink.
2104 ****************************************************************************/
2105 static int cmd_link(struct smbclient_context *ctx, const char **args)
2106 {
2107         char *src,*dest;
2108   
2109         if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
2110                 d_printf("Server doesn't support UNIX CIFS calls.\n");
2111                 return 1;
2112         }
2113
2114         
2115         if (!args[1] || !args[2]) {
2116                 d_printf("link <src> <dest>\n");
2117                 return 1;
2118         }
2119
2120         src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
2121         dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
2122
2123         if (NT_STATUS_IS_ERR(smbcli_unix_hardlink(ctx->cli->tree, src, dest))) {
2124                 d_printf("%s linking files (%s -> %s)\n", smbcli_errstr(ctx->cli->tree), src, dest);
2125                 return 1;
2126         }  
2127
2128         return 0;
2129 }
2130
2131 /****************************************************************************
2132  UNIX symlink.
2133 ****************************************************************************/
2134
2135 static int cmd_symlink(struct smbclient_context *ctx, const char **args)
2136 {
2137         char *src,*dest;
2138   
2139         if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
2140                 d_printf("Server doesn't support UNIX CIFS calls.\n");
2141                 return 1;
2142         }
2143
2144         if (!args[1] || !args[2]) {
2145                 d_printf("symlink <src> <dest>\n");
2146                 return 1;
2147         }
2148
2149         src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
2150         dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
2151
2152         if (NT_STATUS_IS_ERR(smbcli_unix_symlink(ctx->cli->tree, src, dest))) {
2153                 d_printf("%s symlinking files (%s -> %s)\n",
2154                         smbcli_errstr(ctx->cli->tree), src, dest);
2155                 return 1;
2156         } 
2157
2158         return 0;
2159 }
2160
2161 /****************************************************************************
2162  UNIX chmod.
2163 ****************************************************************************/
2164
2165 static int cmd_chmod(struct smbclient_context *ctx, const char **args)
2166 {
2167         char *src;
2168         mode_t mode;
2169   
2170         if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
2171                 d_printf("Server doesn't support UNIX CIFS calls.\n");
2172                 return 1;
2173         }
2174
2175         if (!args[1] || !args[2]) {
2176                 d_printf("chmod mode file\n");
2177                 return 1;
2178         }
2179
2180         src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
2181         
2182         mode = (mode_t)strtol(args[1], NULL, 8);
2183
2184         if (NT_STATUS_IS_ERR(smbcli_unix_chmod(ctx->cli->tree, src, mode))) {
2185                 d_printf("%s chmod file %s 0%o\n",
2186                         smbcli_errstr(ctx->cli->tree), src, (uint_t)mode);
2187                 return 1;
2188         } 
2189
2190         return 0;
2191 }
2192
2193 /****************************************************************************
2194  UNIX chown.
2195 ****************************************************************************/
2196
2197 static int cmd_chown(struct smbclient_context *ctx, const char **args)
2198 {
2199         char *src;
2200         uid_t uid;
2201         gid_t gid;
2202   
2203         if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
2204                 d_printf("Server doesn't support UNIX CIFS calls.\n");
2205                 return 1;
2206         }
2207
2208         if (!args[1] || !args[2] || !args[3]) {
2209                 d_printf("chown uid gid file\n");
2210                 return 1;
2211         }
2212
2213         uid = (uid_t)atoi(args[1]);
2214         gid = (gid_t)atoi(args[2]);
2215         src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[3]);
2216
2217         if (NT_STATUS_IS_ERR(smbcli_unix_chown(ctx->cli->tree, src, uid, gid))) {
2218                 d_printf("%s chown file %s uid=%d, gid=%d\n",
2219                         smbcli_errstr(ctx->cli->tree), src, (int)uid, (int)gid);
2220                 return 1;
2221         } 
2222
2223         return 0;
2224 }
2225
2226 /****************************************************************************
2227 rename some files
2228 ****************************************************************************/
2229 static int cmd_rename(struct smbclient_context *ctx, const char **args)
2230 {
2231         char *src,*dest;
2232   
2233         if (!args[1] || !args[2]) {
2234                 d_printf("rename <src> <dest>\n");
2235                 return 1;
2236         }
2237
2238         src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
2239         dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
2240
2241         if (NT_STATUS_IS_ERR(smbcli_rename(ctx->cli->tree, src, dest))) {
2242                 d_printf("%s renaming files\n",smbcli_errstr(ctx->cli->tree));
2243                 return 1;
2244         }
2245         
2246         return 0;
2247 }
2248
2249
2250 /****************************************************************************
2251 toggle the prompt flag
2252 ****************************************************************************/
2253 static int cmd_prompt(struct smbclient_context *ctx, const char **args)
2254 {
2255         ctx->prompt = !ctx->prompt;
2256         DEBUG(2,("prompting is now %s\n",ctx->prompt?"on":"off"));
2257         
2258         return 1;
2259 }
2260
2261
2262 /****************************************************************************
2263 set the newer than time
2264 ****************************************************************************/
2265 static int cmd_newer(struct smbclient_context *ctx, const char **args)
2266 {
2267         struct stat sbuf;
2268
2269         if (args[1] && (stat(args[1],&sbuf) == 0)) {
2270                 ctx->newer_than = sbuf.st_mtime;
2271                 DEBUG(1,("Getting files newer than %s",
2272                          asctime(localtime(&ctx->newer_than))));
2273         } else {
2274                 ctx->newer_than = 0;
2275         }
2276
2277         if (args[1] && ctx->newer_than == 0) {
2278                 d_printf("Error setting newer-than time\n");
2279                 return 1;
2280         }
2281
2282         return 0;
2283 }
2284
2285 /****************************************************************************
2286 set the archive level
2287 ****************************************************************************/
2288 static int cmd_archive(struct smbclient_context *ctx, const char **args)
2289 {
2290         if (args[1]) {
2291                 ctx->archive_level = atoi(args[1]);
2292         } else
2293                 d_printf("Archive level is %d\n",ctx->archive_level);
2294
2295         return 0;
2296 }
2297
2298 /****************************************************************************
2299 toggle the lowercaseflag
2300 ****************************************************************************/
2301 static int cmd_lowercase(struct smbclient_context *ctx, const char **args)
2302 {
2303         ctx->lowercase = !ctx->lowercase;
2304         DEBUG(2,("filename lowercasing is now %s\n",ctx->lowercase?"on":"off"));
2305
2306         return 0;
2307 }
2308
2309
2310
2311
2312 /****************************************************************************
2313 toggle the recurse flag
2314 ****************************************************************************/
2315 static int cmd_recurse(struct smbclient_context *ctx, const char **args)
2316 {
2317         ctx->recurse = !ctx->recurse;
2318         DEBUG(2,("directory recursion is now %s\n",ctx->recurse?"on":"off"));
2319
2320         return 0;
2321 }
2322
2323 /****************************************************************************
2324 toggle the translate flag
2325 ****************************************************************************/
2326 static int cmd_translate(struct smbclient_context *ctx, const char **args)
2327 {
2328         ctx->translation = !ctx->translation;
2329         DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
2330                  ctx->translation?"on":"off"));
2331
2332         return 0;
2333 }
2334
2335
2336 /****************************************************************************
2337 do a printmode command
2338 ****************************************************************************/
2339 static int cmd_printmode(struct smbclient_context *ctx, const char **args)
2340 {
2341         if (args[1]) {
2342                 if (strequal(args[1],"text")) {
2343                         ctx->printmode = 0;      
2344                 } else {
2345                         if (strequal(args[1],"graphics"))
2346                                 ctx->printmode = 1;
2347                         else
2348                                 ctx->printmode = atoi(args[1]);
2349                 }
2350         }
2351
2352         switch(ctx->printmode)
2353         {
2354                 case 0: 
2355                         DEBUG(2,("the printmode is now text\n"));
2356                         break;
2357                 case 1: 
2358                         DEBUG(2,("the printmode is now graphics\n"));
2359                         break;
2360                 default: 
2361                         DEBUG(2,("the printmode is now %d\n", ctx->printmode));
2362                         break;
2363         }
2364         
2365         return 0;
2366 }
2367
2368 /****************************************************************************
2369  do the lcd command
2370  ****************************************************************************/
2371 static int cmd_lcd(struct smbclient_context *ctx, const char **args)
2372 {
2373         char d[PATH_MAX];
2374         
2375         if (args[1]) 
2376                 chdir(args[1]);
2377         DEBUG(2,("the local directory is now %s\n",getcwd(d, PATH_MAX)));
2378
2379         return 0;
2380 }
2381
2382 /****************************************************************************
2383 history
2384 ****************************************************************************/
2385 static int cmd_history(struct smbclient_context *ctx, const char **args)
2386 {
2387 #if defined(HAVE_LIBREADLINE)
2388         HIST_ENTRY **hlist;
2389         int i;
2390
2391         hlist = history_list();
2392         
2393         for (i = 0; hlist && hlist[i]; i++) {
2394                 DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
2395         }
2396 #else
2397         DEBUG(0,("no history without readline support\n"));
2398 #endif
2399
2400         return 0;
2401 }
2402
2403 /****************************************************************************
2404  get a file restarting at end of local file
2405  ****************************************************************************/
2406 static int cmd_reget(struct smbclient_context *ctx, const char **args)
2407 {
2408         char *local_name;
2409         char *remote_name;
2410
2411         if (!args[1]) {
2412                 d_printf("reget <filename>\n");
2413                 return 1;
2414         }
2415         remote_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
2416         dos_clean_name(remote_name);
2417         
2418         if (args[2]) 
2419                 local_name = talloc_strdup(ctx, args[2]);
2420         else
2421                 local_name = talloc_strdup(ctx, args[1]);
2422         
2423         return do_get(ctx, remote_name, local_name, True);
2424 }
2425
2426 /****************************************************************************
2427  put a file restarting at end of local file
2428  ****************************************************************************/
2429 static int cmd_reput(struct smbclient_context *ctx, const char **args)
2430 {
2431         char *local_name;
2432         char *remote_name;
2433         
2434         if (!args[1]) {
2435                 d_printf("reput <filename>\n");
2436                 return 1;
2437         }
2438         local_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
2439   
2440         if (!file_exist(local_name)) {
2441                 d_printf("%s does not exist\n", local_name);
2442                 return 1;
2443         }
2444
2445         if (args[2]) 
2446                 remote_name = talloc_strdup(ctx, args[2]);
2447         else
2448                 remote_name = talloc_strdup(ctx, args[1]);
2449         
2450         dos_clean_name(remote_name);
2451
2452         return do_put(ctx, remote_name, local_name, True);
2453 }
2454
2455
2456 /*
2457   return a string representing a share type
2458 */
2459 static const char *share_type_str(uint32_t type)
2460 {
2461         switch (type & 0xF) {
2462         case STYPE_DISKTREE: 
2463                 return "Disk";
2464         case STYPE_PRINTQ: 
2465                 return "Printer";
2466         case STYPE_DEVICE: 
2467                 return "Device";
2468         case STYPE_IPC: 
2469                 return "IPC";
2470         default:
2471                 return "Unknown";
2472         }
2473 }
2474
2475
2476 /*
2477   display a list of shares from a level 1 share enum
2478 */
2479 static void display_share_result(struct srvsvc_NetShareCtr1 *ctr1)
2480 {
2481         int i;
2482
2483         for (i=0;i<ctr1->count;i++) {
2484                 struct srvsvc_NetShareInfo1 *info = ctr1->array+i;
2485
2486                 printf("\t%-15s %-10.10s %s\n", 
2487                        info->name, 
2488                        share_type_str(info->type), 
2489                        info->comment);
2490         }
2491 }
2492
2493
2494
2495 /****************************************************************************
2496 try and browse available shares on a host
2497 ****************************************************************************/
2498 static BOOL browse_host(const char *query_host)
2499 {
2500         struct dcerpc_pipe *p;
2501         char *binding;
2502         NTSTATUS status;
2503         struct srvsvc_NetShareEnumAll r;
2504         uint32_t resume_handle = 0;
2505         TALLOC_CTX *mem_ctx = talloc_init("browse_host");
2506         struct srvsvc_NetShareCtr1 ctr1;
2507
2508         binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", query_host);
2509
2510         status = dcerpc_pipe_connect(mem_ctx, &p, binding, 
2511                                          &dcerpc_table_srvsvc,
2512                                      cmdline_credentials, NULL);
2513         if (!NT_STATUS_IS_OK(status)) {
2514                 d_printf("Failed to connect to %s - %s\n", 
2515                          binding, nt_errstr(status));
2516                 talloc_free(mem_ctx);
2517                 return False;
2518         }
2519
2520         r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
2521         r.in.level = 1;
2522         r.in.ctr.ctr1 = &ctr1;
2523         r.in.max_buffer = ~0;
2524         r.in.resume_handle = &resume_handle;
2525
2526         d_printf("\n\tSharename       Type       Comment\n");
2527         d_printf("\t---------       ----       -------\n");
2528
2529         do {
2530                 ZERO_STRUCT(ctr1);
2531                 status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r);
2532
2533                 if (NT_STATUS_IS_OK(status) && 
2534                     (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA) ||
2535                      W_ERROR_IS_OK(r.out.result)) &&
2536                     r.out.ctr.ctr1) {
2537                         display_share_result(r.out.ctr.ctr1);
2538                         resume_handle += r.out.ctr.ctr1->count;
2539                 }
2540         } while (NT_STATUS_IS_OK(status) && W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA));
2541
2542         talloc_free(mem_ctx);
2543
2544         if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
2545                 d_printf("Failed NetShareEnumAll %s - %s/%s\n", 
2546                          binding, nt_errstr(status), win_errstr(r.out.result));
2547                 return False;
2548         }
2549
2550         return False;
2551 }
2552
2553 /****************************************************************************
2554 try and browse available connections on a host
2555 ****************************************************************************/
2556 static BOOL list_servers(const char *wk_grp)
2557 {
2558         d_printf("REWRITE: list servers not implemented\n");
2559         return False;
2560 }
2561
2562 /* Some constants for completing filename arguments */
2563
2564 #define COMPL_NONE        0          /* No completions */
2565 #define COMPL_REMOTE      1          /* Complete remote filename */
2566 #define COMPL_LOCAL       2          /* Complete local filename */
2567
2568 static int cmd_help(struct smbclient_context *ctx, const char **args);
2569
2570 /* This defines the commands supported by this client.
2571  * NOTE: The "!" must be the last one in the list because it's fn pointer
2572  *       field is NULL, and NULL in that field is used in process_tok()
2573  *       (below) to indicate the end of the list.  crh
2574  */
2575 static struct
2576 {
2577   const char *name;
2578   int (*fn)(struct smbclient_context *ctx, const char **args);
2579   const char *description;
2580   char compl_args[2];      /* Completion argument info */
2581 } commands[] = 
2582 {
2583   {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
2584   {"addprivileges",cmd_addprivileges,"<sid|name> <privilege...> add privileges for a user",{COMPL_NONE,COMPL_NONE}},
2585   {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
2586   {"acl",cmd_acl,"<file> show file ACL",{COMPL_NONE,COMPL_NONE}},
2587   {"allinfo",cmd_allinfo,"<file> show all possible info about a file",{COMPL_NONE,COMPL_NONE}},
2588   {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
2589   {"cancel",cmd_rewrite,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
2590   {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
2591   {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
2592   {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
2593   {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
2594   {"delprivileges",cmd_delprivileges,"<sid|name> <privilege...> remove privileges for a user",{COMPL_NONE,COMPL_NONE}},
2595   {"deltree",cmd_deltree,"<dir> delete a whole directory tree",{COMPL_REMOTE,COMPL_NONE}},
2596   {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
2597   {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
2598   {"eainfo",cmd_eainfo,"<file> show EA contents for a file",{COMPL_NONE,COMPL_NONE}},
2599   {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
2600   {"fsinfo",cmd_fsinfo,"query file system info",{COMPL_NONE,COMPL_NONE}},
2601   {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
2602   {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
2603   {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
2604   {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
2605   {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
2606   {"lookup",cmd_lookup,"<sid|name> show SID for name or name for SID",{COMPL_NONE,COMPL_NONE}},
2607   {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},  
2608   {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
2609   {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
2610   {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
2611   {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
2612   {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
2613   {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},  
2614   {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
2615   {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
2616   {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
2617   {"privileges",cmd_privileges,"<user> show privileges for a user",{COMPL_NONE,COMPL_NONE}},
2618   {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
2619   {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
2620   {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},  
2621   {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
2622   {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
2623   {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
2624   {"queue",cmd_rewrite,"show the print queue",{COMPL_NONE,COMPL_NONE}},
2625   {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
2626   {"rd",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
2627   {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}},  
2628   {"reget",cmd_reget,"<remote name> [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}},
2629   {"rename",cmd_rename,"<src> <dest> rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
2630   {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
2631   {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
2632   {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
2633   {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
2634   {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
2635   
2636   /* Yes, this must be here, see crh's comment above. */
2637   {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
2638   {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
2639 };
2640
2641
2642 /*******************************************************************
2643   lookup a command string in the list of commands, including 
2644   abbreviations
2645   ******************************************************************/
2646 static int process_tok(const char *tok)
2647 {
2648         int i = 0, matches = 0;
2649         int cmd=0;
2650         int tok_len = strlen(tok);
2651         
2652         while (commands[i].fn != NULL) {
2653                 if (strequal(commands[i].name,tok)) {
2654                         matches = 1;
2655                         cmd = i;
2656                         break;
2657                 } else if (strncasecmp(commands[i].name, tok, tok_len) == 0) {
2658                         matches++;
2659                         cmd = i;
2660                 }
2661                 i++;
2662         }
2663   
2664         if (matches == 0)
2665                 return(-1);
2666         else if (matches == 1)
2667                 return(cmd);
2668         else
2669                 return(-2);
2670 }
2671
2672 /****************************************************************************
2673 help
2674 ****************************************************************************/
2675 static int cmd_help(struct smbclient_context *ctx, const char **args)
2676 {
2677         int i=0,j;
2678         
2679         if (args[1]) {
2680                 if ((i = process_tok(args[1])) >= 0)
2681                         d_printf("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description);
2682         } else {
2683                 while (commands[i].description) {
2684                         for (j=0; commands[i].description && (j<5); j++) {
2685                                 d_printf("%-15s",commands[i].name);
2686                                 i++;
2687                         }
2688                         d_printf("\n");
2689                 }
2690         }
2691         return 0;
2692 }
2693
2694 static int process_line(struct smbclient_context *ctx, const char *cline);
2695 /****************************************************************************
2696 process a -c command string
2697 ****************************************************************************/
2698 static int process_command_string(struct smbclient_context *ctx, const char *cmd)
2699 {
2700         const char **lines;
2701         int i, rc = 0;
2702
2703         lines = str_list_make(NULL, cmd, ";");
2704         for (i = 0; lines[i]; i++) {
2705                 rc |= process_line(ctx, lines[i]);
2706         }
2707         talloc_free(lines);
2708
2709         return rc;
2710 }       
2711
2712 #define MAX_COMPLETIONS 100
2713
2714 typedef struct {
2715         char *dirmask;
2716         char **matches;
2717         int count, samelen;
2718         const char *text;
2719         int len;
2720 } completion_remote_t;
2721
2722 static void completion_remote_filter(struct clilist_file_info *f, const char *mask, void *state)
2723 {
2724         completion_remote_t *info = (completion_remote_t *)state;
2725
2726         if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (strcmp(f->name, ".") != 0) && (strcmp(f->name, "..") != 0)) {
2727                 if ((info->dirmask[0] == 0) && !(f->attrib & FILE_ATTRIBUTE_DIRECTORY))
2728                         info->matches[info->count] = strdup(f->name);
2729                 else {
2730                         char *tmp;
2731
2732                         if (info->dirmask[0] != 0)
2733                                 tmp = talloc_asprintf(NULL, "%s/%s", info->dirmask, f->name);
2734                         else
2735                                 tmp = talloc_strdup(NULL, f->name);
2736                         
2737                         if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
2738                                 tmp = talloc_append_string(NULL, tmp, "/");
2739                         info->matches[info->count] = tmp;
2740                 }
2741                 if (info->matches[info->count] == NULL)
2742                         return;
2743                 if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
2744                         smb_readline_ca_char(0);
2745
2746                 if (info->count == 1)
2747                         info->samelen = strlen(info->matches[info->count]);
2748                 else
2749                         while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0)
2750                                 info->samelen--;
2751                 info->count++;
2752         }
2753 }
2754
2755 static char **remote_completion(const char *text, int len)
2756 {
2757         char *dirmask;
2758         int i;
2759         completion_remote_t info;
2760
2761         info.samelen = len;
2762         info.text = text;
2763         info.len = len;
2764  
2765         if (len >= PATH_MAX)
2766                 return(NULL);
2767
2768         info.matches = malloc_array_p(char *, MAX_COMPLETIONS);
2769         if (!info.matches) return NULL;
2770         info.matches[0] = NULL;
2771
2772         for (i = len-1; i >= 0; i--)
2773                 if ((text[i] == '/') || (text[i] == '\\'))
2774                         break;
2775         info.text = text+i+1;
2776         info.samelen = info.len = len-i-1;
2777
2778         if (i > 0) {
2779                 info.dirmask = talloc_strndup(NULL, text, i+1);
2780                 info.dirmask[i+1] = 0;
2781                 asprintf(&dirmask, "%s%*s*", rl_ctx->remote_cur_dir, i-1, text);
2782         } else
2783                 asprintf(&dirmask, "%s*", rl_ctx->remote_cur_dir);
2784
2785         if (smbcli_list(rl_ctx->cli->tree, dirmask, 
2786                      FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 
2787                      completion_remote_filter, &info) < 0)
2788                 goto cleanup;
2789
2790         if (info.count == 2)
2791                 info.matches[0] = strdup(info.matches[1]);
2792         else {
2793                 info.matches[0] = malloc(info.samelen+1);
2794                 if (!info.matches[0])
2795                         goto cleanup;
2796                 strncpy(info.matches[0], info.matches[1], info.samelen);
2797                 info.matches[0][info.samelen] = 0;
2798         }
2799         info.matches[info.count] = NULL;
2800         return info.matches;
2801
2802 cleanup:
2803         for (i = 0; i < info.count; i++)
2804                 free(info.matches[i]);
2805         free(info.matches);
2806         return NULL;
2807 }
2808
2809 static char **completion_fn(const char *text, int start, int end)
2810 {
2811         smb_readline_ca_char(' ');
2812
2813         if (start) {
2814                 const char *buf, *sp;
2815                 int i;
2816                 char compl_type;
2817
2818                 buf = smb_readline_get_line_buffer();
2819                 if (buf == NULL)
2820                         return NULL;
2821                 
2822                 sp = strchr(buf, ' ');
2823                 if (sp == NULL)
2824                         return NULL;
2825                 
2826                 for (i = 0; commands[i].name; i++)
2827                         if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
2828                                 break;
2829                 if (commands[i].name == NULL)
2830                         return NULL;
2831
2832                 while (*sp == ' ')
2833                         sp++;
2834
2835                 if (sp == (buf + start))
2836                         compl_type = commands[i].compl_args[0];
2837                 else
2838                         compl_type = commands[i].compl_args[1];
2839
2840                 if (compl_type == COMPL_REMOTE)
2841                         return remote_completion(text, end - start);
2842                 else /* fall back to local filename completion */
2843                         return NULL;
2844         } else {
2845                 char **matches;
2846                 int i, len, samelen = 0, count=1;
2847
2848                 matches = malloc_array_p(char *, MAX_COMPLETIONS);
2849                 if (!matches) return NULL;
2850                 matches[0] = NULL;
2851
2852                 len = strlen(text);
2853                 for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
2854                         if (strncmp(text, commands[i].name, len) == 0) {
2855                                 matches[count] = strdup(commands[i].name);
2856                                 if (!matches[count])
2857                                         goto cleanup;
2858                                 if (count == 1)
2859                                         samelen = strlen(matches[count]);
2860                                 else
2861                                         while (strncmp(matches[count], matches[count-1], samelen) != 0)
2862                                                 samelen--;
2863                                 count++;
2864                         }
2865                 }
2866
2867                 switch (count) {
2868                 case 0: /* should never happen */
2869                 case 1:
2870                         goto cleanup;
2871                 case 2:
2872                         matches[0] = strdup(matches[1]);
2873                         break;
2874                 default:
2875                         matches[0] = malloc(samelen+1);
2876                         if (!matches[0])
2877                                 goto cleanup;
2878                         strncpy(matches[0], matches[1], samelen);
2879                         matches[0][samelen] = 0;
2880                 }
2881                 matches[count] = NULL;
2882                 return matches;
2883
2884 cleanup:
2885                 while (i >= 0) {
2886                         free(matches[i]);
2887                         i--;
2888                 }
2889                 free(matches);
2890                 return NULL;
2891         }
2892 }
2893
2894 /****************************************************************************
2895 make sure we swallow keepalives during idle time
2896 ****************************************************************************/
2897 static void readline_callback(void)
2898 {
2899         static time_t last_t;
2900         time_t t;
2901
2902         t = time(NULL);
2903
2904         if (t - last_t < 5) return;
2905
2906         last_t = t;
2907
2908         smbcli_transport_process(rl_ctx->cli->transport);
2909
2910         if (rl_ctx->cli->tree) {
2911                 smbcli_chkpath(rl_ctx->cli->tree, "\\");
2912         }
2913 }
2914
2915 static int process_line(struct smbclient_context *ctx, const char *cline)
2916 {
2917         const char **args;
2918         int i;
2919
2920         /* and get the first part of the command */
2921         args = str_list_make_shell(ctx, cline, NULL);
2922         if (!args || !args[0])
2923                 return 0;
2924
2925         if ((i = process_tok(args[0])) >= 0) {
2926                 i = commands[i].fn(ctx, args);
2927         } else if (i == -2) {
2928                 d_printf("%s: command abbreviation ambiguous\n",args[0]);
2929         } else {
2930                 d_printf("%s: command not found\n",args[0]);
2931         }
2932
2933         talloc_free(args);
2934
2935         return i;
2936 }
2937
2938 /****************************************************************************
2939 process commands on stdin
2940 ****************************************************************************/
2941 static int process_stdin(struct smbclient_context *ctx)
2942 {
2943         int rc = 0;
2944         while (1) {
2945                 /* display a prompt */
2946                 char *the_prompt = talloc_asprintf(ctx, "smb: %s> ", ctx->remote_cur_dir);
2947                 char *cline = smb_readline(the_prompt, readline_callback, completion_fn);
2948                 talloc_free(the_prompt);
2949                         
2950                 if (!cline) break;
2951                 
2952                 /* special case - first char is ! */
2953                 if (*cline == '!') {
2954                         system(cline + 1);
2955                         continue;
2956                 }
2957
2958                 rc |= process_command_string(ctx, cline); 
2959         }
2960
2961         return rc;
2962 }
2963
2964
2965 /***************************************************** 
2966 return a connection to a server
2967 *******************************************************/
2968 static struct smbclient_context *do_connect(TALLOC_CTX *mem_ctx, 
2969                                        const char *specified_server, const char *specified_share, struct cli_credentials *cred)
2970 {
2971         NTSTATUS status;
2972         struct smbclient_context *ctx = talloc_zero(mem_ctx, struct smbclient_context);
2973         char *server, *share;
2974
2975         if (!ctx) {
2976                 return NULL;
2977         }
2978
2979         rl_ctx = ctx; /* Ugly hack */
2980
2981         if (strncmp(specified_share, "\\\\", 2) == 0 ||
2982             strncmp(specified_share, "//", 2) == 0) {
2983                 smbcli_parse_unc(specified_share, ctx, &server, &share);
2984         } else {
2985                 share = talloc_strdup(ctx, specified_share);
2986                 server = talloc_strdup(ctx, specified_server);
2987         }
2988
2989         ctx->remote_cur_dir = talloc_strdup(ctx, "\\");
2990         
2991         status = smbcli_full_connection(ctx, &ctx->cli, server,
2992                                         share, NULL, cred, NULL);
2993         if (!NT_STATUS_IS_OK(status)) {
2994                 d_printf("Connection to \\\\%s\\%s failed - %s\n", 
2995                          server, share, nt_errstr(status));
2996                 talloc_free(ctx);
2997                 return NULL;
2998         }
2999
3000         return ctx;
3001 }
3002
3003 /****************************************************************************
3004 handle a -L query
3005 ****************************************************************************/
3006 static int do_host_query(const char *query_host)
3007 {
3008         browse_host(query_host);
3009         list_servers(lp_workgroup());
3010         return(0);
3011 }
3012
3013
3014 /****************************************************************************
3015 handle a message operation
3016 ****************************************************************************/
3017 static int do_message_op(const char *desthost, const char *destip, int name_type)
3018 {
3019         struct nbt_name called, calling;
3020         const char *server_name;
3021         struct smbcli_state *cli;
3022
3023         make_nbt_name_client(&calling, lp_netbios_name());
3024
3025         nbt_choose_called_name(NULL, &called, desthost, name_type);
3026
3027         server_name = destip ? destip : desthost;
3028
3029         if (!(cli=smbcli_state_init(NULL)) || !smbcli_socket_connect(cli, server_name)) {
3030                 d_printf("Connection to %s failed\n", server_name);
3031                 return 1;
3032         }
3033
3034         if (!smbcli_transport_establish(cli, &calling, &called)) {
3035                 d_printf("session request failed\n");
3036                 talloc_free(cli);
3037                 return 1;
3038         }
3039
3040         send_message(cli, desthost);
3041         talloc_free(cli);
3042
3043         return 0;
3044 }
3045
3046
3047 /****************************************************************************
3048   main program
3049 ****************************************************************************/
3050  int main(int argc,char *argv[])
3051 {
3052         const char *base_directory = NULL;
3053         const char *dest_ip;
3054         int opt;
3055         const char *query_host = NULL;
3056         BOOL message = False;
3057         const char *desthost;
3058 #ifdef KANJI
3059         const char *term_code = KANJI;
3060 #else
3061         const char *term_code = "";
3062 #endif /* KANJI */
3063         poptContext pc;
3064         const char *service = NULL;
3065         int port = 0;
3066         char *p;
3067         int rc = 0;
3068         int name_type = 0x20;
3069         TALLOC_CTX *mem_ctx;
3070         struct smbclient_context *ctx;
3071         const char *cmdstr = NULL;
3072
3073         struct poptOption long_options[] = {
3074                 POPT_AUTOHELP
3075
3076                 { "message", 'M', POPT_ARG_STRING, NULL, 'M', "Send message", "HOST" },
3077                 { "ip-address", 'I', POPT_ARG_STRING, NULL, 'I', "Use this IP to connect to", "IP" },
3078                 { "stderr", 'E', POPT_ARG_NONE, NULL, 'E', "Write messages to stderr instead of stdout" },
3079                 { "list", 'L', POPT_ARG_STRING, NULL, 'L', "Get a list of shares available on a host", "HOST" },
3080                 { "terminal", 't', POPT_ARG_STRING, NULL, 't', "Terminal I/O code {sjis|euc|jis7|jis8|junet|hex}", "CODE" },
3081                 { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" },
3082                 { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" }, 
3083                 { "send-buffer", 'b', POPT_ARG_INT, NULL, 'b', "Changes the transmit/send buffer", "BYTES" },
3084                 { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
3085                 POPT_COMMON_SAMBA
3086                 POPT_COMMON_CONNECTION
3087                 POPT_COMMON_CREDENTIALS
3088                 POPT_COMMON_VERSION
3089                 POPT_TABLEEND
3090         };
3091         
3092         mem_ctx = talloc_init("client.c/main");
3093         if (!mem_ctx) {
3094                 d_printf("\nclient.c: Not enough memory\n");
3095                 exit(1);
3096         }
3097
3098         pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 0);
3099         poptSetOtherOptionHelp(pc, "[OPTIONS] service <password>");
3100
3101         while ((opt = poptGetNextOpt(pc)) != -1) {
3102                 switch (opt) {
3103                 case 'M':
3104                         /* Messages are sent to NetBIOS name type 0x3
3105                          * (Messenger Service).  Make sure we default
3106                          * to port 139 instead of port 445. srl,crh
3107                          */
3108                         name_type = 0x03; 
3109                         desthost = strdup(poptGetOptArg(pc));
3110                         if( 0 == port ) port = 139;
3111                         message = True;
3112                         break;
3113                 case 'I':
3114                         dest_ip = poptGetOptArg(pc);
3115                         break;
3116                 case 'L':
3117                         query_host = strdup(poptGetOptArg(pc));
3118                         break;
3119                 case 't':
3120                         term_code = strdup(poptGetOptArg(pc));
3121                         break;
3122                 case 'D':
3123                         base_directory = strdup(poptGetOptArg(pc));
3124                         break;
3125                 case 'b':
3126                         io_bufsize = MAX(1, atoi(poptGetOptArg(pc)));
3127                         break;
3128                 }
3129         }
3130
3131         gensec_init();
3132
3133         if(poptPeekArg(pc)) {
3134                 char *s = strdup(poptGetArg(pc)); 
3135
3136                 /* Convert any '/' characters in the service name to '\' characters */
3137                 string_replace(s, '/','\\');
3138
3139                 service = s;
3140
3141                 if (count_chars(s,'\\') < 3) {
3142                         d_printf("\n%s: Not enough '\\' characters in service\n",s);
3143                         poptPrintUsage(pc, stderr, 0);
3144                         exit(1);
3145                 }
3146         }
3147
3148         if (poptPeekArg(pc)) { 
3149                 cli_credentials_set_password(cmdline_credentials, poptGetArg(pc), CRED_SPECIFIED);
3150         }
3151
3152         /*init_names(); */
3153
3154         if (!query_host && !service && !message) {
3155                 poptPrintUsage(pc, stderr, 0);
3156                 exit(1);
3157         }
3158
3159         poptFreeContext(pc);
3160
3161         DEBUG( 3, ( "Client started (version %s).\n", SAMBA_VERSION_STRING ) );
3162
3163         if (query_host && (p=strchr_m(query_host,'#'))) {
3164                 *p = 0;
3165                 p++;
3166                 sscanf(p, "%x", &name_type);
3167         }
3168   
3169         if (query_host) {
3170                 return do_host_query(query_host);
3171         }
3172
3173         if (message) {
3174                 return do_message_op(desthost, dest_ip, name_type);
3175         }
3176         
3177
3178         ctx = do_connect(mem_ctx, desthost, service, cmdline_credentials);
3179         if (!ctx)
3180                 return 1;
3181
3182         if (base_directory) 
3183                 do_cd(ctx, base_directory);
3184         
3185         if (cmdstr) {
3186                 rc = process_command_string(ctx, cmdstr);
3187         } else {
3188                 rc = process_stdin(ctx);
3189         }
3190   
3191         talloc_free(mem_ctx);
3192
3193         return rc;
3194 }