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