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