summaryrefslogtreecommitdiff
path: root/source3/client
diff options
context:
space:
mode:
Diffstat (limited to 'source3/client')
-rw-r--r--source3/client/client.c4534
-rw-r--r--source3/client/clitar.c1713
2 files changed, 6247 insertions, 0 deletions
diff --git a/source3/client/client.c b/source3/client/client.c
new file mode 100644
index 0000000000..504cb5a0bb
--- /dev/null
+++ b/source3/client/client.c
@@ -0,0 +1,4534 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifdef SYSLOG
+#undef SYSLOG
+#endif
+
+#include "includes.h"
+#include "nameserv.h"
+
+#ifndef REGISTER
+#define REGISTER 0
+#endif
+
+pstring cur_dir = "\\";
+pstring cd_path = "";
+pstring service="";
+pstring desthost="";
+pstring myname = "";
+pstring password = "";
+pstring username="";
+pstring workgroup=WORKGROUP;
+BOOL got_pass = False;
+BOOL connect_as_printer = False;
+BOOL connect_as_ipc = False;
+extern struct in_addr bcast_ip;
+static BOOL got_bcast=False;
+
+char cryptkey[8];
+BOOL doencrypt=False;
+
+extern pstring user_socket_options;
+
+/* 30 second timeout on most commands */
+#define CLIENT_TIMEOUT (30*1000)
+#define SHORT_TIMEOUT (5*1000)
+
+/* value for unused fid field in trans2 secondary request */
+#define FID_UNUSED (0xFFFF)
+
+int name_type = 0x20;
+
+int max_protocol = PROTOCOL_NT1;
+
+
+time_t newer_than = 0;
+int archive_level = 0;
+
+extern struct in_addr myip;
+
+extern pstring debugf;
+extern int DEBUGLEVEL;
+
+BOOL translation = False;
+
+/* clitar bits insert */
+extern void cmd_tar();
+extern void cmd_block();
+extern void cmd_tarmode();
+extern void cmd_setmode();
+extern int blocksize;
+extern BOOL tar_inc;
+extern BOOL tar_reset;
+extern int process_tar();
+extern int tar_parseargs();
+/* clitar bits end */
+
+
+int cnum = 0;
+int pid = 0;
+int gid = 0;
+int uid = 0;
+int mid = 0;
+int myumask = 0755;
+
+int max_xmit = BUFFER_SIZE;
+
+extern pstring scope;
+
+BOOL prompt = True;
+
+int printmode = 1;
+
+BOOL recurse = False;
+BOOL lowercase = False;
+
+BOOL have_ip = False;
+
+struct in_addr dest_ip;
+
+#define SEPARATORS " \t\n\r"
+
+BOOL abort_mget = True;
+
+extern int Protocol;
+
+BOOL readbraw_supported = False;
+BOOL writebraw_supported = False;
+
+pstring fileselection = "";
+
+extern file_info def_finfo;
+
+/* timing globals */
+int get_total_size = 0;
+int get_total_time_ms = 0;
+int put_total_size = 0;
+int put_total_time_ms = 0;
+
+
+extern int Client;
+
+#define USENMB
+
+#ifdef KANJI
+extern int coding_system;
+#define CNV_LANG(s) (coding_system == DOSV_CODE?s:dos_to_unix(s, False))
+#define CNV_INPUT(s) (coding_system == DOSV_CODE?s:unix_to_dos(s, True))
+static BOOL
+setup_term_code (char *code)
+{
+ int new;
+ new = interpret_coding_system (code, UNKNOWN_CODE);
+ if (new != UNKNOWN_CODE) {
+ coding_system = new;
+ return True;
+ }
+ return False;
+}
+#else
+#define CNV_LANG(s) dos2unix_format(s,False)
+#define CNV_INPUT(s) unix2dos_format(s,True)
+#endif
+
+static void send_logout(void );
+BOOL reopen_connection(char *inbuf,char *outbuf);
+static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+static BOOL call_api(int prcnt,int drcnt,int mprcnt,int mdrcnt,
+ int *rprcnt,int *rdrcnt,char *param,char *data,
+ char **rparam,char **rdata);
+static BOOL send_trans_request(char *outbuf,int trans,
+ char *name,int fid,int flags,
+ char *data,char *param,uint16 *setup,
+ int ldata,int lparam,int lsetup,
+ int mdata,int mparam,int msetup);
+
+
+/****************************************************************************
+setup basics in a outgoing packet
+****************************************************************************/
+void setup_pkt(char *outbuf)
+{
+ SSVAL(outbuf,smb_pid,pid);
+ SSVAL(outbuf,smb_uid,uid);
+ SSVAL(outbuf,smb_mid,mid);
+ if (Protocol > PROTOCOL_CORE)
+ {
+ SCVAL(outbuf,smb_flg,0x8);
+ SSVAL(outbuf,smb_flg2,0x1);
+ }
+}
+
+/****************************************************************************
+write to a local file with CR/LF->LF translation if appropriate. return the
+number taken from the buffer. This may not equal the number written.
+****************************************************************************/
+static int writefile(int f, char *b, int n)
+{
+ int i;
+
+ if (!translation)
+ return(write(f,b,n));
+
+ i = 0;
+ while (i < n)
+ {
+ if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n')
+ {
+ b++;i++;
+ }
+ if (write(f, b, 1) != 1)
+ {
+ break;
+ }
+ b++;
+ i++;
+ }
+
+ return(i);
+}
+
+/****************************************************************************
+ read from a file with LF->CR/LF translation if appropriate. return the
+ number read. read approx n bytes.
+****************************************************************************/
+static int readfile(char *b, int size, int n, FILE *f)
+{
+ int i;
+ int c;
+
+ if (!translation || (size != 1))
+ return(fread(b,size,n,f));
+
+ i = 0;
+ while (i < n)
+ {
+ if ((c = getc(f)) == EOF)
+ {
+ break;
+ }
+
+ if (c == '\n') /* change all LFs to CR/LF */
+ {
+ b[i++] = '\r';
+ n++;
+ }
+
+ b[i++] = c;
+ }
+
+ return(i);
+}
+
+
+/****************************************************************************
+read from a file with print translation. return the number read. read approx n
+bytes.
+****************************************************************************/
+static int printread(FILE *f,char *b,int n)
+{
+ int i;
+
+ i = readfile(b,1, n-1,f);
+#if FORMFEED
+ if (feof(f) && i>0)
+ b[i++] = '\014';
+#endif
+
+ return(i);
+}
+
+/****************************************************************************
+check for existance of a dir
+****************************************************************************/
+static BOOL chkpath(char *path,BOOL report)
+{
+ fstring path2;
+ pstring inbuf,outbuf;
+ char *p;
+
+ strcpy(path2,path);
+ trim_string(path2,NULL,"\\");
+ if (!*path2) *path2 = '\\';
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,4 + strlen(path2),True);
+ SCVAL(outbuf,smb_com,SMBchkpth);
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,path2);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (report && CVAL(inbuf,smb_rcls) != 0)
+ DEBUG(2,("chkpath: %s\n",smb_errstr(inbuf)));
+
+ return(CVAL(inbuf,smb_rcls) == 0);
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+static void send_message(char *inbuf,char *outbuf)
+{
+ int total_len = 0;
+
+ char *p;
+ int grp_id;
+
+ /* send a SMBsendstrt command */
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,0,True);
+ CVAL(outbuf,smb_com) = SMBsendstrt;
+ SSVAL(outbuf,smb_tid,cnum);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,username);
+ p = skip_string(p,1);
+ *p++ = 4;
+ strcpy(p,desthost);
+ p = skip_string(p,1);
+
+ set_message(outbuf,0,PTR_DIFF(p,smb_buf(outbuf)),False);
+
+ send_smb(Client,outbuf);
+
+
+ if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+ {
+ printf("SMBsendstrt failed. (%s)\n",smb_errstr(inbuf));
+ return;
+ }
+
+ grp_id = SVAL(inbuf,smb_vwv0);
+
+ printf("Connected. Type your message, ending it with a Control-D\n");
+
+ while (!feof(stdin) && total_len < 1600)
+ {
+ int maxlen = MIN(1600 - total_len,127);
+ pstring msg;
+ int l=0;
+ int c;
+
+ bzero(msg,smb_size);
+
+ for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++)
+ {
+ if (c == '\n')
+ msg[l++] = '\r';
+ msg[l] = c;
+ }
+
+ CVAL(outbuf,smb_com) = SMBsendtxt;
+
+ set_message(outbuf,1,l+3,True);
+
+ SSVAL(outbuf,smb_vwv0,grp_id);
+
+ p = smb_buf(outbuf);
+ *p = 1;
+ SSVAL(p,1,l);
+ memcpy(p+3,msg,l);
+
+ send_smb(Client,outbuf);
+
+
+ if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+ {
+ printf("SMBsendtxt failed (%s)\n",smb_errstr(inbuf));
+ return;
+ }
+
+ total_len += l;
+ }
+
+ if (total_len >= 1600)
+ printf("the message was truncated to 1600 bytes ");
+ else
+ printf("sent %d bytes ",total_len);
+
+ printf("(status was %d-%d)\n",CVAL(inbuf,smb_rcls),SVAL(inbuf,smb_err));
+
+ CVAL(outbuf,smb_com) = SMBsendend;
+ set_message(outbuf,1,0,False);
+ SSVAL(outbuf,smb_vwv0,grp_id);
+
+ send_smb(Client,outbuf);
+
+
+ if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+ {
+ printf("SMBsendend failed (%s)\n",smb_errstr(inbuf));
+ return;
+ }
+}
+
+
+
+/****************************************************************************
+check the space on a device
+****************************************************************************/
+static void do_dskattr(void)
+{
+ pstring inbuf,outbuf;
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,0,True);
+ CVAL(outbuf,smb_com) = SMBdskattr;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ DEBUG(0,("Error in dskattr: %s\n",smb_errstr(inbuf)));
+
+ DEBUG(0,("\n\t\t%d blocks of size %d. %d blocks available\n",
+ SVAL(inbuf,smb_vwv0),
+ SVAL(inbuf,smb_vwv1)*SVAL(inbuf,smb_vwv2),
+ SVAL(inbuf,smb_vwv3)));
+}
+
+/****************************************************************************
+show cd/pwd
+****************************************************************************/
+static void cmd_pwd(void)
+{
+ DEBUG(0,("Current directory is %s",CNV_LANG(service)));
+ DEBUG(0,("%s\n",CNV_LANG(cur_dir)));
+}
+
+
+/****************************************************************************
+change directory - inner section
+****************************************************************************/
+static void do_cd(char *newdir)
+{
+ char *p = newdir;
+ pstring saved_dir;
+ pstring dname;
+
+ /* Save the current directory in case the
+ new directory is invalid */
+ strcpy(saved_dir, cur_dir);
+ if (*p == '\\')
+ strcpy(cur_dir,p);
+ else
+ strcat(cur_dir,p);
+ if (*(cur_dir+strlen(cur_dir)-1) != '\\') {
+ strcat(cur_dir, "\\");
+ }
+ dos_clean_name(cur_dir);
+ strcpy(dname,cur_dir);
+ strcat(cur_dir,"\\");
+ dos_clean_name(cur_dir);
+
+ if (!strequal(cur_dir,"\\"))
+ if (!chkpath(dname,True))
+ strcpy(cur_dir,saved_dir);
+
+ strcpy(cd_path,cur_dir);
+}
+
+/****************************************************************************
+change directory
+****************************************************************************/
+static void cmd_cd(char *inbuf,char *outbuf)
+{
+ fstring buf;
+
+ if (next_token(NULL,buf,NULL))
+ do_cd(buf);
+ else
+ DEBUG(0,("Current directory is %s\n",CNV_LANG(cur_dir)));
+}
+
+
+/****************************************************************************
+ display info about a file
+ ****************************************************************************/
+static void display_finfo(file_info *finfo)
+{
+ time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+ DEBUG(0,(" %-30s%7.7s%10d %s",
+ CNV_LANG(finfo->name),
+ attrib_string(finfo->mode),
+ finfo->size,
+ asctime(LocalTime(&t,GMT_TO_LOCAL))));
+}
+
+/****************************************************************************
+ do a directory listing, calling fn on each file found
+ ****************************************************************************/
+void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+ DEBUG(5,("do_dir(%s,%x,%s)\n",Mask,attribute,BOOLSTR(recurse_dir)));
+ if (Protocol >= PROTOCOL_LANMAN2)
+ {
+ if (do_long_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir) > 0)
+ return;
+ }
+
+ expand_mask(Mask,False);
+ do_short_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir);
+ return;
+}
+
+/*******************************************************************
+ decide if a file should be operated on
+ ********************************************************************/
+static BOOL do_this_one(file_info *finfo)
+{
+ if (finfo->mode & aDIR) return(True);
+
+ if (newer_than && finfo->mtime < newer_than)
+ return(False);
+
+ if ((archive_level==1 || archive_level==2) && !(finfo->mode & aARCH))
+ return(False);
+
+ return(True);
+}
+
+/****************************************************************************
+interpret a short filename structure
+The length of the structure is returned
+****************************************************************************/
+static int interpret_short_filename(char *p,file_info *finfo)
+{
+ finfo->mode = CVAL(p,21);
+
+ /* this date is converted to GMT by make_unix_date */
+ finfo->ctime = make_unix_date(p+22);
+ finfo->mtime = finfo->atime = finfo->ctime;
+ finfo->size = IVAL(p,26);
+ strcpy(finfo->name,p+30);
+
+ return(DIR_STRUCT_SIZE);
+}
+
+/****************************************************************************
+interpret a long filename structure - this is mostly guesses at the moment
+The length of the structure is returned
+The structure of a long filename depends on the info level. 260 is used
+by NT and 2 is used by OS/2
+****************************************************************************/
+static int interpret_long_filename(int level,char *p,file_info *finfo)
+{
+ if (finfo)
+ memcpy(finfo,&def_finfo,sizeof(*finfo));
+
+ switch (level)
+ {
+ case 1: /* OS/2 understands this */
+ if (finfo)
+ {
+ /* these dates are converted to GMT by make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ strcpy(finfo->name,p+27);
+ }
+ return(28 + CVAL(p,26));
+
+ case 2: /* this is what OS/2 uses mostly */
+ if (finfo)
+ {
+ /* these dates are converted to GMT by make_unix_date */
+ finfo->ctime = make_unix_date2(p+4);
+ finfo->atime = make_unix_date2(p+8);
+ finfo->mtime = make_unix_date2(p+12);
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ strcpy(finfo->name,p+31);
+ }
+ return(32 + CVAL(p,30));
+
+ /* levels 3 and 4 are untested */
+ case 3:
+ if (finfo)
+ {
+ /* these dates are probably like the other ones */
+ finfo->ctime = make_unix_date2(p+8);
+ finfo->atime = make_unix_date2(p+12);
+ finfo->mtime = make_unix_date2(p+16);
+ finfo->size = IVAL(p,20);
+ finfo->mode = CVAL(p,28);
+ strcpy(finfo->name,p+33);
+ }
+ return(SVAL(p,4)+4);
+
+ case 4:
+ if (finfo)
+ {
+ /* these dates are probably like the other ones */
+ finfo->ctime = make_unix_date2(p+8);
+ finfo->atime = make_unix_date2(p+12);
+ finfo->mtime = make_unix_date2(p+16);
+ finfo->size = IVAL(p,20);
+ finfo->mode = CVAL(p,28);
+ strcpy(finfo->name,p+37);
+ }
+ return(SVAL(p,4)+4);
+
+ case 260: /* NT uses this, but also accepts 2 */
+ if (finfo)
+ {
+ int ret = SVAL(p,0);
+ int namelen;
+ p += 4; /* next entry offset */
+ p += 4; /* fileindex */
+
+ /* these dates appear to arrive in a weird way. It seems to
+ be localtime plus the serverzone given in the initial
+ connect. This is GMT when DST is not in effect and one
+ hour from GMT otherwise. Can this really be right??
+
+ I suppose this could be called kludge-GMT. Is is the GMT
+ you get by using the current DST setting on a different
+ localtime. It will be cheap to calculate, I suppose, as
+ no DST tables will be needed */
+
+ finfo->ctime = interpret_long_date(p); p += 8;
+ finfo->atime = interpret_long_date(p); p += 8;
+ finfo->mtime = interpret_long_date(p); p += 8; p += 8;
+ finfo->size = IVAL(p,0); p += 8;
+ p += 8; /* alloc size */
+ finfo->mode = CVAL(p,0); p += 4;
+ namelen = IVAL(p,0); p += 4;
+ p += 4; /* EA size */
+ p += 2; /* short name len? */
+ p += 24; /* short name? */
+ StrnCpy(finfo->name,p,namelen);
+ return(ret);
+ }
+ return(SVAL(p,0));
+ }
+
+ DEBUG(1,("Unknown long filename format %d\n",level));
+ return(SVAL(p,0));
+}
+
+
+
+
+/****************************************************************************
+ act on the files in a dir listing
+ ****************************************************************************/
+static void dir_action(char *inbuf,char *outbuf,int attribute,file_info *finfo,BOOL recurse_dir,void (*fn)(),BOOL longdir)
+{
+
+ if (!((finfo->mode & aDIR) == 0 && *fileselection &&
+ !mask_match(finfo->name,fileselection,False,False)) &&
+ !(recurse_dir && (strequal(finfo->name,".") ||
+ strequal(finfo->name,".."))))
+ {
+ if (recurse_dir && (finfo->mode & aDIR))
+ {
+ pstring mask2;
+ pstring sav_dir;
+ strcpy(sav_dir,cur_dir);
+ strcat(cur_dir,finfo->name);
+ strcat(cur_dir,"\\");
+ strcpy(mask2,cur_dir);
+
+ if (!fn)
+ DEBUG(0,("\n%s\n",CNV_LANG(cur_dir)));
+
+ strcat(mask2,"*");
+
+ if (longdir)
+ do_long_dir(inbuf,outbuf,mask2,attribute,fn,True);
+ else
+ do_dir(inbuf,outbuf,mask2,attribute,fn,True);
+
+ strcpy(cur_dir,sav_dir);
+ }
+ else
+ {
+ if (fn && do_this_one(finfo))
+ fn(finfo);
+ }
+ }
+}
+
+
+/****************************************************************************
+ do a directory listing, calling fn on each file found
+ ****************************************************************************/
+static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+ char *p;
+ int received = 0;
+ BOOL first = True;
+ char status[21];
+ int num_asked = (max_xmit - 100)/DIR_STRUCT_SIZE;
+ int num_received = 0;
+ int i;
+ char *dirlist = NULL;
+ pstring mask;
+ file_info finfo;
+
+ finfo = def_finfo;
+
+ bzero(status,21);
+
+ strcpy(mask,Mask);
+
+ while (1)
+ {
+ bzero(outbuf,smb_size);
+ if (first)
+ set_message(outbuf,2,5 + strlen(mask),True);
+ else
+ set_message(outbuf,2,5 + 21,True);
+
+#if FFIRST
+ if (Protocol >= PROTOCOL_LANMAN1)
+ CVAL(outbuf,smb_com) = SMBffirst;
+ else
+#endif
+ CVAL(outbuf,smb_com) = SMBsearch;
+
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,num_asked);
+ SSVAL(outbuf,smb_vwv1,attribute);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+
+ if (first)
+ strcpy(p,mask);
+ else
+ strcpy(p,"");
+ p += strlen(p) + 1;
+
+ *p++ = 5;
+ if (first)
+ SSVAL(p,0,0);
+ else
+ {
+ SSVAL(p,0,21);
+ p += 2;
+ memcpy(p,status,21);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ received = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(5,("dir received %d\n",received));
+
+ DEBUG(6,("errstr=%s\n",smb_errstr(inbuf)));
+
+ if (received <= 0) break;
+
+ first = False;
+
+ dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+
+ if (!dirlist)
+ return 0;
+
+ p = smb_buf(inbuf) + 3;
+
+ memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
+ p,received*DIR_STRUCT_SIZE);
+
+ memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
+
+ num_received += received;
+
+ if (CVAL(inbuf,smb_rcls) != 0) break;
+ }
+
+#if FFIRST
+ if (!first && Protocol >= PROTOCOL_LANMAN1)
+ {
+ bzero(outbuf,smb_size);
+ CVAL(outbuf,smb_com) = SMBfclose;
+
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+
+ strcpy(p,"");
+ p += strlen(p) + 1;
+
+ *p++ = 5;
+ SSVAL(p,0,21);
+ p += 2;
+ memcpy(p,status,21);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT,False);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ DEBUG(0,("Error closing search: %s\n",smb_errstr(inbuf)));
+ }
+#endif
+
+ if (!fn)
+ for (p=dirlist,i=0;i<num_received;i++)
+ {
+ p += interpret_short_filename(p,&finfo);
+ display_finfo(&finfo);
+ }
+
+ for (p=dirlist,i=0;i<num_received;i++)
+ {
+ p += interpret_short_filename(p,&finfo);
+ dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,False);
+ }
+
+ if (dirlist) free(dirlist);
+ return(num_received);
+}
+
+/****************************************************************************
+ receive a SMB trans or trans2 response allocating the necessary memory
+ ****************************************************************************/
+static BOOL receive_trans_response(char *inbuf,int trans,
+ int *data_len,int *param_len,
+ char **data,char **param)
+{
+ int total_data=0;
+ int total_param=0;
+ int this_data,this_param;
+
+ *data_len = *param_len = 0;
+
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+ show_msg(inbuf);
+
+ /* sanity check */
+ if (CVAL(inbuf,smb_com) != trans)
+ {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com)));
+ return(False);
+ }
+ if (CVAL(inbuf,smb_rcls) != 0)
+ return(False);
+
+ /* parse out the lengths */
+ total_data = SVAL(inbuf,smb_tdrcnt);
+ total_param = SVAL(inbuf,smb_tprcnt);
+
+ /* allocate it */
+ *data = Realloc(*data,total_data);
+ *param = Realloc(*param,total_param);
+
+ while (1)
+ {
+ this_data = SVAL(inbuf,smb_drcnt);
+ this_param = SVAL(inbuf,smb_prcnt);
+ if (this_data)
+ memcpy(*data + SVAL(inbuf,smb_drdisp),
+ smb_base(inbuf) + SVAL(inbuf,smb_droff),
+ this_data);
+ if (this_param)
+ memcpy(*param + SVAL(inbuf,smb_prdisp),
+ smb_base(inbuf) + SVAL(inbuf,smb_proff),
+ this_param);
+ *data_len += this_data;
+ *param_len += this_param;
+
+ /* parse out the total lengths again - they can shrink! */
+ total_data = SVAL(inbuf,smb_tdrcnt);
+ total_param = SVAL(inbuf,smb_tprcnt);
+
+ if (total_data <= *data_len && total_param <= *param_len)
+ break;
+
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+ show_msg(inbuf);
+
+ /* sanity check */
+ if (CVAL(inbuf,smb_com) != trans)
+ {
+ DEBUG(0,("Expected %s response, got command 0x%02x\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com)));
+ return(False);
+ }
+ if (CVAL(inbuf,smb_rcls) != 0)
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+ do a directory listing, calling fn on each file found. Use the TRANSACT2
+ call for long filenames
+ ****************************************************************************/
+static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir)
+{
+ int max_matches = 512;
+ int info_level = Protocol<PROTOCOL_NT1?1:260; /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ char *p;
+ pstring mask;
+ file_info finfo;
+ int i;
+ char *dirlist = NULL;
+ int dirlist_len = 0;
+ int total_received = 0;
+ BOOL First = True;
+ char *resp_data=NULL;
+ char *resp_param=NULL;
+ int resp_data_len = 0;
+ int resp_param_len=0;
+
+ int ff_resume_key = 0;
+ int ff_searchcount=0;
+ int ff_eos=0;
+ int ff_lastname=0;
+ int ff_dir_handle=0;
+ int loop_count = 0;
+
+ uint16 setup;
+ pstring param;
+
+ strcpy(mask,Mask);
+
+ while (ff_eos == 0)
+ {
+ loop_count++;
+ if (loop_count > 200)
+ {
+ DEBUG(0,("ERROR: Looping in FIND_NEXT??\n"));
+ break;
+ }
+
+ if (First)
+ {
+ setup = TRANSACT2_FINDFIRST;
+ SSVAL(param,0,attribute); /* attribute */
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,8+4+2); /* resume required + close on end + continue */
+ SSVAL(param,6,info_level);
+ SIVAL(param,8,0);
+ strcpy(param+12,mask);
+ }
+ else
+ {
+ setup = TRANSACT2_FINDNEXT;
+ SSVAL(param,0,ff_dir_handle);
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,info_level);
+ SIVAL(param,6,ff_resume_key); /* ff_resume_key */
+ SSVAL(param,10,8+4+2); /* resume required + close on end + continue */
+ strcpy(param+12,mask);
+
+ DEBUG(5,("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+ ff_dir_handle,ff_resume_key,ff_lastname,mask));
+ }
+ /* ??? original code added 1 pad byte after param */
+
+ send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0,
+ NULL,param,&setup,
+ 0,12+strlen(mask)+1,1,
+ BUFFER_SIZE,10,0);
+
+ if (!receive_trans_response(inbuf,SMBtrans2,
+ &resp_data_len,&resp_param_len,
+ &resp_data,&resp_param))
+ {
+ DEBUG(3,("FIND%s gave %s\n",First?"FIRST":"NEXT",smb_errstr(inbuf)));
+ break;
+ }
+
+ /* parse out some important return info */
+ p = resp_param;
+ if (First)
+ {
+ ff_dir_handle = SVAL(p,0);
+ ff_searchcount = SVAL(p,2);
+ ff_eos = SVAL(p,4);
+ ff_lastname = SVAL(p,8);
+ }
+ else
+ {
+ ff_searchcount = SVAL(p,0);
+ ff_eos = SVAL(p,2);
+ ff_lastname = SVAL(p,6);
+ }
+
+ if (ff_searchcount == 0)
+ break;
+
+ /* point to the data bytes */
+ p = resp_data;
+
+ /* we might need the lastname for continuations */
+ if (ff_lastname > 0)
+ {
+ switch(info_level)
+ {
+ case 260:
+ ff_resume_key =0;
+ StrnCpy(mask,p+ff_lastname,resp_data_len-ff_lastname);
+ /* strcpy(mask,p+ff_lastname+94); */
+ break;
+ case 1:
+ strcpy(mask,p + ff_lastname + 1);
+ ff_resume_key = 0;
+ break;
+ }
+ }
+ else
+ strcpy(mask,"");
+
+ /* and add them to the dirlist pool */
+ dirlist = Realloc(dirlist,dirlist_len + resp_data_len);
+
+ if (!dirlist)
+ {
+ DEBUG(0,("Failed to expand dirlist\n"));
+ break;
+ }
+
+ /* put in a length for the last entry, to ensure we can chain entries
+ into the next packet */
+ {
+ char *p2;
+ for (p2=p,i=0;i<(ff_searchcount-1);i++)
+ p2 += interpret_long_filename(info_level,p2,NULL);
+ SSVAL(p2,0,resp_data_len - PTR_DIFF(p2,p));
+ }
+
+ /* grab the data for later use */
+ memcpy(dirlist+dirlist_len,p,resp_data_len);
+ dirlist_len += resp_data_len;
+
+ total_received += ff_searchcount;
+
+ if (resp_data) free(resp_data); resp_data = NULL;
+ if (resp_param) free(resp_param); resp_param = NULL;
+
+ DEBUG(3,("received %d entries (eos=%d resume=%d)\n",
+ ff_searchcount,ff_eos,ff_resume_key));
+
+ First = False;
+ }
+
+ if (!fn)
+ for (p=dirlist,i=0;i<total_received;i++)
+ {
+ p += interpret_long_filename(info_level,p,&finfo);
+ display_finfo(&finfo);
+ }
+
+ for (p=dirlist,i=0;i<total_received;i++)
+ {
+ p += interpret_long_filename(info_level,p,&finfo);
+ dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,True);
+ }
+
+ /* free up the dirlist buffer */
+ if (dirlist) free(dirlist);
+ return(total_received);
+}
+
+
+/****************************************************************************
+ get a directory listing
+ ****************************************************************************/
+static void cmd_dir(char *inbuf,char *outbuf)
+{
+ int attribute = aDIR | aSYSTEM | aHIDDEN;
+ pstring mask;
+ fstring buf;
+ char *p=buf;
+
+ strcpy(mask,cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ strcat(mask,"\\");
+
+ if (next_token(NULL,buf,NULL))
+ {
+ if (*p == '\\')
+ strcpy(mask,p);
+ else
+ strcat(mask,p);
+ }
+ else {
+ strcat(mask,"*");
+ }
+
+ do_dir(inbuf,outbuf,mask,attribute,NULL,recurse);
+
+ do_dskattr();
+}
+
+
+
+/****************************************************************************
+ get a file from rname to lname
+ ****************************************************************************/
+static void do_get(char *rname,char *lname,file_info *finfo1)
+{
+ int handle=0,fnum;
+ uint32 nread=0;
+ char *p;
+ BOOL newhandle = False;
+ char *inbuf,*outbuf;
+ file_info finfo;
+ BOOL close_done = False;
+ BOOL ignore_close_error = False;
+ char *dataptr=NULL;
+ int datalen=0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (finfo1)
+ finfo = *finfo1;
+ else
+ finfo = def_finfo;
+
+ if (lowercase)
+ strlower(lname);
+
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,15,1 + strlen(rname),True);
+
+ CVAL(outbuf,smb_com) = SMBopenX;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv2,1);
+ SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4));
+ SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+ SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN);
+ SSVAL(outbuf,smb_vwv8,1);
+
+ p = smb_buf(outbuf);
+ strcpy(p,rname);
+ p = skip_string(p,1);
+
+ /* do a chained openX with a readX? */
+#if 1
+ if (finfo.size > 0)
+ {
+ DEBUG(3,("Chaining readX wth openX\n"));
+ SSVAL(outbuf,smb_vwv0,SMBreadX);
+ SSVAL(outbuf,smb_vwv1,smb_offset(p,outbuf));
+ bzero(p,200);
+ p -= smb_wct;
+ SSVAL(p,smb_wct,10);
+ SSVAL(p,smb_vwv0,0xFF);
+ SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size));
+ SSVAL(p,smb_vwv9,MIN(BUFFER_SIZE,finfo.size));
+ smb_setlen(outbuf,smb_len(outbuf)+11*2+1);
+ }
+#endif
+
+ if(!strcmp(lname,"-"))
+ handle = fileno(stdout);
+ else
+ {
+ handle = creat(lname,0644);
+ newhandle = True;
+ }
+ if (handle < 0)
+ {
+ DEBUG(0,("Error opening local file %s\n",lname));
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ if (CVAL(inbuf,smb_rcls) == ERRSRV &&
+ SVAL(inbuf,smb_err) == ERRnoresource &&
+ reopen_connection(inbuf,outbuf))
+ {
+ do_get(rname,lname,finfo1);
+ return;
+ }
+ DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+ if(newhandle)
+ close(handle);
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ strcpy(finfo.name,rname);
+
+ if (!finfo1)
+ {
+ finfo.mode = SVAL(inbuf,smb_vwv3);
+ /* these times arrive as LOCAL time, using the DST offset
+ corresponding to that time, we convert them to GMT */
+ finfo.mtime = make_unix_date3(inbuf+smb_vwv4);
+ finfo.atime = finfo.ctime = finfo.mtime;
+ finfo.size = IVAL(inbuf,smb_vwv6);
+ }
+
+ DEBUG(3,("file %s attrib 0x%X\n",CNV_LANG(finfo.name),finfo.mode));
+
+ fnum = SVAL(inbuf,smb_vwv2);
+
+ /* we might have got some data from a chained readX */
+ if (SVAL(inbuf,smb_vwv0) == SMBreadX)
+ {
+ p = (smb_base(inbuf)+SVAL(inbuf,smb_vwv1)) - smb_wct;
+ datalen = SVAL(p,smb_vwv5);
+ dataptr = smb_base(inbuf) + SVAL(p,smb_vwv6);
+ }
+ else
+ {
+ dataptr = NULL;
+ datalen = 0;
+ }
+
+
+ DEBUG(2,("getting file %s of size %d bytes as %s ",
+ CNV_LANG(finfo.name),
+ finfo.size,
+ lname));
+
+ while (nread < finfo.size && !close_done)
+ {
+ int method = -1;
+ static BOOL can_chain_close = True;
+
+ p=NULL;
+
+ DEBUG(3,("nread=%d max_xmit=%d fsize=%d\n",nread,max_xmit,finfo.size));
+
+ /* 3 possible read types. readbraw if a large block is required.
+ readX + close if not much left and read if neither is supported */
+
+ /* we might have already read some data from a chained readX */
+ if (dataptr && datalen>0)
+ method=3;
+
+ /* if we can finish now then readX+close */
+ if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) &&
+ ((finfo.size - nread) <
+ (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300))))
+ method = 0;
+
+ /* if we support readraw then use that */
+ if (method<0 && readbraw_supported)
+ method = 1;
+
+ /* if we can then use readX */
+ if (method<0 && (Protocol >= PROTOCOL_LANMAN1))
+ method = 2;
+
+ switch (method)
+ {
+ /* use readX */
+ case 0:
+ case 2:
+ if (method == 0)
+ close_done = True;
+
+ /* use readX + close */
+ bzero(outbuf,smb_size);
+ set_message(outbuf,10,0,True);
+ CVAL(outbuf,smb_com) = SMBreadX;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ if (close_done)
+ {
+ CVAL(outbuf,smb_vwv0) = SMBclose;
+ SSVAL(outbuf,smb_vwv1,smb_offset(smb_buf(outbuf),outbuf));
+ }
+ else
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+
+ SSVAL(outbuf,smb_vwv2,fnum);
+ SIVAL(outbuf,smb_vwv3,nread);
+ SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread));
+ SSVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv7,0);
+ SSVAL(outbuf,smb_vwv9,MIN(BUFFER_SIZE,finfo.size-nread));
+
+ if (close_done)
+ {
+ p = smb_buf(outbuf);
+ bzero(p,9);
+
+ CVAL(p,0) = 3;
+ SSVAL(p,1,fnum);
+ SIVALS(p,3,-1);
+
+ /* now set the total packet length */
+ smb_setlen(outbuf,smb_len(outbuf)+9);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+ break;
+ }
+
+ if (close_done &&
+ SVAL(inbuf,smb_vwv0) != SMBclose)
+ {
+ /* NOTE: WfWg sometimes just ignores the chained
+ command! This seems to break the spec? */
+ DEBUG(3,("Rejected chained close?\n"));
+ close_done = False;
+ can_chain_close = False;
+ ignore_close_error = True;
+ }
+
+ datalen = SVAL(inbuf,smb_vwv5);
+ dataptr = smb_base(inbuf) + SVAL(inbuf,smb_vwv6);
+ break;
+
+ /* use readbraw */
+ case 1:
+ {
+ static int readbraw_size = BUFFER_SIZE;
+
+ extern int Client;
+ bzero(outbuf,smb_size);
+ set_message(outbuf,8,0,True);
+ CVAL(outbuf,smb_com) = SMBreadbraw;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SIVAL(outbuf,smb_vwv1,nread);
+ SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
+ SSVAL(outbuf,smb_vwv4,0);
+ SIVALS(outbuf,smb_vwv5,-1);
+ send_smb(Client,outbuf);
+
+ /* Now read the raw data into the buffer and write it */
+ if(read_smb_length(Client,inbuf,0) == -1) {
+ DEBUG(0,("Failed to read length in readbraw\n"));
+ exit(1);
+ }
+
+ /* Even though this is not an smb message, smb_len
+ returns the generic length of an smb message */
+ datalen = smb_len(inbuf);
+
+ if (datalen == 0)
+ {
+ /* we got a readbraw error */
+ DEBUG(4,("readbraw error - reducing size\n"));
+ readbraw_size = (readbraw_size * 9) / 10;
+
+ if (readbraw_size < max_xmit)
+ {
+ DEBUG(0,("disabling readbraw\n"));
+ readbraw_supported = False;
+ }
+
+ dataptr=NULL;
+ continue;
+ }
+
+ if(read_data(Client,inbuf,datalen) != datalen) {
+ DEBUG(0,("Failed to read data in readbraw\n"));
+ exit(1);
+ }
+ dataptr = inbuf;
+ }
+ break;
+
+ case 3:
+ /* we've already read some data with a chained readX */
+ break;
+
+ default:
+ /* use plain read */
+ bzero(outbuf,smb_size);
+ set_message(outbuf,5,0,True);
+ CVAL(outbuf,smb_com) = SMBread;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
+ SIVAL(outbuf,smb_vwv2,nread);
+ SSVAL(outbuf,smb_vwv4,finfo.size - nread);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+ break;
+ }
+
+ datalen = SVAL(inbuf,smb_vwv0);
+ dataptr = smb_buf(inbuf) + 3;
+ break;
+ }
+
+ if (writefile(handle,dataptr,datalen) != datalen)
+ {
+ DEBUG(0,("Error writing local file\n"));
+ break;
+ }
+
+ nread += datalen;
+ if (datalen == 0)
+ {
+ DEBUG(0,("Error reading file %s. Got %d bytes\n",CNV_LANG(rname),nread));
+ break;
+ }
+
+ dataptr=NULL;
+ datalen=0;
+ }
+
+
+
+ if (!close_done)
+ {
+ bzero(outbuf,smb_size);
+ set_message(outbuf,3,0,True);
+ CVAL(outbuf,smb_com) = SMBclose;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SIVALS(outbuf,smb_vwv1,-1);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf)));
+ if(newhandle)
+ close(handle);
+ free(inbuf);free(outbuf);
+ return;
+ }
+ }
+
+ if(newhandle)
+ close(handle);
+
+ if (archive_level >= 2 && (finfo.mode & aARCH)) {
+ bzero(outbuf,smb_size);
+ set_message(outbuf,8,strlen(rname)+4,True);
+ CVAL(outbuf,smb_com) = SMBsetatr;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+ SSVAL(outbuf,smb_vwv0,finfo.mode & ~(aARCH));
+ SIVALS(outbuf,smb_vwv1,0);
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,rname);
+ p += strlen(p)+1;
+ *p++ = 4;
+ *p = 0;
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+ }
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += finfo.size;
+
+ DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+ finfo.size / (1.024*this_time + 1.0e-4),
+ get_total_size / (1.024*get_total_time_ms)));
+ }
+
+ free(inbuf);free(outbuf);
+}
+
+
+/****************************************************************************
+ get a file
+ ****************************************************************************/
+static void cmd_get(void)
+{
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ strcpy(rname,cur_dir);
+ strcat(rname,"\\");
+
+ p = rname + strlen(rname);
+
+ if (!next_token(NULL,p,NULL)) {
+ DEBUG(0,("get <filename>\n"));
+ return;
+ }
+ strcpy(lname,p);
+ dos_clean_name(rname);
+
+ next_token(NULL,lname,NULL);
+
+ do_get(rname,lname,NULL);
+}
+
+
+/****************************************************************************
+ do a mget operation on one file
+ ****************************************************************************/
+static void do_mget(file_info *finfo)
+{
+ pstring rname;
+ pstring quest;
+
+ if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+ return;
+
+ if (abort_mget)
+ {
+ DEBUG(0,("mget aborted\n"));
+ return;
+ }
+
+ if (finfo->mode & aDIR)
+ sprintf(quest,"Get directory %s? ",CNV_LANG(finfo->name));
+ else
+ sprintf(quest,"Get file %s? ",CNV_LANG(finfo->name));
+
+ if (prompt && !yesno(quest)) return;
+
+ if (finfo->mode & aDIR)
+ {
+ pstring saved_curdir;
+ pstring mget_mask;
+ char *inbuf,*outbuf;
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ strcpy(saved_curdir,cur_dir);
+
+ strcat(cur_dir,finfo->name);
+ strcat(cur_dir,"\\");
+
+ unix_format(finfo->name);
+ {
+ if (lowercase)
+ strlower(finfo->name);
+
+ if (!directory_exist(finfo->name,NULL) &&
+ sys_mkdir(finfo->name,0777) != 0)
+ {
+ DEBUG(0,("failed to create directory %s\n",CNV_LANG(finfo->name)));
+ strcpy(cur_dir,saved_curdir);
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ if (sys_chdir(finfo->name) != 0)
+ {
+ DEBUG(0,("failed to chdir to directory %s\n",CNV_LANG(finfo->name)));
+ strcpy(cur_dir,saved_curdir);
+ free(inbuf);free(outbuf);
+ return;
+ }
+ }
+
+ strcpy(mget_mask,cur_dir);
+ strcat(mget_mask,"*");
+
+ do_dir((char *)inbuf,(char *)outbuf,
+ mget_mask,aSYSTEM | aHIDDEN | aDIR,do_mget,False);
+ chdir("..");
+ strcpy(cur_dir,saved_curdir);
+ free(inbuf);free(outbuf);
+ }
+ else
+ {
+ strcpy(rname,cur_dir);
+ strcat(rname,finfo->name);
+ do_get(rname,finfo->name,finfo);
+ }
+}
+
+/****************************************************************************
+view the file using the pager
+****************************************************************************/
+static void cmd_more(void)
+{
+ fstring rname,lname,tmpname,pager_cmd;
+ char *pager;
+
+ strcpy(rname,cur_dir);
+ strcat(rname,"\\");
+ sprintf(tmpname,"/tmp/smbmore.%d",getpid());
+ strcpy(lname,tmpname);
+
+ if (!next_token(NULL,rname+strlen(rname),NULL)) {
+ DEBUG(0,("more <filename>\n"));
+ return;
+ }
+ dos_clean_name(rname);
+
+ do_get(rname,lname,NULL);
+
+ pager=getenv("PAGER");
+ sprintf(pager_cmd,"%s %s",(pager? pager:PAGER), tmpname);
+ system(pager_cmd);
+ unlink(tmpname);
+}
+
+
+
+/****************************************************************************
+do a mget command
+****************************************************************************/
+static void cmd_mget(char *inbuf,char *outbuf)
+{
+ int attribute = aSYSTEM | aHIDDEN;
+ pstring mget_mask;
+ fstring buf;
+ char *p=buf;
+
+ *mget_mask = 0;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ abort_mget = False;
+
+ while (next_token(NULL,p,NULL))
+ {
+ strcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ strcat(mget_mask,"\\");
+
+ if (*p == '\\')
+ strcpy(mget_mask,p);
+ else
+ strcat(mget_mask,p);
+ do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False);
+ }
+
+ if (! *mget_mask)
+ {
+ strcpy(mget_mask,cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ strcat(mget_mask,"\\");
+ strcat(mget_mask,"*");
+ do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False);
+ }
+}
+
+/****************************************************************************
+make a directory of name "name"
+****************************************************************************/
+static BOOL do_mkdir(char *name)
+{
+ char *p;
+ char *inbuf,*outbuf;
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return False;
+ }
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,2 + strlen(name),True);
+
+ CVAL(outbuf,smb_com) = SMBmkdir;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,name);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s making remote directory %s\n",
+ smb_errstr(inbuf),CNV_LANG(name)));
+
+ free(inbuf);free(outbuf);
+ return(False);
+ }
+
+ free(inbuf);free(outbuf);
+ return(True);
+}
+
+
+/****************************************************************************
+ make a directory
+ ****************************************************************************/
+static void cmd_mkdir(char *inbuf,char *outbuf)
+{
+ pstring mask;
+ fstring buf;
+ char *p=buf;
+
+ strcpy(mask,cur_dir);
+
+ if (!next_token(NULL,p,NULL))
+ {
+ if (!recurse)
+ DEBUG(0,("mkdir <dirname>\n"));
+ return;
+ }
+ strcat(mask,p);
+
+ if (recurse)
+ {
+ pstring ddir;
+ pstring ddir2;
+ *ddir2 = 0;
+
+ strcpy(ddir,mask);
+ trim_string(ddir,".",NULL);
+ p = strtok(ddir,"/\\");
+ while (p)
+ {
+ strcat(ddir2,p);
+ if (!chkpath(ddir2,False))
+ {
+ do_mkdir(ddir2);
+ }
+ strcat(ddir2,"\\");
+ p = strtok(NULL,"/\\");
+ }
+ }
+ else
+ do_mkdir(mask);
+}
+
+
+/*******************************************************************
+ write to a file using writebraw
+ ********************************************************************/
+static int smb_writeraw(char *outbuf,int fnum,int pos,char *buf,int n)
+{
+ extern int Client;
+ pstring inbuf;
+
+ bzero(outbuf,smb_size);
+ bzero(inbuf,smb_size);
+ set_message(outbuf,Protocol>PROTOCOL_COREPLUS?12:10,0,True);
+
+ CVAL(outbuf,smb_com) = SMBwritebraw;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,n);
+ SIVAL(outbuf,smb_vwv3,pos);
+ SSVAL(outbuf,smb_vwv7,1);
+
+ send_smb(Client,outbuf);
+
+ if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+ return(0);
+
+ _smb_setlen(buf-4,n); /* HACK! XXXX */
+
+ if (write_socket(Client,buf-4,n+4) != n+4)
+ return(0);
+
+ if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) {
+ DEBUG(0,("Error writing remote file (2)\n"));
+ return(0);
+ }
+ return(SVAL(inbuf,smb_vwv0));
+}
+
+
+
+/*******************************************************************
+ write to a file
+ ********************************************************************/
+static int smb_writefile(char *outbuf,int fnum,int pos,char *buf,int n)
+{
+ pstring inbuf;
+
+ if (writebraw_supported && n > (max_xmit-200))
+ return(smb_writeraw(outbuf,fnum,pos,buf,n));
+
+ bzero(outbuf,smb_size);
+ bzero(inbuf,smb_size);
+ set_message(outbuf,5,n + 3,True);
+
+ CVAL(outbuf,smb_com) = SMBwrite;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,n);
+ SIVAL(outbuf,smb_vwv2,pos);
+ SSVAL(outbuf,smb_vwv4,0);
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,n);
+
+ memcpy(smb_buf(outbuf)+3,buf,n);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0) {
+ DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf)));
+ return(0);
+ }
+ return(SVAL(inbuf,smb_vwv0));
+}
+
+
+
+/****************************************************************************
+ put a single file
+ ****************************************************************************/
+static void do_put(char *rname,char *lname,file_info *finfo)
+{
+ int fnum;
+ FILE *f;
+ int nread=0;
+ char *p;
+ char *inbuf,*outbuf;
+ time_t close_time = finfo->mtime;
+ char *buf=NULL;
+ static int maxwrite=0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,3,2 + strlen(rname),True);
+
+ if (finfo->mtime == 0 || finfo->mtime == -1)
+ finfo->mtime = finfo->atime = finfo->ctime = time(NULL);
+
+ CVAL(outbuf,smb_com) = SMBcreate;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,finfo->mode);
+ put_dos_date3(outbuf,smb_vwv1,finfo->mtime);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,rname);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+
+ free(inbuf);free(outbuf);if (buf) free(buf);
+ return;
+ }
+
+ f = fopen(lname,"r");
+
+ if (!f)
+ {
+ DEBUG(0,("Error opening local file %s\n",lname));
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+
+ fnum = SVAL(inbuf,smb_vwv0);
+ if (finfo->size < 0)
+ finfo->size = file_size(lname);
+
+ DEBUG(1,("putting file %s of size %d bytes as %s ",lname,finfo->size,CNV_LANG(rname)));
+
+ if (!maxwrite)
+ maxwrite = writebraw_supported?MAX(max_xmit,BUFFER_SIZE):(max_xmit-200);
+
+ while (nread < finfo->size)
+ {
+ int n = maxwrite;
+ int ret;
+
+ n = MIN(n,finfo->size - nread);
+
+ buf = (char *)Realloc(buf,n+4);
+
+ fseek(f,nread,SEEK_SET);
+ if ((n = readfile(buf+4,1,n,f)) < 1)
+ {
+ DEBUG(0,("Error reading local file\n"));
+ break;
+ }
+
+ ret = smb_writefile(outbuf,fnum,nread,buf+4,n);
+
+ if (n != ret) {
+ if (!maxwrite) {
+ DEBUG(0,("Error writing file\n"));
+ break;
+ } else {
+ maxwrite /= 2;
+ continue;
+ }
+ }
+
+ nread += n;
+ }
+
+
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,3,0,True);
+ CVAL(outbuf,smb_com) = SMBclose;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ put_dos_date3(outbuf,smb_vwv1,close_time);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+ fclose(f);
+ free(inbuf);free(outbuf);
+ if (buf) free(buf);
+ return;
+ }
+
+
+ fclose(f);
+ free(inbuf);free(outbuf);
+ if (buf) free(buf);
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ put_total_time_ms += this_time;
+ put_total_size += finfo->size;
+
+ DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+ finfo->size / (1.024*this_time + 1.0e-4),
+ put_total_size / (1.024*put_total_time_ms)));
+ }
+}
+
+
+
+/****************************************************************************
+ put a file
+ ****************************************************************************/
+static void cmd_put(void)
+{
+ pstring lname;
+ pstring rname;
+ fstring buf;
+ char *p=buf;
+ file_info finfo;
+ finfo = def_finfo;
+
+ strcpy(rname,cur_dir);
+ strcat(rname,"\\");
+
+
+ if (!next_token(NULL,p,NULL))
+ {
+ DEBUG(0,("put <filename>\n"));
+ return;
+ }
+ strcpy(lname,p);
+
+ if (next_token(NULL,p,NULL))
+ strcat(rname,p);
+ else
+ strcat(rname,lname);
+
+ dos_clean_name(rname);
+
+ {
+ struct stat st;
+ if (!file_exist(lname,&st)) {
+ DEBUG(0,("%s does not exist\n",lname));
+ return;
+ }
+ finfo.mtime = st.st_mtime;
+ }
+
+ do_put(rname,lname,&finfo);
+}
+
+/****************************************************************************
+ seek in a directory/file list until you get something that doesn't start with
+ the specified name
+ ****************************************************************************/
+static BOOL seek_list(FILE *f,char *name)
+{
+ pstring s;
+ while (!feof(f))
+ {
+ if (fscanf(f,"%s",s) != 1) return(False);
+ trim_string(s,"./",NULL);
+ if (strncmp(s,name,strlen(name)) != 0)
+ {
+ strcpy(name,s);
+ return(True);
+ }
+ }
+
+ return(False);
+}
+
+
+/****************************************************************************
+ set the file selection mask
+ ****************************************************************************/
+static void cmd_select(void)
+{
+ strcpy(fileselection,"");
+ next_token(NULL,fileselection,NULL);
+}
+
+
+/****************************************************************************
+ mput some files
+ ****************************************************************************/
+static void cmd_mput(void)
+{
+ pstring lname;
+ pstring rname;
+ file_info finfo;
+ fstring buf;
+ char *p=buf;
+
+ finfo = def_finfo;
+
+
+ while (next_token(NULL,p,NULL))
+ {
+ struct stat st;
+ pstring cmd;
+ pstring tmpname;
+ FILE *f;
+
+ sprintf(tmpname,"/tmp/ls.smb.%d",(int)getpid());
+ if (recurse)
+ sprintf(cmd,"find . -name \"%s\" -print > %s",p,tmpname);
+ else
+ sprintf(cmd,"/bin/ls %s > %s",p,tmpname);
+ system(cmd);
+
+ f = fopen(tmpname,"r");
+ if (!f) continue;
+
+ while (!feof(f))
+ {
+ pstring quest;
+
+ if (fscanf(f,"%s",lname) != 1) break;
+ trim_string(lname,"./",NULL);
+
+ again1:
+
+ /* check if it's a directory */
+ if (directory_exist(lname,&st))
+ {
+ if (!recurse) continue;
+ sprintf(quest,"Put directory %s? ",lname);
+ if (prompt && !yesno(quest))
+ {
+ strcat(lname,"/");
+ if (!seek_list(f,lname))
+ break;
+ goto again1;
+ }
+
+ strcpy(rname,cur_dir);
+ strcat(rname,lname);
+ if (!do_mkdir(rname))
+ {
+ strcat(lname,"/");
+ if (!seek_list(f,lname))
+ break;
+ goto again1;
+ }
+
+ continue;
+ }
+ else
+ {
+ sprintf(quest,"Put file %s? ",lname);
+ if (prompt && !yesno(quest)) continue;
+
+ strcpy(rname,cur_dir);
+ strcat(rname,lname);
+ }
+ dos_format(rname);
+
+ /* null size so do_put knows to ignore it */
+ finfo.size = -1;
+
+ /* set the date on the file */
+ finfo.mtime = st.st_mtime;
+
+ do_put(rname,lname,&finfo);
+ }
+ fclose(f);
+ unlink(tmpname);
+ }
+}
+
+/****************************************************************************
+ cancel a print job
+ ****************************************************************************/
+static void do_cancel(int job)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ int rdrcnt,rprcnt;
+ pstring param;
+
+ bzero(param,sizeof(param));
+
+ p = param;
+ SSVAL(p,0,81); /* api number */
+ p += 2;
+ strcpy(p,"W");
+ p = skip_string(p,1);
+ strcpy(p,"");
+ p = skip_string(p,1);
+ SSVAL(p,0,job);
+ p += 2;
+
+ if (call_api(PTR_DIFF(p,param),0,
+ 6,1000,
+ &rprcnt,&rdrcnt,
+ param,NULL,
+ &rparam,&rdata))
+ {
+ int res = SVAL(rparam,0);
+
+ if (!res)
+ printf("Job %d cancelled\n",job);
+ else
+ printf("Error %d calcelling job %d\n",res,job);
+ return;
+ }
+ else
+ printf("Server refused cancel request\n");
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return;
+}
+
+
+/****************************************************************************
+ cancel a print job
+ ****************************************************************************/
+static void cmd_cancel(char *inbuf,char *outbuf )
+{
+ fstring buf;
+ int job;
+
+ if (!connect_as_printer)
+ {
+ DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n"));
+ DEBUG(0,("Trying to cancel print jobs without -P may fail\n"));
+ }
+
+ if (!next_token(NULL,buf,NULL)) {
+ printf("cancel <jobid> ...\n");
+ return;
+ }
+ do {
+ job = atoi(buf);
+ do_cancel(job);
+ } while (next_token(NULL,buf,NULL));
+}
+
+
+/****************************************************************************
+ get info on a file
+ ****************************************************************************/
+static void cmd_stat(char *inbuf,char *outbuf)
+{
+ fstring buf;
+ pstring param;
+ char *resp_data=NULL;
+ char *resp_param=NULL;
+ int resp_data_len = 0;
+ int resp_param_len=0;
+ char *p;
+ uint16 setup = TRANSACT2_QPATHINFO;
+
+ if (!next_token(NULL,buf,NULL)) {
+ printf("stat <file>\n");
+ return;
+ }
+
+ bzero(param,6);
+ SSVAL(param,0,4); /* level */
+ p = param+6;
+ strcpy(p,cur_dir);
+ strcat(p,buf);
+
+ send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0,
+ NULL,param,&setup,
+ 0,6 + strlen(p)+1,1,
+ BUFFER_SIZE,2,0);
+
+ receive_trans_response(inbuf,SMBtrans2,
+ &resp_data_len,&resp_param_len,
+ &resp_data,&resp_param);
+
+ if (resp_data) free(resp_data); resp_data = NULL;
+ if (resp_param) free(resp_param); resp_param = NULL;
+}
+
+
+/****************************************************************************
+ print a file
+ ****************************************************************************/
+static void cmd_print(char *inbuf,char *outbuf )
+{
+ int fnum;
+ FILE *f = NULL;
+ uint32 nread=0;
+ pstring lname;
+ pstring rname;
+ char *p;
+
+ if (!connect_as_printer)
+ {
+ DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n"));
+ DEBUG(0,("Trying to print without -P may fail\n"));
+ }
+
+ if (!next_token(NULL,lname,NULL))
+ {
+ DEBUG(0,("print <filename>\n"));
+ return;
+ }
+
+ strcpy(rname,lname);
+ p = strrchr(rname,'/');
+ if (p)
+ {
+ pstring tname;
+ strcpy(tname,p+1);
+ strcpy(rname,tname);
+ }
+
+ if ((int)strlen(rname) > 14)
+ rname[14] = 0;
+
+ if (strequal(lname,"-"))
+ {
+ f = stdin;
+ strcpy(rname,"stdin");
+ }
+
+ dos_clean_name(rname);
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,2,2 + strlen(rname),True);
+
+ CVAL(outbuf,smb_com) = SMBsplopen;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,0);
+ SSVAL(outbuf,smb_vwv1,printmode);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,rname);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s opening printer for %s\n",smb_errstr(inbuf),CNV_LANG(rname)));
+ return;
+ }
+
+ if (!f)
+ f = fopen(lname,"r");
+ if (!f)
+ {
+ DEBUG(0,("Error opening local file %s\n",lname));
+ return;
+ }
+
+
+ fnum = SVAL(inbuf,smb_vwv0);
+
+ DEBUG(1,("printing file %s as %s\n",lname,CNV_LANG(rname)));
+
+ while (!feof(f))
+ {
+ int n;
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,1,3,True);
+
+ /* for some strange reason the OS/2 print server can't handle large
+ packets when printing. weird */
+ n = MIN(1024,max_xmit-(smb_len(outbuf)+4));
+
+ if (translation)
+ n = printread(f,smb_buf(outbuf)+3,(int)(0.95*n));
+ else
+ n = readfile(smb_buf(outbuf)+3,1,n,f);
+ if (n <= 0)
+ {
+ DEBUG(0,("read gave %d\n",n));
+ break;
+ }
+
+ smb_setlen(outbuf,smb_len(outbuf) + n);
+
+ CVAL(outbuf,smb_com) = SMBsplwr;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,n+3);
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,n);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s printing remote file\n",smb_errstr(inbuf)));
+ break;
+ }
+
+ nread += n;
+ }
+
+ DEBUG(2,("%d bytes printed\n",nread));
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,1,0,True);
+ CVAL(outbuf,smb_com) = SMBsplclose;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s closing print file\n",smb_errstr(inbuf)));
+ if (f != stdin)
+ fclose(f);
+ return;
+ }
+
+ if (f != stdin)
+ fclose(f);
+}
+
+/****************************************************************************
+print a file
+****************************************************************************/
+static void cmd_queue(char *inbuf,char *outbuf )
+{
+ int count;
+ char *p;
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,2,0,True);
+
+ CVAL(outbuf,smb_com) = SMBsplretq;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,32); /* a max of 20 entries is to be shown */
+ SSVAL(outbuf,smb_vwv1,0); /* the index into the queue */
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s obtaining print queue\n",smb_errstr(inbuf)));
+ return;
+ }
+
+ count = SVAL(inbuf,smb_vwv0);
+ p = smb_buf(inbuf) + 3;
+ if (count <= 0)
+ {
+ DEBUG(0,("No entries in the print queue\n"));
+ return;
+ }
+
+ {
+ char status[20];
+
+ DEBUG(0,("Job Name Size Status\n"));
+
+ while (count--)
+ {
+ switch (CVAL(p,4))
+ {
+ case 0x01: sprintf(status,"held or stopped"); break;
+ case 0x02: sprintf(status,"printing"); break;
+ case 0x03: sprintf(status,"awaiting print"); break;
+ case 0x04: sprintf(status,"in intercept"); break;
+ case 0x05: sprintf(status,"file had error"); break;
+ case 0x06: sprintf(status,"printer error"); break;
+ default: sprintf(status,"unknown"); break;
+ }
+
+ DEBUG(0,("%-6d %-16.16s %-9d %s\n",
+ SVAL(p,5),p+12,IVAL(p,7),status));
+ p += 28;
+ }
+ }
+
+}
+
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static void do_del(file_info *finfo)
+{
+ char *p;
+ char *inbuf,*outbuf;
+ pstring mask;
+
+ strcpy(mask,cur_dir);
+ strcat(mask,finfo->name);
+
+ if (finfo->mode & aDIR)
+ return;
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,1,2 + strlen(mask),True);
+
+ CVAL(outbuf,smb_com) = SMBunlink;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,0);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,mask);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ DEBUG(0,("%s deleting remote file %s\n",smb_errstr(inbuf),CNV_LANG(mask)));
+
+ free(inbuf);free(outbuf);
+
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static void cmd_del(char *inbuf,char *outbuf )
+{
+ pstring mask;
+ fstring buf;
+ int attribute = aSYSTEM | aHIDDEN;
+
+ if (recurse)
+ attribute |= aDIR;
+
+ strcpy(mask,cur_dir);
+
+ if (!next_token(NULL,buf,NULL))
+ {
+ DEBUG(0,("del <filename>\n"));
+ return;
+ }
+ strcat(mask,buf);
+
+ do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_del,False);
+}
+
+
+/****************************************************************************
+remove a directory
+****************************************************************************/
+static void cmd_rmdir(char *inbuf,char *outbuf )
+{
+ pstring mask;
+ fstring buf;
+ char *p;
+
+ strcpy(mask,cur_dir);
+
+ if (!next_token(NULL,buf,NULL))
+ {
+ DEBUG(0,("rmdir <dirname>\n"));
+ return;
+ }
+ strcat(mask,buf);
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,2 + strlen(mask),True);
+
+ CVAL(outbuf,smb_com) = SMBrmdir;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,mask);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s removing remote directory file %s\n",smb_errstr(inbuf),CNV_LANG(mask)));
+ return;
+ }
+
+}
+
+/****************************************************************************
+rename some files
+****************************************************************************/
+static void cmd_rename(char *inbuf,char *outbuf )
+{
+ pstring src,dest;
+ fstring buf,buf2;
+ char *p;
+
+ strcpy(src,cur_dir);
+ strcpy(dest,cur_dir);
+
+ if (!next_token(NULL,buf,NULL) || !next_token(NULL,buf2,NULL))
+ {
+ DEBUG(0,("rename <src> <dest>\n"));
+ return;
+ }
+ strcat(src,buf);
+ strcat(dest,buf2);
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,1,4 + strlen(src) + strlen(dest),True);
+
+ CVAL(outbuf,smb_com) = SMBmv;
+ SSVAL(outbuf,smb_tid,cnum);
+ SSVAL(outbuf,smb_vwv0,aHIDDEN | aDIR | aSYSTEM);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,src);
+ p = skip_string(p,1);
+ *p++ = 4;
+ strcpy(p,dest);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s renaming files\n",smb_errstr(inbuf)));
+ return;
+ }
+
+}
+
+
+/****************************************************************************
+toggle the prompt flag
+****************************************************************************/
+static void cmd_prompt(void)
+{
+ prompt = !prompt;
+ DEBUG(2,("prompting is now %s\n",prompt?"on":"off"));
+}
+
+
+/****************************************************************************
+set the newer than time
+****************************************************************************/
+static void cmd_newer(void)
+{
+ fstring buf;
+ BOOL ok;
+ struct stat sbuf;
+
+ ok = next_token(NULL,buf,NULL);
+ if (ok && (sys_stat(buf,&sbuf) == 0))
+ {
+ newer_than = sbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than,GMT_TO_LOCAL))));
+ }
+ else
+ newer_than = 0;
+
+ if (ok && newer_than == 0)
+ DEBUG(0,("Error setting newer-than time\n"));
+}
+
+/****************************************************************************
+set the archive level
+****************************************************************************/
+static void cmd_archive(void)
+{
+ fstring buf;
+
+ if (next_token(NULL,buf,NULL)) {
+ archive_level = atoi(buf);
+ } else
+ DEBUG(0,("Archive level is %d\n",archive_level));
+}
+
+/****************************************************************************
+toggle the lowercaseflag
+****************************************************************************/
+static void cmd_lowercase(void)
+{
+ lowercase = !lowercase;
+ DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off"));
+}
+
+
+
+
+/****************************************************************************
+toggle the recurse flag
+****************************************************************************/
+static void cmd_recurse(void)
+{
+ recurse = !recurse;
+ DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off"));
+}
+
+/****************************************************************************
+toggle the translate flag
+****************************************************************************/
+static void cmd_translate(void)
+{
+ translation = !translation;
+ DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+ translation?"on":"off"));
+}
+
+
+/****************************************************************************
+do a printmode command
+****************************************************************************/
+static void cmd_printmode(void)
+{
+ fstring buf;
+ fstring mode;
+
+ if (next_token(NULL,buf,NULL))
+ {
+ if (strequal(buf,"text"))
+ printmode = 0;
+ else
+ {
+ if (strequal(buf,"graphics"))
+ printmode = 1;
+ else
+ printmode = atoi(buf);
+ }
+ }
+
+ switch(printmode)
+ {
+ case 0:
+ strcpy(mode,"text");
+ break;
+ case 1:
+ strcpy(mode,"graphics");
+ break;
+ default:
+ sprintf(mode,"%d",printmode);
+ break;
+ }
+
+ DEBUG(2,("the printmode is now %s\n",mode));
+}
+
+/****************************************************************************
+do the lcd command
+****************************************************************************/
+static void cmd_lcd(void)
+{
+ fstring buf;
+ pstring d;
+
+ if (next_token(NULL,buf,NULL))
+ sys_chdir(buf);
+ DEBUG(2,("the local directory is now %s\n",GetWd(d)));
+}
+
+
+/****************************************************************************
+send a session request
+****************************************************************************/
+static BOOL send_session_request(char *inbuf,char *outbuf)
+{
+ fstring dest;
+ char *p;
+ int len = 4;
+ /* send a session request (RFC 8002) */
+
+ strcpy(dest,desthost);
+ p = strchr(dest,'.');
+ if (p) *p = 0;
+
+ /* put in the destination name */
+ p = outbuf+len;
+ name_mangle(dest,p,name_type);
+ len += name_len(p);
+
+ /* and my name */
+ p = outbuf+len;
+ name_mangle(myname,p,0);
+ len += name_len(p);
+
+ /* setup the packet length */
+ _smb_setlen(outbuf,len);
+ CVAL(outbuf,0) = 0x81;
+
+ send_smb(Client,outbuf);
+ DEBUG(5,("Sent session request\n"));
+
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,0) == 0x84) /* C. Hoch 9/14/95 Start */
+ {
+ /* For information, here is the response structure.
+ * We do the byte-twiddling to for portability.
+ struct RetargetResponse{
+ unsigned char type;
+ unsigned char flags;
+ int16 length;
+ int32 ip_addr;
+ int16 port;
+ };
+ */
+ extern int Client;
+ int port = (CVAL(inbuf,8)<<8)+CVAL(inbuf,9);
+ /* SESSION RETARGET */
+ putip((char *)&dest_ip,inbuf+4);
+
+ close_sockets();
+ Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+ if (Client == -1)
+ return False;
+
+ DEBUG(3,("Retargeted\n"));
+
+ set_socket_options(Client,user_socket_options);
+
+ /* Try again */
+ return send_session_request(inbuf,outbuf);
+ } /* C. Hoch 9/14/95 End */
+
+
+ if (CVAL(inbuf,0) != 0x82)
+ {
+ int ecode = CVAL(inbuf,4);
+ DEBUG(0,("Session request failed (%d,%d) with myname=%s destname=%s\n",
+ CVAL(inbuf,0),ecode,myname,desthost));
+ switch (ecode)
+ {
+ case 0x80:
+ DEBUG(0,("Not listening on called name\n"));
+ DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost));
+ DEBUG(0,("You may find the -I option useful for this\n"));
+ break;
+ case 0x81:
+ DEBUG(0,("Not listening for calling name\n"));
+ DEBUG(0,("Try to connect as another name (instead of %s)\n",myname));
+ DEBUG(0,("You may find the -n option useful for this\n"));
+ break;
+ case 0x82:
+ DEBUG(0,("Called name not present\n"));
+ DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost));
+ DEBUG(0,("You may find the -I option useful for this\n"));
+ break;
+ case 0x83:
+ DEBUG(0,("Called name present, but insufficient resources\n"));
+ DEBUG(0,("Perhaps you should try again later?\n"));
+ break;
+ default:
+ DEBUG(0,("Unspecified error 0x%X\n",ecode));
+ DEBUG(0,("Your server software is being unfriendly\n"));
+ break;
+ }
+ return(False);
+ }
+ return(True);
+}
+
+
+/****************************************************************************
+send a login command
+****************************************************************************/
+static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setup)
+{
+ BOOL was_null = (!inbuf && !outbuf);
+ int sesskey=0;
+ time_t servertime = 0;
+ extern int serverzone;
+ int sec_mode=0;
+ int crypt_len;
+ int max_vcs=0;
+ struct {
+ int prot;
+ char *name;
+ }
+ prots[] =
+ {
+ {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+ {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1,"LANMAN1.0"},
+ {PROTOCOL_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {-1,NULL}
+ };
+ char *pass = NULL;
+ pstring dev;
+ char *p;
+ int numprots;
+
+ if (was_null)
+ {
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ }
+
+#if AJT
+ if (strstr(service,"IPC$")) connect_as_ipc = True;
+#endif
+
+ strcpy(dev,"A:");
+ if (connect_as_printer)
+ strcpy(dev,"LPT1:");
+ if (connect_as_ipc)
+ strcpy(dev,"IPC");
+
+
+ if (start_session && !send_session_request(inbuf,outbuf))
+ {
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return(False);
+ }
+
+ bzero(outbuf,smb_size);
+
+ /* setup the protocol strings */
+ {
+ int plength;
+
+ for (plength=0,numprots=0;
+ prots[numprots].name && prots[numprots].prot<=max_protocol;
+ numprots++)
+ plength += strlen(prots[numprots].name)+2;
+
+ set_message(outbuf,0,plength,True);
+
+ p = smb_buf(outbuf);
+ for (numprots=0;
+ prots[numprots].name && prots[numprots].prot<=max_protocol;
+ numprots++)
+ {
+ *p++ = 2;
+ strcpy(p,prots[numprots].name);
+ p += strlen(p) + 1;
+ }
+ }
+
+ CVAL(outbuf,smb_com) = SMBnegprot;
+ setup_pkt(outbuf);
+
+ CVAL(smb_buf(outbuf),0) = 2;
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ show_msg(inbuf);
+
+ if (CVAL(inbuf,smb_rcls) != 0 || ((int)SVAL(inbuf,smb_vwv0) >= numprots))
+ {
+ DEBUG(0,("SMBnegprot failed. myname=%s destname=%s - %s \n",
+ myname,desthost,smb_errstr(inbuf)));
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return(False);
+ }
+
+ Protocol = prots[SVAL(inbuf,smb_vwv0)].prot;
+
+
+ if (Protocol < PROTOCOL_NT1) {
+ sec_mode = SVAL(inbuf,smb_vwv1);
+ max_xmit = SVAL(inbuf,smb_vwv2);
+ sesskey = IVAL(inbuf,smb_vwv6);
+ serverzone = SVALS(inbuf,smb_vwv10)*60;
+ /* this time is converted to GMT by make_unix_date */
+ servertime = make_unix_date(inbuf+smb_vwv8);
+ if (Protocol >= PROTOCOL_COREPLUS) {
+ readbraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x1) != 0);
+ writebraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x2) != 0);
+ }
+ crypt_len = smb_buflen(inbuf);
+ memcpy(cryptkey,smb_buf(inbuf),8);
+ DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv3)));
+ max_vcs = SVAL(inbuf,smb_vwv4);
+ DEBUG(3,("max vcs %d\n",max_vcs));
+ DEBUG(3,("max blk %d\n",SVAL(inbuf,smb_vwv5)));
+ } else {
+ /* NT protocol */
+ sec_mode = CVAL(inbuf,smb_vwv1);
+ max_xmit = IVAL(inbuf,smb_vwv3+1);
+ sesskey = IVAL(inbuf,smb_vwv7+1);
+ serverzone = SVALS(inbuf,smb_vwv15+1)*60;
+ /* this time arrives in real GMT */
+ servertime = interpret_long_date(inbuf+smb_vwv11+1);
+ crypt_len = CVAL(inbuf,smb_vwv16+1);
+ memcpy(cryptkey,smb_buf(inbuf),8);
+ if (IVAL(inbuf,smb_vwv9+1) & 1)
+ readbraw_supported = writebraw_supported = True;
+ DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv1+1)));
+ max_vcs = SVAL(inbuf,smb_vwv2+1);
+ DEBUG(3,("max vcs %d\n",max_vcs));
+ DEBUG(3,("max raw %d\n",IVAL(inbuf,smb_vwv5+1)));
+ DEBUG(3,("capabilities 0x%x\n",IVAL(inbuf,smb_vwv9+1)));
+ }
+
+ DEBUG(3,("Sec mode %d\n",SVAL(inbuf,smb_vwv1)));
+ DEBUG(3,("max xmt %d\n",max_xmit));
+ DEBUG(3,("Got %d byte crypt key\n",crypt_len));
+ DEBUG(3,("Chose protocol [%s]\n",prots[SVAL(inbuf,smb_vwv0)].name));
+
+ doencrypt = ((sec_mode & 2) != 0);
+
+ if (servertime) {
+ static BOOL done_time = False;
+ if (!done_time) {
+ DEBUG(1,("Server time is %sTimezone is UTC%+02.1f\n",
+ asctime(LocalTime(&servertime,GMT_TO_LOCAL)),
+ -(double)(serverzone/3600.0)));
+ done_time = True;
+ }
+ }
+
+ get_pass:
+
+ if (got_pass)
+ pass = password;
+ else
+ pass = (char *)getpass("Password: ");
+
+ if (Protocol >= PROTOCOL_LANMAN1 && use_setup)
+ {
+ fstring pword;
+ int passlen = strlen(pass)+1;
+ strcpy(pword,pass);
+
+#ifdef SMB_PASSWD
+ if (doencrypt && *pass) {
+ DEBUG(3,("Using encrypted passwords\n"));
+ passlen = 24;
+ SMBencrypt(pass,cryptkey,pword);
+ }
+#else
+ doencrypt = False;
+#endif
+
+ /* if in share level security then don't send a password now */
+ if (!(sec_mode & 1)) {strcpy(pword, "");passlen=1;}
+
+ /* send a session setup command */
+ bzero(outbuf,smb_size);
+
+ if (Protocol < PROTOCOL_NT1) {
+ set_message(outbuf,10,1 + strlen(username) + passlen,True);
+ CVAL(outbuf,smb_com) = SMBsesssetupX;
+ setup_pkt(outbuf);
+
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+ SSVAL(outbuf,smb_vwv2,max_xmit);
+ SSVAL(outbuf,smb_vwv3,2);
+ SSVAL(outbuf,smb_vwv4,max_vcs-1);
+ SIVAL(outbuf,smb_vwv5,sesskey);
+ SSVAL(outbuf,smb_vwv7,passlen);
+ p = smb_buf(outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ strcpy(p,username);
+ } else {
+ if (!doencrypt) passlen--;
+ /* for Win95 */
+ set_message(outbuf,13,0,True);
+ CVAL(outbuf,smb_com) = SMBsesssetupX;
+ setup_pkt(outbuf);
+
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+ SSVAL(outbuf,smb_vwv2,BUFFER_SIZE);
+ SSVAL(outbuf,smb_vwv3,2);
+ SSVAL(outbuf,smb_vwv4,getpid());
+ SIVAL(outbuf,smb_vwv5,sesskey);
+ SSVAL(outbuf,smb_vwv7,passlen);
+ SSVAL(outbuf,smb_vwv8,0);
+ p = smb_buf(outbuf);
+ memcpy(p,pword,passlen); p += SVAL(outbuf,smb_vwv7);
+ strcpy(p,username);p = skip_string(p,1);
+ strcpy(p,workgroup);p = skip_string(p,1);
+ strcpy(p,"Unix");p = skip_string(p,1);
+ strcpy(p,"Samba");p = skip_string(p,1);
+ set_message(outbuf,13,PTR_DIFF(p,smb_buf(outbuf)),False);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ show_msg(inbuf);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ if (! *pass &&
+ ((CVAL(inbuf,smb_rcls) == ERRDOS &&
+ SVAL(inbuf,smb_err) == ERRnoaccess) ||
+ (CVAL(inbuf,smb_rcls) == ERRSRV &&
+ SVAL(inbuf,smb_err) == ERRbadpw)))
+ {
+ got_pass = False;
+ DEBUG(3,("resending login\n"));
+ goto get_pass;
+ }
+
+ DEBUG(0,("Session setup failed for username=%s myname=%s destname=%s %s\n",
+ username,myname,desthost,smb_errstr(inbuf)));
+ DEBUG(0,("You might find the -U, -W or -n options useful\n"));
+ DEBUG(0,("Sometimes you have to use `-n USERNAME' (particularly with OS/2)\n"));
+ DEBUG(0,("Some servers also insist on uppercase-only passwords\n"));
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return(False);
+ }
+
+ if (Protocol >= PROTOCOL_NT1) {
+ char *domain,*os,*lanman;
+ p = smb_buf(inbuf);
+ os = p;
+ lanman = skip_string(os,1);
+ domain = skip_string(lanman,1);
+ if (*domain || *os || *lanman)
+ DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",domain,os,lanman));
+ }
+
+ /* use the returned uid from now on */
+ if (SVAL(inbuf,smb_uid) != uid)
+ DEBUG(3,("Server gave us a UID of %d. We gave %d\n",
+ SVAL(inbuf,smb_uid),uid));
+ uid = SVAL(inbuf,smb_uid);
+ }
+
+ /* now we've got a connection - send a tcon message */
+ bzero(outbuf,smb_size);
+
+ if (strncmp(service,"\\\\",2) != 0)
+ {
+ DEBUG(0,("\nWarning: Your service name doesn't start with \\\\. This is probably incorrect.\n"));
+ DEBUG(0,("Perhaps try replacing each \\ with \\\\ on the command line?\n\n"));
+ }
+
+
+ again2:
+
+ {
+ int passlen = strlen(pass)+1;
+ fstring pword;
+ strcpy(pword,pass);
+
+#ifdef SMB_PASSWD
+ if (doencrypt && *pass) {
+ passlen=24;
+ SMBencrypt(pass,cryptkey,pword);
+ }
+#endif
+
+ /* if in user level security then don't send a password now */
+ if ((sec_mode & 1)) {
+ strcpy(pword, ""); passlen=1;
+ }
+
+ set_message(outbuf,4,2 + strlen(service) + passlen + strlen(dev),True);
+ CVAL(outbuf,smb_com) = SMBtconX;
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv3,passlen);
+
+ p = smb_buf(outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ strcpy(p,service);
+ p = skip_string(p,1);
+ strcpy(p,dev);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ /* trying again with a blank password */
+ if (CVAL(inbuf,smb_rcls) != 0 &&
+ (int)strlen(pass) > 0 &&
+ !doencrypt &&
+ Protocol >= PROTOCOL_LANMAN1)
+ {
+ DEBUG(2,("first SMBtconX failed, trying again. %s\n",smb_errstr(inbuf)));
+ strcpy(pass,"");
+ goto again2;
+ }
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("SMBtconX failed. %s\n",smb_errstr(inbuf)));
+ DEBUG(0,("Perhaps you are using the wrong sharename, username or password?\n"));
+ DEBUG(0,("Some servers insist that these be in uppercase\n"));
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return(False);
+ }
+
+
+ max_xmit = MIN(max_xmit,BUFFER_SIZE-4);
+ if (max_xmit <= 0)
+ max_xmit = BUFFER_SIZE - 4;
+
+ cnum = SVAL(inbuf,smb_tid);
+
+ DEBUG(3,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit));
+
+ if (was_null)
+ {
+ free(inbuf);
+ free(outbuf);
+ }
+ return True;
+}
+
+
+/****************************************************************************
+send a logout command
+****************************************************************************/
+static void send_logout(void )
+{
+ pstring inbuf,outbuf;
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,0,0,True);
+ CVAL(outbuf,smb_com) = SMBtdis;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,SHORT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("SMBtdis failed %s\n",smb_errstr(inbuf)));
+ }
+
+
+#ifdef STATS
+ stats_report();
+#endif
+ exit(0);
+}
+
+
+
+/****************************************************************************
+call a remote api
+****************************************************************************/
+static BOOL call_api(int prcnt,int drcnt,
+ int mprcnt,int mdrcnt,
+ int *rprcnt,int *rdrcnt,
+ char *param,char *data,
+ char **rparam,char **rdata)
+{
+ static char *inbuf=NULL;
+ static char *outbuf=NULL;
+
+ if (!inbuf) inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ if (!outbuf) outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ send_trans_request(outbuf,SMBtrans,"\\PIPE\\LANMAN",0,0,
+ data,param,NULL,
+ drcnt,prcnt,0,
+ mdrcnt,mprcnt,0);
+
+ return (receive_trans_response(inbuf,SMBtrans,
+ rdrcnt,rprcnt,
+ rdata,rparam));
+}
+
+/****************************************************************************
+ send a SMB trans or trans2 request
+ ****************************************************************************/
+static BOOL send_trans_request(char *outbuf,int trans,
+ char *name,int fid,int flags,
+ char *data,char *param,uint16 *setup,
+ int ldata,int lparam,int lsetup,
+ int mdata,int mparam,int msetup)
+{
+ int i;
+ int this_ldata,this_lparam;
+ int tot_data=0,tot_param=0;
+ char *outdata,*outparam;
+ pstring inbuf;
+ char *p;
+
+ this_lparam = MIN(lparam,max_xmit - (500+lsetup*SIZEOFWORD)); /* hack */
+ this_ldata = MIN(ldata,max_xmit - (500+lsetup*SIZEOFWORD+this_lparam));
+
+ bzero(outbuf,smb_size);
+ set_message(outbuf,14+lsetup,0,True);
+ CVAL(outbuf,smb_com) = trans;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ outparam = smb_buf(outbuf)+(trans==SMBtrans ? strlen(name)+1 : 3);
+ outdata = outparam+this_lparam;
+
+ /* primary request */
+ SSVAL(outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(outbuf,smb_mprcnt,mparam); /* mprcnt */
+ SSVAL(outbuf,smb_mdrcnt,mdata); /* mdrcnt */
+ SCVAL(outbuf,smb_msrcnt,msetup); /* msrcnt */
+ SSVAL(outbuf,smb_flags,flags); /* flags */
+ SIVAL(outbuf,smb_timeout,0); /* timeout */
+ SSVAL(outbuf,smb_pscnt,this_lparam); /* pscnt */
+ SSVAL(outbuf,smb_psoff,smb_offset(outparam,outbuf)); /* psoff */
+ SSVAL(outbuf,smb_dscnt,this_ldata); /* dscnt */
+ SSVAL(outbuf,smb_dsoff,smb_offset(outdata,outbuf)); /* dsoff */
+ SCVAL(outbuf,smb_suwcnt,lsetup); /* suwcnt */
+ for (i=0;i<lsetup;i++) /* setup[] */
+ SSVAL(outbuf,smb_setup+i*SIZEOFWORD,setup[i]);
+ p = smb_buf(outbuf);
+ if (trans==SMBtrans)
+ strcpy(p,name); /* name[] */
+ else
+ {
+ *p++ = 0; /* put in a null smb_name */
+ *p++ = 'D'; *p++ = ' '; /* this was added because OS/2 does it */
+ }
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+ set_message(outbuf,14+lsetup, /* wcnt, bcc */
+ PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False);
+
+ show_msg(outbuf);
+ send_smb(Client,outbuf);
+
+ if (this_ldata < ldata || this_lparam < lparam)
+ {
+ /* receive interim response */
+ if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s request failed (%s)\n",
+ trans==SMBtrans?"SMBtrans":"SMBtrans2", smb_errstr(inbuf)));
+ return(False);
+ }
+
+ tot_data = this_ldata;
+ tot_param = this_lparam;
+
+ while (tot_data < ldata || tot_param < lparam)
+ {
+ this_lparam = MIN(lparam-tot_param,max_xmit - 500); /* hack */
+ this_ldata = MIN(ldata-tot_data,max_xmit - (500+this_lparam));
+
+ set_message(outbuf,trans==SMBtrans?8:9,0,True);
+ CVAL(outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2;
+
+ outparam = smb_buf(outbuf);
+ outdata = outparam+this_lparam;
+
+ /* secondary request */
+ SSVAL(outbuf,smb_tpscnt,lparam); /* tpscnt */
+ SSVAL(outbuf,smb_tdscnt,ldata); /* tdscnt */
+ SSVAL(outbuf,smb_spscnt,this_lparam); /* pscnt */
+ SSVAL(outbuf,smb_spsoff,smb_offset(outparam,outbuf)); /* psoff */
+ SSVAL(outbuf,smb_spsdisp,tot_param); /* psdisp */
+ SSVAL(outbuf,smb_sdscnt,this_ldata); /* dscnt */
+ SSVAL(outbuf,smb_sdsoff,smb_offset(outdata,outbuf)); /* dsoff */
+ SSVAL(outbuf,smb_sdsdisp,tot_data); /* dsdisp */
+ if (trans==SMBtrans2)
+ SSVAL(outbuf,smb_sfid,fid); /* fid */
+ if (this_lparam) /* param[] */
+ memcpy(outparam,param,this_lparam);
+ if (this_ldata) /* data[] */
+ memcpy(outdata,data,this_ldata);
+ set_message(outbuf,trans==SMBtrans?8:9, /* wcnt, bcc */
+ PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False);
+
+ show_msg(outbuf);
+ send_smb(Client,outbuf);
+
+ tot_data += this_ldata;
+ tot_param += this_lparam;
+ }
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL browse_host(BOOL sort)
+{
+#ifdef NOSTRCASECMP
+#define strcasecmp StrCaseCmp
+#endif
+ extern int strcasecmp();
+
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ int rdrcnt,rprcnt;
+ pstring param;
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = param;
+ SSVAL(p,0,0); /* api number */
+ p += 2;
+ strcpy(p,"WrLeh");
+ p = skip_string(p,1);
+ strcpy(p,"B13BWz");
+ p = skip_string(p,1);
+ SSVAL(p,0,1);
+ SSVAL(p,2,BUFFER_SIZE);
+ p += 4;
+
+ if (call_api(PTR_DIFF(p,param),0,
+ 1024,BUFFER_SIZE,
+ &rprcnt,&rdrcnt,
+ param,NULL,
+ &rparam,&rdata))
+ {
+ int res = SVAL(rparam,0);
+ int converter=SVAL(rparam,2);
+ int i;
+ BOOL long_share_name=False;
+
+ if (res == 0)
+ {
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ if (count > 0)
+ {
+ printf("\n\tSharename Type Comment\n");
+ printf("\t--------- ---- -------\n");
+ }
+
+ if (sort)
+ qsort(p,count,20,QSORT_CAST strcasecmp);
+
+ for (i=0;i<count;i++)
+ {
+ char *sname = p;
+ int type = SVAL(p,14);
+ int comment_offset = IVAL(p,16) & 0xFFFF;
+ fstring typestr;
+ *typestr=0;
+
+ switch (type)
+ {
+ case STYPE_DISKTREE:
+ strcpy(typestr,"Disk"); break;
+ case STYPE_PRINTQ:
+ strcpy(typestr,"Printer"); break;
+ case STYPE_DEVICE:
+ strcpy(typestr,"Device"); break;
+ case STYPE_IPC:
+ strcpy(typestr,"IPC"); break;
+ }
+
+ printf("\t%-15.15s%-10.10s%s\n",
+ sname,
+ typestr,
+ comment_offset?rdata+comment_offset-converter:"");
+
+ if (strlen(sname)>8) long_share_name=True;
+
+ p += 20;
+ }
+
+ if (long_share_name) {
+ printf("\nNOTE: There were share names longer than 8 chars.\nOn older clients these may not be accessible or may give browsing errors\n");
+ }
+ }
+ }
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return(count>0);
+}
+
+
+/****************************************************************************
+get some server info
+****************************************************************************/
+static void server_info()
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ int rdrcnt,rprcnt;
+ pstring param;
+
+ bzero(param,sizeof(param));
+
+ p = param;
+ SSVAL(p,0,63); /* api number */
+ p += 2;
+ strcpy(p,"WrLh");
+ p = skip_string(p,1);
+ strcpy(p,"zzzBBzz");
+ p = skip_string(p,1);
+ SSVAL(p,0,10); /* level 10 */
+ SSVAL(p,2,1000);
+ p += 6;
+
+ if (call_api(PTR_DIFF(p,param),0,
+ 6,1000,
+ &rprcnt,&rdrcnt,
+ param,NULL,
+ &rparam,&rdata))
+ {
+ int res = SVAL(rparam,0);
+ int converter=SVAL(rparam,2);
+
+ if (res == 0)
+ {
+ p = rdata;
+
+ printf("\nServer=[%s] User=[%s] Workgroup=[%s] Domain=[%s]\n",
+ rdata+SVAL(p,0)-converter,
+ rdata+SVAL(p,4)-converter,
+ rdata+SVAL(p,8)-converter,
+ rdata+SVAL(p,14)-converter);
+ }
+ }
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return;
+}
+
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static BOOL list_servers()
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ int rdrcnt,rprcnt;
+ char *p;
+ pstring param;
+ int uLevel = 1;
+ int count = 0;
+
+ /* now send a SMBtrans command with api ServerEnum? */
+ p = param;
+ SSVAL(p,0,0x68); /* api number */
+ p += 2;
+ strcpy(p,"WrLehDO");
+ p = skip_string(p,1);
+
+ strcpy(p,"B16BBDz");
+#if 0
+ strcpy(p,getenv("XX_STR2"));
+#endif
+
+ p = skip_string(p,1);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,0x2000); /* buf length */
+ p += 4;
+
+ SIVAL(p,0,SV_TYPE_ALL);
+
+ if (call_api(PTR_DIFF(p+4,param),0,
+ 8,10000,
+ &rprcnt,&rdrcnt,
+ param,NULL,
+ &rparam,&rdata))
+ {
+ int res = SVAL(rparam,0);
+ int converter=SVAL(rparam,2);
+ int i;
+
+ if (res == 0) {
+ char *p2 = rdata;
+ count=SVAL(rparam,4);
+
+ if (count > 0) {
+ printf("\n\nThis machine has a browse list:\n");
+ printf("\n\tServer Comment\n");
+ printf("\t--------- -------\n");
+ }
+
+ for (i=0;i<count;i++) {
+ char *sname = p2;
+ int comment_offset = IVAL(p2,22) & 0xFFFF;
+ printf("\t%-16.16s %s\n",
+ sname,
+ comment_offset?rdata+comment_offset-converter:"");
+
+ p2 += 26;
+ }
+ }
+ }
+
+ if (rparam) {free(rparam); rparam = NULL;}
+ if (rdata) {free(rdata); rdata = NULL;}
+
+ SIVAL(p,0,SV_TYPE_DOMAIN_ENUM);
+
+ if (call_api(PTR_DIFF(p+4,param),0,
+ 8,10000,
+ &rprcnt,&rdrcnt,
+ param,NULL,
+ &rparam,&rdata))
+ {
+ int res = SVAL(rparam,0);
+ int converter=SVAL(rparam,2);
+ int i;
+
+ if (res == 0) {
+ char *p2 = rdata;
+ count=SVAL(rparam,4);
+
+ if (count > 0) {
+ printf("\n\nThis machine has a workgroup list:\n");
+ printf("\n\tWorkgroup Master\n");
+ printf("\t--------- -------\n");
+ }
+
+ for (i=0;i<count;i++) {
+ char *sname = p2;
+ int comment_offset = IVAL(p2,22) & 0xFFFF;
+ printf("\t%-16.16s %s\n",
+ sname,
+ comment_offset?rdata+comment_offset-converter:"");
+
+ p2 += 26;
+ }
+ }
+ }
+
+ if (rparam) free(rparam);
+ if (rdata) free(rdata);
+
+ return(count>0);
+}
+
+
+
+
+void cmd_help();
+
+/* This defines the commands supported by this client */
+struct
+{
+ char *name;
+ void (*fn)();
+ char *description;
+} commands[] =
+{
+ {"ls",cmd_dir,"<mask> list the contents of the current directory"},
+ {"dir",cmd_dir,"<mask> list the contents of the current directory"},
+ {"lcd",cmd_lcd,"[directory] change/report the local current working directory"},
+ {"cd",cmd_cd,"[directory] change/report the remote directory"},
+ {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)"},
+ {"get",cmd_get,"<remote name> [local name] get a file"},
+ {"mget",cmd_mget,"<mask> get all the matching files"},
+ {"put",cmd_put,"<local name> [remote name] put a file"},
+ {"mput",cmd_mput,"<mask> put all matching files"},
+ {"rename",cmd_rename,"<src> <dest> rename some files"},
+ {"more",cmd_more,"<remote name> view a remote file with your pager"},
+ {"mask",cmd_select,"<mask> mask all filenames against this"},
+ {"del",cmd_del,"<mask> delete all matching files"},
+ {"rm",cmd_del,"<mask> delete all matching files"},
+ {"mkdir",cmd_mkdir,"<directory> make a directory"},
+ {"md",cmd_mkdir,"<directory> make a directory"},
+ {"rmdir",cmd_rmdir,"<directory> remove a directory"},
+ {"rd",cmd_rmdir,"<directory> remove a directory"},
+ {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput"},
+ {"recurse",cmd_recurse,"toggle directory recursion for mget and mput"},
+ {"translate",cmd_translate,"toggle text translation for printing"},
+ {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get"},
+ {"print",cmd_print,"<file name> print a file"},
+ {"printmode",cmd_printmode,"<graphics or text> set the print mode"},
+ {"queue",cmd_queue,"show the print queue"},
+ {"cancel",cmd_cancel,"<jobid> cancel a print queue entry"},
+ {"stat",cmd_stat,"<file> get info on a file (experimental!)"},
+ {"quit",send_logout,"logoff the server"},
+ {"q",send_logout,"logoff the server"},
+ {"exit",send_logout,"logoff the server"},
+ {"newer",cmd_newer,"<file> only mget files newer than the specified local file"},
+ {"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"},
+ {"tar",cmd_tar,"tar <c|x>[IXbgNa] current directory to/from <file name>" },
+ {"blocksize",cmd_block,"blocksize <number> (default 20)" },
+ {"tarmode",cmd_tarmode,
+ "<full|inc|reset|noreset> tar's behaviour towards archive bits" },
+ {"setmode",cmd_setmode,"filename <setmode string> change modes of file"},
+ {"help",cmd_help,"[command] give help on a command"},
+ {"?",cmd_help,"[command] give help on a command"},
+ {"!",NULL,"run a shell command on the local system"},
+ {"",NULL,NULL}
+};
+
+
+/*******************************************************************
+ lookup a command string in the list of commands, including
+ abbreviations
+ ******************************************************************/
+static int process_tok(fstring tok)
+{
+ int i = 0, matches = 0;
+ int cmd=0;
+ int tok_len = strlen(tok);
+
+ while (commands[i].fn != NULL)
+ {
+ if (strequal(commands[i].name,tok))
+ {
+ matches = 1;
+ cmd = i;
+ break;
+ }
+ else if (strnequal(commands[i].name, tok, tok_len+1))
+ {
+ matches++;
+ cmd = i;
+ }
+ i++;
+ }
+
+ if (matches == 0)
+ return(-1);
+ else if (matches == 1)
+ return(cmd);
+ else
+ return(-2);
+}
+
+/****************************************************************************
+help
+****************************************************************************/
+void cmd_help(void)
+{
+ int i=0,j;
+ fstring buf;
+
+ if (next_token(NULL,buf,NULL))
+ {
+ if ((i = process_tok(buf)) >= 0)
+ DEBUG(0,("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description));
+ }
+ else
+ while (commands[i].description)
+ {
+ for (j=0; commands[i].description && (j<5); j++) {
+ DEBUG(0,("%-15s",commands[i].name));
+ i++;
+ }
+ DEBUG(0,("\n"));
+ }
+}
+
+/****************************************************************************
+open the client sockets
+****************************************************************************/
+static BOOL open_sockets(int port )
+{
+ static int last_port;
+ char *host;
+ pstring service2;
+ extern int Client;
+#ifdef USENMB
+ BOOL failed = True;
+#endif
+
+ if (port == 0) port=last_port;
+ last_port=port;
+
+ strupper(service);
+
+ if (*desthost)
+ {
+ host = desthost;
+ }
+ else
+ {
+ strcpy(service2,service);
+ host = strtok(service2,"\\/");
+ if (!host) {
+ DEBUG(0,("Badly formed host name\n"));
+ return(False);
+ }
+ strcpy(desthost,host);
+ }
+
+ DEBUG(3,("Opening sockets\n"));
+
+ if (*myname == 0)
+ {
+ get_myname(myname,NULL);
+ strupper(myname);
+ }
+
+ if (!have_ip)
+ {
+ struct hostent *hp;
+
+ if ((hp = Get_Hostbyname(host))) {
+ putip((char *)&dest_ip,(char *)hp->h_addr);
+ failed = False;
+ } else {
+#ifdef USENMB
+ /* Try and resolve the name with the netbios server */
+ int bcast;
+ pstring hs;
+ struct in_addr ip1, ip2;
+
+ if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3)) != -1) {
+ set_socket_options (bcast, "SO_BROADCAST");
+
+ if (!got_bcast && get_myname(hs, &ip1)) {
+ get_broadcast(&ip1, &bcast_ip, &ip2);
+ }
+
+ if (name_query(bcast, host, 0x20, True, True, bcast_ip, &dest_ip,0)){
+ failed = False;
+ }
+ close (bcast);
+ }
+#endif
+ if (failed) {
+ DEBUG(0,("Get_Hostbyname: Unknown host %s.\n",host));
+ return False;
+ }
+ }
+ }
+
+ Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+ if (Client == -1)
+ return False;
+
+ DEBUG(3,("Connected\n"));
+
+ set_socket_options(Client,user_socket_options);
+
+ return True;
+}
+
+/****************************************************************************
+wait for keyboard activity, swallowing network packets
+****************************************************************************/
+#ifdef CLIX
+static char wait_keyboard(char *buffer)
+#else
+static void wait_keyboard(char *buffer)
+#endif
+{
+ fd_set fds;
+ int selrtn;
+ struct timeval timeout;
+
+#ifdef CLIX
+ int delay = 0;
+#endif
+
+ while (1)
+ {
+ extern int Client;
+ FD_ZERO(&fds);
+ FD_SET(Client,&fds);
+#ifndef CLIX
+ FD_SET(fileno(stdin),&fds);
+#endif
+
+ timeout.tv_sec = 20;
+ timeout.tv_usec = 0;
+#ifdef CLIX
+ timeout.tv_sec = 0;
+#endif
+ selrtn = sys_select(&fds,&timeout);
+
+#ifndef CLIX
+ if (FD_ISSET(fileno(stdin),&fds))
+ return;
+#else
+ {
+ char ch;
+ int f_flags;
+ int readret;
+
+ f_flags = fcntl(fileno(stdin), F_GETFL, 0);
+ fcntl( fileno(stdin), F_SETFL, f_flags | O_NONBLOCK);
+ readret = read_data( fileno(stdin), &ch, 1);
+ fcntl(fileno(stdin), F_SETFL, f_flags);
+ if (readret == -1)
+ {
+ if (errno != EAGAIN)
+ {
+ /* should crash here */
+ DEBUG(1,("readchar stdin failed\n"));
+ }
+ }
+ else if (readret != 0)
+ {
+ return ch;
+ }
+ }
+#endif
+ if (FD_ISSET(Client,&fds))
+ receive_smb(Client,buffer,0);
+
+#ifdef CLIX
+ delay++;
+ if (delay > 100000)
+ {
+ delay = 0;
+ chkpath("\\",False);
+ }
+#else
+ chkpath("\\",False);
+#endif
+ }
+}
+
+
+/****************************************************************************
+close and open the connection again
+****************************************************************************/
+BOOL reopen_connection(char *inbuf,char *outbuf)
+{
+ static int open_count=0;
+
+ open_count++;
+
+ if (open_count>5) return(False);
+
+ DEBUG(1,("Trying to re-open connection\n"));
+
+ set_message(outbuf,0,0,True);
+ SCVAL(outbuf,smb_com,SMBtdis);
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,SHORT_TIMEOUT);
+
+ close_sockets();
+ if (!open_sockets(0)) return(False);
+
+ return(send_login(inbuf,outbuf,True,True));
+}
+
+/****************************************************************************
+ process commands from the client
+****************************************************************************/
+BOOL process(char *base_directory)
+{
+ extern FILE *dbf;
+ pstring line;
+
+ char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return(False);
+
+ bzero(OutBuffer,smb_size);
+
+ if (!send_login(InBuffer,OutBuffer,True,True))
+ return(False);
+
+ if (*base_directory) do_cd(base_directory);
+
+ while (!feof(stdin))
+ {
+ fstring tok;
+ int i;
+
+ bzero(OutBuffer,smb_size);
+
+ /* display a prompt */
+ DEBUG(1,("smb: %s> ", CNV_LANG(cur_dir)));
+ fflush(dbf);
+
+#ifdef CLIX
+ line[0] = wait_keyboard(InBuffer);
+ /* this might not be such a good idea... */
+ if ( line[0] == EOF)
+ break;
+#else
+ wait_keyboard(InBuffer);
+#endif
+
+ /* and get a response */
+#ifdef CLIX
+ fgets( &line[1],999, stdin);
+#else
+ if (!fgets(line,1000,stdin))
+ break;
+#endif
+
+ /* input language code to internal one */
+ CNV_INPUT (line);
+
+ /* special case - first char is ! */
+ if (*line == '!')
+ {
+ system(line + 1);
+ continue;
+ }
+
+ /* and get the first part of the command */
+ {
+ char *ptr = line;
+ if (!next_token(&ptr,tok,NULL)) continue;
+ }
+
+ if ((i = process_tok(tok)) >= 0)
+ commands[i].fn(InBuffer,OutBuffer);
+ else if (i == -2)
+ DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok)));
+ else
+ DEBUG(0,("%s: command not found\n",CNV_LANG(tok)));
+ }
+
+ send_logout();
+ return(True);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+void usage(char *pname)
+{
+ DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ",
+ pname));
+
+#ifdef KANJI
+ DEBUG(0,("[-t termcode] "));
+#endif /* KANJI */
+
+ DEBUG(0,("\nVersion %s\n",VERSION));
+ DEBUG(0,("\t-p port listen on the specified port\n"));
+ DEBUG(0,("\t-d debuglevel set the debuglevel\n"));
+ DEBUG(0,("\t-l log basename. Basename for log/debug files\n"));
+ DEBUG(0,("\t-n netbios name. Use this name as my netbios name\n"));
+ DEBUG(0,("\t-N don't ask for a password\n"));
+ DEBUG(0,("\t-P connect to service as a printer\n"));
+ DEBUG(0,("\t-M host send a winpopup message to the host\n"));
+ DEBUG(0,("\t-m max protocol set the max protocol level\n"));
+ DEBUG(0,("\t-L host get a list of shares available on a host\n"));
+ DEBUG(0,("\t-I dest IP use this IP to connect to\n"));
+ DEBUG(0,("\t-E write messages to stderr instead of stdout\n"));
+ DEBUG(0,("\t-U username set the network username\n"));
+ DEBUG(0,("\t-W workgroup set the workgroup name\n"));
+#ifdef KANJI
+ DEBUG(0,("\t-t terminal code terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n"));
+#endif /* KANJI */
+ DEBUG(0,("\t-T<c|x>IXgbNa command line tar\n"));
+ DEBUG(0,("\t-D directory start from directory\n"));
+ DEBUG(0,("\n"));
+}
+
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+ fstring base_directory;
+ char *pname = argv[0];
+ int port = 139;
+ int opt;
+ extern FILE *dbf;
+ extern char *optarg;
+ extern int optind;
+ pstring query_host;
+ BOOL message = False;
+ extern char tar_type;
+
+ *query_host = 0;
+ *base_directory = 0;
+
+ DEBUGLEVEL = 2;
+
+ setup_logging(pname,True);
+
+ TimeInit();
+ charset_initialise();
+
+ pid = getpid();
+ uid = getuid();
+ gid = getgid();
+ mid = pid + 100;
+ myumask = umask(0);
+ umask(myumask);
+
+ if (getenv("USER"))
+ {
+ strcpy(username,getenv("USER"));
+ strupper(username);
+ }
+
+ if (*username == 0 && getenv("LOGNAME"))
+ {
+ strcpy(username,getenv("LOGNAME"));
+ strupper(username);
+ }
+
+ if (argc < 2)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+ if (*argv[1] != '-')
+ {
+
+ strcpy(service,argv[1]);
+ argc--;
+ argv++;
+
+ if (count_chars(service,'\\') < 3)
+ {
+ usage(pname);
+ printf("\n%s: Not enough '\\' characters in service\n",service);
+ exit(1);
+ }
+
+/*
+ if (count_chars(service,'\\') > 3)
+ {
+ usage(pname);
+ printf("\n%s: Too many '\\' characters in service\n",service);
+ exit(1);
+ }
+ */
+
+ if (argc > 1 && (*argv[1] != '-'))
+ {
+ got_pass = True;
+ strcpy(password,argv[1]);
+ memset(argv[1],'X',strlen(argv[1]));
+ argc--;
+ argv++;
+ }
+ }
+
+#ifdef KANJI
+ setup_term_code (KANJI);
+ while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:")) != EOF)
+#else
+ while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:m:W:T:D:")) != EOF)
+#endif /* KANJI */
+ switch (opt)
+ {
+ case 'm':
+ max_protocol = interpret_protocol(optarg,max_protocol);
+ break;
+ case 'O':
+ strcpy(user_socket_options,optarg);
+ break;
+ case 'M':
+ name_type = 3;
+ strcpy(desthost,optarg);
+ strupper(desthost);
+ message = True;
+ break;
+ case 'B':
+ bcast_ip = *interpret_addr2(optarg);
+ got_bcast = True;
+ break;
+ case 'D':
+ strcpy(base_directory,optarg);
+ break;
+ case 'T':
+ if (!tar_parseargs(argc, argv, optarg, optind)) {
+ usage(pname);
+ exit(1);
+ }
+ break;
+ case 'i':
+ strcpy(scope,optarg);
+ break;
+ case 'L':
+ got_pass = True;
+ strcpy(query_host,optarg);
+ break;
+ case 'U':
+ {
+ char *p;
+ strcpy(username,optarg);
+ if ((p=strchr(username,'%')))
+ {
+ *p = 0;
+ strcpy(password,p+1);
+ got_pass = True;
+ memset(strchr(optarg,'%')+1,'X',strlen(password));
+ }
+ }
+
+ break;
+ case 'W':
+ strcpy(workgroup,optarg);
+ break;
+ case 'E':
+ dbf = stderr;
+ break;
+ case 'I':
+ {
+ dest_ip = *interpret_addr2(optarg);
+ if (zero_ip(dest_ip)) exit(1);
+ have_ip = True;
+ }
+ break;
+ case 'n':
+ strcpy(myname,optarg);
+ break;
+ case 'N':
+ got_pass = True;
+ break;
+ case 'P':
+ connect_as_printer = True;
+ break;
+ case 'd':
+ if (*optarg == 'A')
+ DEBUGLEVEL = 10000;
+ else
+ DEBUGLEVEL = atoi(optarg);
+ break;
+ case 'l':
+ sprintf(debugf,"%s.client",optarg);
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'h':
+ usage(pname);
+ exit(0);
+ break;
+#ifdef KANJI
+ case 't':
+ if (!setup_term_code (optarg)) {
+ DEBUG(0, ("%s: unknown terminal code name\n", optarg));
+ usage (pname);
+ exit (1);
+ }
+ break;
+#endif /* KANJI */
+ default:
+ usage(pname);
+ exit(1);
+ }
+
+ if (!tar_type && !*query_host && !*service && !message)
+ {
+ usage(pname);
+ exit(1);
+ }
+
+
+ DEBUG(3,("%s client started (version %s)\n",timestring(),VERSION));
+
+ get_myname(*myname?NULL:myname,&myip);
+ strupper(myname);
+
+ if (tar_type) {
+ recurse=True;
+
+ if (open_sockets(port)) {
+ char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ int ret;
+
+ if ((InBuffer == NULL) || (OutBuffer == NULL))
+ return(1);
+
+ bzero(OutBuffer,smb_size);
+ if (!send_login(InBuffer,OutBuffer,True,True))
+ return(False);
+
+ if (*base_directory) do_cd(base_directory);
+
+ ret=process_tar(InBuffer, OutBuffer);
+
+ send_logout();
+ close_sockets();
+ return(ret);
+ } else
+ return(1);
+ }
+
+ if (*query_host)
+ {
+ int ret = 0;
+ sprintf(service,"\\\\%s\\IPC$",query_host);
+ strupper(service);
+ connect_as_ipc = True;
+ if (open_sockets(port))
+ {
+#if 0
+ *username = 0;
+#endif
+ if (!send_login(NULL,NULL,True,True))
+ return(1);
+
+ server_info();
+ if (!browse_host(True)) {
+ sleep(1);
+ browse_host(True);
+ }
+ if (!list_servers()) {
+ sleep(1);
+ list_servers();
+ }
+
+ send_logout();
+ close_sockets();
+ }
+
+ return(ret);
+ }
+
+ if (message)
+ {
+ int ret = 0;
+ if (open_sockets(port))
+ {
+ pstring inbuf,outbuf;
+ bzero(outbuf,smb_size);
+ if (!send_session_request(inbuf,outbuf))
+ return(1);
+
+ send_message(inbuf,outbuf);
+
+ close_sockets();
+ }
+
+ return(ret);
+ }
+
+ if (open_sockets(port))
+ {
+ if (!process(base_directory))
+ {
+ close_sockets();
+ return(1);
+ }
+ close_sockets();
+ }
+ else
+ return(1);
+
+ return(0);
+}
+
+
+/* error code stuff - put together by Merik Karman
+ merik@blackadder.dsh.oz.au */
+
+typedef struct
+{
+ char *name;
+ int code;
+ char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+ {"ERRbadfunc",1,"Invalid function."},
+ {"ERRbadfile",2,"File not found."},
+ {"ERRbadpath",3,"Directory invalid."},
+ {"ERRnofids",4,"No file descriptors available"},
+ {"ERRnoaccess",5,"Access denied."},
+ {"ERRbadfid",6,"Invalid file handle."},
+ {"ERRbadmcb",7,"Memory control blocks destroyed."},
+ {"ERRnomem",8,"Insufficient server memory to perform the requested function."},
+ {"ERRbadmem",9,"Invalid memory block address."},
+ {"ERRbadenv",10,"Invalid environment."},
+ {"ERRbadformat",11,"Invalid format."},
+ {"ERRbadaccess",12,"Invalid open mode."},
+ {"ERRbaddata",13,"Invalid data."},
+ {"ERR",14,"reserved."},
+ {"ERRbaddrive",15,"Invalid drive specified."},
+ {"ERRremcd",16,"A Delete Directory request attempted to remove the server's current directory."},
+ {"ERRdiffdevice",17,"Not same device."},
+ {"ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."},
+ {"ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing FIDs on the file."},
+ {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRfilexists",80,"The file named in a Create Directory, Make New File or Link request already exists."},
+ {"ERRbadpipe",230,"Pipe invalid."},
+ {"ERRpipebusy",231,"All instances of the requested pipe are busy."},
+ {"ERRpipeclosing",232,"Pipe close in progress."},
+ {"ERRnotconnected",233,"No process on other end of pipe."},
+ {"ERRmoredata",234,"There is more data to be returned."},
+ {"ERRinvgroup",2455,"Invalid workgroup (try the -W option)"},
+ {NULL,-1,NULL}};
+
+/* Server Error Messages */
+err_code_struct server_msgs[] = {
+ {"ERRerror",1,"Non-specific error code."},
+ {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+ {"ERRbadtype",3,"reserved."},
+ {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."},
+ {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+ {"ERRinvnetname",6,"Invalid network name in tree connect."},
+ {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."},
+ {"ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+ {"ERRqtoobig",50,"Print queue full -- no space."},
+ {"ERRqeof",51,"EOF on print queue dump."},
+ {"ERRinvpfid",52,"Invalid print file FID."},
+ {"ERRsmbcmd",64,"The server did not recognize the command received."},
+ {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+ {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."},
+ {"ERRreserved",68,"reserved."},
+ {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."},
+ {"ERRreserved",70,"reserved."},
+ {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+ {"ERRpaused",81,"Server is paused."},
+ {"ERRmsgoff",82,"Not receiving messages."},
+ {"ERRnoroom",83,"No room to buffer message."},
+ {"ERRrmuns",87,"Too many remote user names."},
+ {"ERRtimeout",88,"Operation timed out."},
+ {"ERRnoresource",89,"No resources currently available for request."},
+ {"ERRtoomanyuids",90,"Too many UIDs active on this session."},
+ {"ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+ {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+ {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+ {"ERRcontmpx",252,"Continue in MPX mode."},
+ {"ERRreserved",253,"reserved."},
+ {"ERRreserved",254,"reserved."},
+ {"ERRnosupport",0xFFFF,"Function not supported."},
+ {NULL,-1,NULL}};
+
+/* Hard Error Messages */
+err_code_struct hard_msgs[] = {
+ {"ERRnowrite",19,"Attempt to write on write-protected diskette."},
+ {"ERRbadunit",20,"Unknown unit."},
+ {"ERRnotready",21,"Drive not ready."},
+ {"ERRbadcmd",22,"Unknown command."},
+ {"ERRdata",23,"Data error (CRC)."},
+ {"ERRbadreq",24,"Bad request structure length."},
+ {"ERRseek",25 ,"Seek error."},
+ {"ERRbadmedia",26,"Unknown media type."},
+ {"ERRbadsector",27,"Sector not found."},
+ {"ERRnopaper",28,"Printer out of paper."},
+ {"ERRwrite",29,"Write fault."},
+ {"ERRread",30,"Read fault."},
+ {"ERRgeneral",31,"General failure."},
+ {"ERRbadshare",32,"A open conflicts with an existing open."},
+ {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRwrongdisk",34,"The wrong disk was found in a drive."},
+ {"ERRFCBUnavail",35,"No FCBs are available to process request."},
+ {"ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+ {NULL,-1,NULL}};
+
+
+struct
+{
+ int code;
+ char *class;
+ err_code_struct *err_msgs;
+} err_classes[] = {
+ {0,"SUCCESS",NULL},
+ {0x01,"ERRDOS",dos_msgs},
+ {0x02,"ERRSRV",server_msgs},
+ {0x03,"ERRHRD",hard_msgs},
+ {0x04,"ERRXOS",NULL},
+ {0xE1,"ERRRMX1",NULL},
+ {0xE2,"ERRRMX2",NULL},
+ {0xE3,"ERRRMX3",NULL},
+ {0xFF,"ERRCMD",NULL},
+ {-1,NULL,NULL}};
+
+
+/****************************************************************************
+return a SMB error string from a SMB buffer
+****************************************************************************/
+char *smb_errstr(char *inbuf)
+{
+ static pstring ret;
+ int class = CVAL(inbuf,smb_rcls);
+ int num = SVAL(inbuf,smb_err);
+ int i,j;
+
+ for (i=0;err_classes[i].class;i++)
+ if (err_classes[i].code == class)
+ {
+ if (err_classes[i].err_msgs)
+ {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code)
+ {
+ if (DEBUGLEVEL > 0)
+ sprintf(ret,"%s - %s (%s)",err_classes[i].class,
+ err[j].name,err[j].message);
+ else
+ sprintf(ret,"%s - %s",err_classes[i].class,err[j].name);
+ return ret;
+ }
+ }
+
+ sprintf(ret,"%s - %d",err_classes[i].class,num);
+ return ret;
+ }
+
+ sprintf(ret,"ERROR: Unknown error (%d,%d)",class,num);
+ return(ret);
+}
diff --git a/source3/client/clitar.c b/source3/client/clitar.c
new file mode 100644
index 0000000000..1433ec5941
--- /dev/null
+++ b/source3/client/clitar.c
@@ -0,0 +1,1713 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ Tar Extensions
+ Copyright (C) Ricky Poulten 1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include "includes.h"
+#include "clitar.h"
+
+extern void setup_pkt(char *outbuf);
+extern BOOL reopen_connection(char *inbuf,char *outbuf);
+extern void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir);
+
+int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind);
+
+extern BOOL recurse;
+
+#define SEPARATORS " \t\n\r"
+extern int DEBUGLEVEL;
+extern int Client;
+
+/* These defines are for the do_setrattr routine, to indicate
+ * setting and reseting of file attributes in the function call */
+#define ATTRSET 1
+#define ATTRRESET 0
+
+static int attribute = aDIR | aSYSTEM | aHIDDEN;
+
+#ifndef CLIENT_TIMEOUT
+#define CLIENT_TIMEOUT (30*1000)
+#endif
+
+static char *tarbuf;
+static int tp, ntarf, tbufsiz;
+/* Incremental mode */
+BOOL tar_inc=False;
+/* Reset archive bit */
+BOOL tar_reset=False;
+/* Include / exclude mode (true=include, false=exclude) */
+BOOL tar_excl=True;
+char tar_type='\0';
+static char **cliplist=NULL;
+static int clipn=0;
+
+extern file_info def_finfo;
+extern BOOL lowercase;
+extern int cnum;
+extern BOOL readbraw_supported;
+extern int max_xmit;
+extern pstring cur_dir;
+extern int get_total_time_ms;
+extern int get_total_size;
+extern int Protocol;
+
+int blocksize=20;
+int tarhandle;
+
+static void writetarheader();
+static void do_atar();
+static void do_tar();
+static void oct_it();
+static void fixtarname();
+static int dotarbuf();
+static void dozerobuf();
+static void dotareof();
+static void initarbuf();
+static int do_setrattr();
+void cmd_tar();
+int process_tar();
+char **toktocliplist();
+int clipfind();
+/* restore functions */
+static long readtarheader();
+static long unoct();
+static void do_tarput();
+static void unfixtarname();
+
+/*
+ * tar specific utitlities
+ */
+
+/****************************************************************************
+Write a tar header to buffer
+****************************************************************************/
+static void writetarheader(int f, char *aname, int size, time_t mtime,
+ char *amode)
+{
+ union hblock hb;
+ int i, chk, l;
+ char *jp;
+
+ memset(hb.dummy, 0, sizeof(hb.dummy));
+
+ l=strlen(aname);
+ if (l >= NAMSIZ)
+ {
+ DEBUG(0, ("tar file %s name length exceeds NAMSIZ\n", aname));
+ }
+
+ /* use l + 1 to do the null too */
+ fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1);
+
+ if (lowercase)
+ strlower(hb.dbuf.name);
+
+ /* write out a "standard" tar format header */
+
+ hb.dbuf.name[NAMSIZ-1]='\0';
+ strcpy(hb.dbuf.mode, amode);
+ oct_it(0L, 8, hb.dbuf.uid);
+ oct_it(0L, 8, hb.dbuf.gid);
+ oct_it((long) size, 13, hb.dbuf.size);
+ oct_it((long) mtime, 13, hb.dbuf.mtime);
+ memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum));
+ hb.dbuf.linkflag='0';
+ memset(hb.dbuf.linkname, 0, NAMSIZ);
+
+ for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
+
+ oct_it((long) chk, 8, hb.dbuf.chksum);
+ hb.dbuf.chksum[6] = '\0';
+
+ (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
+}
+
+/****************************************************************************
+Read a tar header into a hblock structure, and validate
+***************************************************************************/
+static long readtarheader(union hblock *hb, file_info *finfo, char *prefix)
+{
+ long chk, fchk;
+ int i;
+ char *jp;
+
+ /*
+ * read in a "standard" tar format header - we're not that interested
+ * in that many fields, though
+ */
+
+ /* check the checksum */
+ for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) chk+=(0xFF & *jp++);
+
+ if (chk == 0)
+ return chk;
+
+ /* compensate for blanks in chksum header */
+ for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
+ chk-=(0xFF & *jp++);
+
+ chk += ' ' * sizeof(hb->dbuf.chksum);
+
+ fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
+
+ DEBUG(5, ("checksum totals chk=%d fchk=%d chksum=%s\n",
+ chk, fchk, hb->dbuf.chksum));
+
+ if (fchk != chk)
+ {
+ DEBUG(0, ("checksums don't match %d %d\n", fchk, chk));
+ return -1;
+ }
+
+ strcpy(finfo->name, prefix);
+
+ /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
+ unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
+ strlen(hb->dbuf.name) + 1);
+
+/* can't handle links at present */
+ if (hb->dbuf.linkflag != '0') {
+ if (hb->dbuf.linkflag == 0) {
+ DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
+ finfo->name));
+ } else {
+ DEBUG(0, ("this tar file appears to contain some kind of link - ignoring\n"));
+ return -2;
+ }
+ }
+
+ if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR)
+ || (*(finfo->name+strlen(finfo->name)-1) == '\\'))
+ {
+ finfo->mode=aDIR;
+ }
+ else
+ finfo->mode=0; /* we don't care about mode at the moment, we'll
+ * just make it a regular file */
+ /*
+ * Bug fix by richard@sj.co.uk
+ *
+ * REC: restore times correctly (as does tar)
+ * We only get the modification time of the file; set the creation time
+ * from the mod. time, and the access time to current time
+ */
+ finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
+ finfo->atime = time(NULL);
+ finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
+
+ return True;
+}
+
+/****************************************************************************
+Write out the tar buffer to tape or wherever
+****************************************************************************/
+static int dotarbuf(int f, char *b, int n)
+{
+ int fail=1, writ=n;
+
+ /* This routine and the next one should be the only ones that do write()s */
+ if (tp + n >= tbufsiz)
+ {
+ int diff;
+
+ diff=tbufsiz-tp;
+ memcpy(tarbuf + tp, b, diff);
+ fail=fail && (1+write(f, tarbuf, tbufsiz));
+ n-=diff;
+ b+=diff;
+ tp=0;
+
+ while (n >= tbufsiz)
+ {
+ fail=fail && (1 + write(f, b, tbufsiz));
+ n-=tbufsiz;
+ b+=tbufsiz;
+ }
+ }
+ if (n>0) {
+ memcpy(tarbuf+tp, b, n);
+ tp+=n;
+ }
+
+ return(fail ? writ : 0);
+}
+
+/****************************************************************************
+Write a zeros to buffer / tape
+****************************************************************************/
+static void dozerobuf(int f, int n)
+{
+ /* short routine just to write out n zeros to buffer -
+ * used to round files to nearest block
+ * and to do tar EOFs */
+
+ if (n+tp >= tbufsiz)
+ {
+ memset(tarbuf+tp, 0, tbufsiz-tp);
+ write(f, tarbuf, tbufsiz);
+ memset(tarbuf, 0, (tp+=n-tbufsiz));
+ }
+ else
+ {
+ memset(tarbuf+tp, 0, n);
+ tp+=n;
+ }
+}
+
+/****************************************************************************
+Malloc tape buffer
+****************************************************************************/
+static void initarbuf()
+{
+ /* initialize tar buffer */
+ tbufsiz=blocksize*TBLOCK;
+ tarbuf=malloc(tbufsiz);
+
+ /* reset tar buffer pointer and tar file counter */
+ tp=0; ntarf=0;
+}
+
+/****************************************************************************
+Write two zero blocks at end of file
+****************************************************************************/
+static void dotareof(int f)
+{
+ struct stat stbuf;
+ /* Two zero blocks at end of file, write out full buffer */
+
+ (void) dozerobuf(f, TBLOCK);
+ (void) dozerobuf(f, TBLOCK);
+
+ if (fstat(f, &stbuf) == -1)
+ {
+ DEBUG(0, ("Couldn't stat file handle\n"));
+ return;
+ }
+
+ /* Could be a pipe, in which case S_ISREG should fail,
+ * and we should write out at full size */
+ if (tp > 0) write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
+}
+
+/****************************************************************************
+(Un)mangle DOS pathname, make nonabsolute
+****************************************************************************/
+static void fixtarname(char *tptr, char *fp, int l)
+{
+ /* add a '.' to start of file name, convert from ugly dos \'s in path
+ * to lovely unix /'s :-} */
+
+ *tptr++='.';
+#ifdef KANJI
+ while (l > 0) {
+ if (is_shift_jis (*fp)) {
+ *tptr++ = *fp++;
+ *tptr++ = *fp++;
+ l -= 2;
+ } else if (is_kana (*fp)) {
+ *tptr++ = *fp++;
+ l--;
+ } else if (*fp == '\\') {
+ *tptr++ = '/';
+ fp++;
+ l--;
+ } else {
+ *tptr++ = *fp++;
+ l--;
+ }
+ }
+#else
+ while (l--) { *tptr=(*fp == '\\') ? '/' : *fp; tptr++; fp++; }
+#endif
+}
+
+/****************************************************************************
+Convert from decimal to octal string
+****************************************************************************/
+static void oct_it (register long value, register int ndgs, register char *p)
+{
+ /* Converts long to octal string, pads with leading zeros */
+
+ /* skip final null, but do final space */
+ --ndgs;
+ p[--ndgs] = ' ';
+
+ /* Loop does at least one digit */
+ do {
+ p[--ndgs] = '0' + (char) (value & 7);
+ value >>= 3;
+ }
+ while (ndgs > 0 && value != 0);
+
+ /* Do leading zeros */
+ while (ndgs > 0)
+ p[--ndgs] = '0';
+}
+
+/****************************************************************************
+Convert from octal string to long
+***************************************************************************/
+static long unoct(char *p, int ndgs)
+{
+ long value=0;
+ /* Converts octal string to long, ignoring any non-digit */
+
+ while (--ndgs)
+ {
+ if (isdigit(*p))
+ value = (value << 3) | (long) (*p - '0');
+
+ p++;
+ }
+
+ return value;
+}
+
+/****************************************************************************
+Compare two strings in a slash insensitive way
+***************************************************************************/
+int strslashcmp(const char *s1, const char *s2)
+{
+ while(*s1 && *s2 &&
+ (*s1 == *s2
+ || tolower(*s1) == tolower(*s2)
+ || (*s1 == '\\' && *s2=='/')
+ || (*s1 == '/' && *s2=='\\'))) {
+ s1++; s2++;
+ }
+
+ return *s1-*s2;
+}
+
+/*
+ * general smb utility functions
+ */
+/****************************************************************************
+Set DOS file attributes
+***************************************************************************/
+static int do_setrattr(char *fname, int attr, int setit)
+{
+ /*
+ * First get the existing attribs from existing file
+ */
+ char *inbuf,*outbuf;
+ char *p;
+ pstring name;
+ int fattr;
+
+ strcpy(name,fname);
+ strcpy(fname,"\\");
+ strcat(fname,name);
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return False;
+ }
+
+ /* send an smb getatr message */
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,0,2 + strlen(fname),True);
+ CVAL(outbuf,smb_com) = SMBgetatr;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,fname);
+ p += (strlen(fname)+1);
+
+ *p++ = 4;
+ *p++ = 0;
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ DEBUG(5,("getatr: %s\n",smb_errstr(inbuf)));
+ else
+ {
+ DEBUG(5,("\nattr 0x%X time %d size %d\n",
+ (int)CVAL(inbuf,smb_vwv0),
+ SVAL(inbuf,smb_vwv1),
+ SVAL(inbuf,smb_vwv3)));
+ }
+
+ fattr=CVAL(inbuf,smb_vwv0);
+
+ /* combine found attributes with bits to be set or reset */
+
+ attr=setit ? (fattr | attr) : (fattr & ~attr);
+
+ /* now try and set attributes by sending smb reset message */
+
+ /* clear out buffer and start again */
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,8,4 + strlen(fname),True);
+ CVAL(outbuf,smb_com) = SMBsetatr;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,attr);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,fname);
+ p += (strlen(fname)+1);
+
+ *p++ = 4;
+ *p++ = 0;
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s setting attributes on file %s\n",
+ smb_errstr(inbuf), fname));
+ free(inbuf);free(outbuf);
+ return(False);
+ }
+
+ free(inbuf);free(outbuf);
+ return(True);
+}
+
+/****************************************************************************
+Create a file on a share
+***************************************************************************/
+static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf)
+{
+ char *p;
+ /* *must* be called with buffer ready malloc'ed */
+ /* open remote file */
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,3,2 + strlen(finfo.name),True);
+ CVAL(outbuf,smb_com) = SMBcreate;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,finfo.mode);
+ put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,finfo.name);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),
+ finfo.name));
+ return 0;
+ }
+
+ *fnum = SVAL(inbuf,smb_vwv0);
+ return True;
+}
+
+/****************************************************************************
+Write a file to a share
+***************************************************************************/
+static BOOL smbwrite(int fnum, int n, int low, int high, int left,
+ char *bufferp, char *inbuf, char *outbuf)
+{
+ /* *must* be called with buffer ready malloc'ed */
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,5,n + 3,True);
+
+ memcpy(smb_buf(outbuf)+3, bufferp, n);
+
+ set_message(outbuf,5,n + 3, False);
+ CVAL(outbuf,smb_com) = SMBwrite;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,n);
+ SIVAL(outbuf,smb_vwv2,low);
+ SSVAL(outbuf,smb_vwv4,left);
+ CVAL(smb_buf(outbuf),0) = 1;
+ SSVAL(smb_buf(outbuf),1,n);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf)));
+ return False;
+ }
+
+ if (n != SVAL(inbuf,smb_vwv0))
+ {
+ DEBUG(0,("Error: only wrote %d bytes out of %d\n",
+ SVAL(inbuf,smb_vwv0), n));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Close a file on a share
+***************************************************************************/
+static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf)
+{
+ /* *must* be called with buffer ready malloc'ed */
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,3,0,True);
+ CVAL(outbuf,smb_com) = SMBclose;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
+
+ DEBUG(3,("Setting date to %s (0x%X)",
+ asctime(LocalTime(&finfo.mtime,GMT_TO_LOCAL)),
+ finfo.mtime));
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),
+ finfo.name));
+ return False;
+ }
+
+ return True;
+}
+
+/****************************************************************************
+Verify existence of path on share
+***************************************************************************/
+static BOOL smbchkpath(char *fname, char *inbuf, char *outbuf)
+{
+ char *p;
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,0,4 + strlen(fname),True);
+ CVAL(outbuf,smb_com) = SMBchkpth;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,fname);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ DEBUG(5,("smbchkpath: %s\n",smb_errstr(inbuf)));
+
+ return(CVAL(inbuf,smb_rcls) == 0);
+}
+
+/****************************************************************************
+Make a directory on share
+***************************************************************************/
+static BOOL smbmkdir(char *fname, char *inbuf, char *outbuf)
+{
+ /* *must* be called with buffer ready malloc'ed */
+ char *p;
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,0,2 + strlen(fname),True);
+
+ CVAL(outbuf,smb_com) = SMBmkdir;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ p = smb_buf(outbuf);
+ *p++ = 4;
+ strcpy(p,fname);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("%s making remote directory %s\n",
+ smb_errstr(inbuf),fname));
+ return(False);
+ }
+
+ return(True);
+}
+
+/****************************************************************************
+Ensure a remote path exists (make if necessary)
+***************************************************************************/
+static BOOL ensurepath(char *fname, char *inbuf, char *outbuf)
+{
+ /* *must* be called with buffer ready malloc'ed */
+ /* ensures path exists */
+
+ pstring partpath, ffname;
+ char *p=fname, *basehack;
+
+ *partpath = 0;
+
+ /* fname copied to ffname so can strtok */
+
+ strcpy(ffname, fname);
+
+ /* do a `basename' on ffname, so don't try and make file name directory */
+ if ((basehack=strrchr(ffname, '\\')) == NULL)
+ return True;
+ else
+ *basehack='\0';
+
+ p=strtok(ffname, "\\");
+
+ while (p)
+ {
+ strcat(partpath, p);
+
+ if (!smbchkpath(partpath, inbuf, outbuf)) {
+ if (!smbmkdir(partpath, inbuf, outbuf))
+ {
+ DEBUG(0, ("Error mkdirhiering\n"));
+ return False;
+ }
+ else
+ DEBUG(3, ("mkdirhiering %s\n", partpath));
+
+ }
+
+ strcat(partpath, "\\");
+ p = strtok(NULL,"/\\");
+ }
+
+ return True;
+}
+
+/*
+ * smbclient functions
+ */
+/****************************************************************************
+append one remote file to the tar file
+***************************************************************************/
+static void do_atar(char *rname,char *lname,file_info *finfo1)
+{
+ int fnum;
+ uint32 nread=0;
+ char *p;
+ char *inbuf,*outbuf;
+ file_info finfo;
+ BOOL close_done = False;
+ BOOL shallitime=True;
+ BOOL ignore_close_error = False;
+ char *dataptr=NULL;
+ int datalen=0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (finfo1)
+ finfo = *finfo1;
+ else
+ finfo = def_finfo;
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,15,1 + strlen(rname),True);
+
+ CVAL(outbuf,smb_com) = SMBopenX;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,0xFF);
+ SSVAL(outbuf,smb_vwv2,1);
+ SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4));
+ SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
+ SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN);
+ SSVAL(outbuf,smb_vwv8,1);
+
+ p = smb_buf(outbuf);
+ strcpy(p,rname);
+ p = skip_string(p,1);
+
+ dos_clean_name(rname);
+
+ /* do a chained openX with a readX? */
+ if (finfo.size > 0)
+ {
+ SSVAL(outbuf,smb_vwv0,SMBreadX);
+ SSVAL(outbuf,smb_vwv1,PTR_DIFF(p,outbuf) - 4);
+ memset(p,0,200);
+ p -= smb_wct;
+ SSVAL(p,smb_wct,10);
+ SSVAL(p,smb_vwv0,0xFF);
+ SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size));
+ SSVAL(p,smb_vwv9,MIN(0xFFFF,finfo.size));
+ smb_setlen(outbuf,smb_len(outbuf)+11*2+1);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ if (CVAL(inbuf,smb_rcls) == ERRSRV &&
+ SVAL(inbuf,smb_err) == ERRnoresource &&
+ reopen_connection(inbuf,outbuf))
+ {
+ do_atar(rname,lname,finfo1);
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),rname));
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ strcpy(finfo.name,rname);
+ if (!finfo1)
+ {
+ finfo.mode = SVAL(inbuf,smb_vwv3);
+ finfo.size = IVAL(inbuf,smb_vwv4);
+ finfo.mtime = make_unix_date3(inbuf+smb_vwv6);
+ finfo.atime = finfo.ctime = finfo.mtime;
+ }
+
+ DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
+
+ fnum = SVAL(inbuf,smb_vwv2);
+
+ if (tar_inc && !(finfo.mode & aARCH))
+ {
+ DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
+ shallitime=0;
+ }
+ else
+ {
+ if (SVAL(inbuf,smb_vwv0) == SMBreadX)
+ {
+ p = (inbuf+4+SVAL(inbuf,smb_vwv1)) - smb_wct;
+ datalen = SVAL(p,smb_vwv5);
+ dataptr = inbuf + 4 + SVAL(p,smb_vwv6);
+ }
+ else
+ {
+ dataptr = NULL;
+ datalen = 0;
+ }
+
+ DEBUG(2,("getting file %s of size %d bytes as a tar file %s",
+ finfo.name,
+ finfo.size,
+ lname));
+
+ /* write a tar header, don't bother with mode - just set to 100644 */
+ writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0");
+
+ while (nread < finfo.size && !close_done)
+ {
+ int method = -1;
+ static BOOL can_chain_close=True;
+
+ p=NULL;
+
+ DEBUG(3,("nread=%d\n",nread));
+
+ /* 3 possible read types. readbraw if a large block is required.
+ readX + close if not much left and read if neither is supported */
+
+ /* we might have already read some data from a chained readX */
+ if (dataptr && datalen>0)
+ method=3;
+
+ /* if we can finish now then readX+close */
+ if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) &&
+ ((finfo.size - nread) <
+ (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300))))
+ method = 0;
+
+ /* if we support readraw then use that */
+ if (method<0 && readbraw_supported)
+ method = 1;
+
+ /* if we can then use readX */
+ if (method<0 && (Protocol >= PROTOCOL_LANMAN1))
+ method = 2;
+
+
+ switch (method)
+ {
+ /* use readX */
+ case 0:
+ case 2:
+ if (method == 0)
+ close_done = True;
+
+ /* use readX + close */
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,10,0,True);
+ CVAL(outbuf,smb_com) = SMBreadX;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ if (close_done)
+ {
+ CVAL(outbuf,smb_vwv0) = SMBclose;
+ SSVAL(outbuf,smb_vwv1,PTR_DIFF(smb_buf(outbuf),outbuf) - 4);
+ }
+ else
+ CVAL(outbuf,smb_vwv0) = 0xFF;
+
+
+ SSVAL(outbuf,smb_vwv2,fnum);
+ SIVAL(outbuf,smb_vwv3,nread);
+ SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread));
+ SSVAL(outbuf,smb_vwv6,0);
+ SIVAL(outbuf,smb_vwv7,0);
+ SSVAL(outbuf,smb_vwv9,MIN(0xFFFF,finfo.size-nread));
+
+ if (close_done)
+ {
+ p = smb_buf(outbuf);
+ memset(p,0,9);
+
+ CVAL(p,0) = 3;
+ SSVAL(p,1,fnum);
+ SIVALS(p,3,-1);
+
+ /* now set the total packet length */
+ smb_setlen(outbuf,smb_len(outbuf)+9);
+ }
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+ break;
+ }
+
+ if (close_done &&
+ SVAL(inbuf,smb_vwv0) != SMBclose)
+ {
+ /* NOTE: WfWg sometimes just ignores the chained
+ command! This seems to break the spec? */
+ DEBUG(3,("Rejected chained close?\n"));
+ close_done = False;
+ can_chain_close = False;
+ ignore_close_error = True;
+ }
+
+ datalen = SVAL(inbuf,smb_vwv5);
+ dataptr = inbuf + 4 + SVAL(inbuf,smb_vwv6);
+ break;
+
+
+ /* use readbraw */
+ case 1:
+ {
+ static int readbraw_size = 0xFFFF;
+
+ extern int Client;
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,8,0,True);
+ CVAL(outbuf,smb_com) = SMBreadbraw;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SIVAL(outbuf,smb_vwv1,nread);
+ SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
+ SSVAL(outbuf,smb_vwv4,0);
+ SIVALS(outbuf,smb_vwv5,-1);
+ send_smb(Client,outbuf);
+
+ /* Now read the raw data into the buffer and write it */
+ if(read_smb_length(Client,inbuf,0) == -1) {
+ DEBUG(0,("Failed to read length in readbraw\n"));
+ exit(1);
+ }
+
+ /* Even though this is not an smb message, smb_len
+ returns the generic length of an smb message */
+ datalen = smb_len(inbuf);
+
+ if (datalen == 0)
+ {
+ /* we got a readbraw error */
+ DEBUG(4,("readbraw error - reducing size\n"));
+ readbraw_size = (readbraw_size * 9) / 10;
+
+ if (readbraw_size < max_xmit)
+ {
+ DEBUG(0,("disabling readbraw\n"));
+ readbraw_supported = False;
+ }
+
+ dataptr=NULL;
+ continue;
+ }
+
+ if(read_data(Client,inbuf,datalen) != datalen) {
+ DEBUG(0,("Failed to read data in readbraw\n"));
+ exit(1);
+ }
+ dataptr = inbuf;
+ }
+ break;
+
+ case 3:
+ /* we've already read some data with a chained readX */
+ break;
+
+ default:
+ /* use plain read */
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,5,0,True);
+ CVAL(outbuf,smb_com) = SMBread;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
+ SIVAL(outbuf,smb_vwv2,nread);
+ SSVAL(outbuf,smb_vwv4,finfo.size - nread);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
+ break;
+ }
+
+ datalen = SVAL(inbuf,smb_vwv0);
+ dataptr = smb_buf(inbuf) + 3;
+ break;
+ }
+
+
+ /* add received bits of file to buffer - dotarbuf will
+ * write out in 512 byte intervals */
+ if (dotarbuf(tarhandle,dataptr,datalen) != datalen)
+ {
+ DEBUG(0,("Error writing local file\n"));
+ break;
+ }
+
+ nread += datalen;
+ if (datalen == 0)
+ {
+ DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
+ break;
+ }
+
+ dataptr=NULL;
+ datalen=0;
+ }
+
+ /* round tar file to nearest block */
+ if (finfo.size % TBLOCK)
+ dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
+
+ ntarf++;
+ }
+
+ if (!close_done)
+ {
+ memset(outbuf,0,smb_size);
+ set_message(outbuf,3,0,True);
+ CVAL(outbuf,smb_com) = SMBclose;
+ SSVAL(outbuf,smb_tid,cnum);
+ setup_pkt(outbuf);
+
+ SSVAL(outbuf,smb_vwv0,fnum);
+ SIVALS(outbuf,smb_vwv1,-1);
+
+ send_smb(Client,outbuf);
+ receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+
+ if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
+ {
+ DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf)));
+ free(inbuf);free(outbuf);
+ return;
+ }
+ }
+
+ if (shallitime)
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ /* if shallitime is true then we didn't skip */
+ if (tar_reset) (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += finfo.size;
+
+ /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
+ DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+ finfo.size / MAX(0.001, (1.024*this_time)),
+ get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
+ }
+
+ free(inbuf);free(outbuf);
+}
+
+/****************************************************************************
+Append single file to tar file (or not)
+***************************************************************************/
+static void do_tar(file_info *finfo)
+{
+ pstring rname;
+
+ if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+ return;
+
+ /* Is it on the exclude list ? */
+ if (!tar_excl && clipn) {
+ pstring exclaim;
+
+ strcpy(exclaim, cur_dir);
+ *(exclaim+strlen(exclaim)-1)='\0';
+
+ if (clipfind(cliplist, clipn, exclaim)) {
+ DEBUG(3,("Skipping directory %s\n", exclaim));
+ return;
+ }
+
+ strcat(exclaim, "\\");
+ strcat(exclaim, finfo->name);
+
+ if (clipfind(cliplist, clipn, exclaim)) {
+ DEBUG(3,("Skipping file %s\n", exclaim));
+ return;
+ }
+ }
+
+ if (finfo->mode & aDIR)
+ {
+ pstring saved_curdir;
+ pstring mtar_mask;
+ char *inbuf,*outbuf;
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ strcpy(saved_curdir,cur_dir);
+
+ strcat(cur_dir,finfo->name);
+ strcat(cur_dir,"\\");
+
+ /* write a tar directory, don't bother with mode - just set it to
+ * 40755 */
+ writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0");
+ strcpy(mtar_mask,cur_dir);
+ strcat(mtar_mask,"*");
+
+ do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse);
+ strcpy(cur_dir,saved_curdir);
+ free(inbuf);free(outbuf);
+ }
+ else
+ {
+ strcpy(rname,cur_dir);
+ strcat(rname,finfo->name);
+ do_atar(rname,finfo->name,finfo);
+ }
+}
+
+/****************************************************************************
+Convert from UNIX to DOS file names
+***************************************************************************/
+static void unfixtarname(char *tptr, char *fp, int l)
+{
+ /* remove '.' from start of file name, convert from unix /'s to
+ * dos \'s in path. Kill any absolute path names.
+ */
+
+ if (*fp == '.') fp++;
+ if (*fp == '\\' || *fp == '/') fp++;
+
+#ifdef KANJI
+ while (l > 0) {
+ if (is_shift_jis (*fp)) {
+ *tptr++ = *fp++;
+ *tptr++ = *fp++;
+ l -= 2;
+ } else if (is_kana (*fp)) {
+ *tptr++ = *fp++;
+ l--;
+ } else if (*fp == '/') {
+ *tptr++ = '\\';
+ fp++;
+ l--;
+ } else {
+ *tptr++ = *fp++;
+ l--;
+ }
+ }
+#else
+ while (l--) { *tptr=(*fp == '/') ? '\\' : *fp; tptr++; fp++; }
+#endif
+}
+
+static void do_tarput()
+{
+ file_info finfo;
+ int nread=0, bufread;
+ char *inbuf,*outbuf;
+ int fsize=0;
+ int fnum;
+ struct timeval tp_start;
+ BOOL tskip=False; /* We'll take each file as it comes */
+
+ GetTimeOfDay(&tp_start);
+
+ inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+ outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+ if (!inbuf || !outbuf)
+ {
+ DEBUG(0,("out of memory\n"));
+ return;
+ }
+
+ /*
+ * Must read in tbufsiz dollops
+ */
+
+ /* These should be the only reads in clitar.c */
+ while ((bufread=read(tarhandle, tarbuf, tbufsiz))>0) {
+ char *bufferp, *endofbuffer;
+ int chunk;
+
+ /* Code to handle a short read.
+ * We always need a TBLOCK full of stuff
+ */
+ if (bufread % TBLOCK) {
+ int lchunk=TBLOCK-(bufread % TBLOCK);
+ int lread;
+
+ /* It's a shorty - a short read that is */
+ DEBUG(3, ("Short read, read %d so far (need %d)\n", bufread, lchunk));
+
+ while ((lread=read(tarhandle, tarbuf+bufread, lchunk))>0) {
+ bufread+=lread;
+ if (!(lchunk-=lread)) break;
+ }
+
+ /* If we've reached EOF then that must be a short file */
+ if (lread<=0) break;
+ }
+
+ bufferp=tarbuf;
+ endofbuffer=tarbuf+bufread;
+
+ if (tskip) {
+ if (fsize<bufread) {
+ tskip=False;
+ bufferp+=fsize;
+ fsize=0;
+ } else {
+ if (fsize==bufread) tskip=False;
+ fsize-=bufread;
+ continue;
+ }
+ }
+
+ do {
+ if (!fsize)
+ {
+ switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir))
+ {
+ case -2: /* something dodgy but not fatal about this */
+ DEBUG(0, ("skipping %s...\n", finfo.name));
+ bufferp+=TBLOCK; /* header - like a link */
+ continue;
+ case -1:
+ DEBUG(0, ("abandoning restore\n"));
+ free(inbuf); free(outbuf);
+ return;
+ case 0: /* chksum is zero - we assume that one all zero
+ *header block will do for eof */
+ DEBUG(0,
+ ("total of %d tar files restored to share\n", ntarf));
+ free(inbuf); free(outbuf);
+ return;
+ default:
+ break;
+ }
+
+ tskip=clipn
+ && (clipfind(cliplist, clipn, finfo.name) ^ tar_excl);
+ if (tskip) {
+ bufferp+=TBLOCK;
+ if (finfo.mode & aDIR)
+ continue;
+ else if ((fsize=finfo.size) % TBLOCK) {
+ fsize+=TBLOCK-(fsize%TBLOCK);
+ }
+ if (fsize<endofbuffer-bufferp) {
+ bufferp+=fsize;
+ fsize=0;
+ continue;
+ } else {
+ fsize-=endofbuffer-bufferp;
+ break;
+ }
+ }
+
+ if (finfo.mode & aDIR)
+ {
+ if (!smbchkpath(finfo.name, inbuf, outbuf)
+ && !smbmkdir(finfo.name, inbuf, outbuf))
+ {
+ DEBUG(0, ("abandoning restore\n"));
+ free(inbuf); free(outbuf);
+ return;
+ }
+ else
+ {
+ bufferp+=TBLOCK;
+ continue;
+ }
+ }
+
+ fsize=finfo.size;
+
+ if (ensurepath(finfo.name, inbuf, outbuf)
+ && !smbcreat(finfo, &fnum, inbuf, outbuf))
+ {
+ DEBUG(0, ("abandoning restore\n"));
+ free(inbuf);free(outbuf);
+ return;
+ }
+
+ DEBUG(0,("restore tar file %s of size %d bytes\n",
+ finfo.name,finfo.size));
+
+ nread=0;
+ if ((bufferp+=TBLOCK) >= endofbuffer) break;
+ } /* if (!fsize) */
+
+ /* write out the file in chunk sized chunks - don't
+ * go past end of buffer though */
+ chunk=(fsize-nread < endofbuffer - bufferp)
+ ? fsize - nread : endofbuffer - bufferp;
+
+ while (chunk > 0) {
+ int minichunk=MIN(chunk, max_xmit-200);
+
+ if (!smbwrite(fnum, /* file descriptor */
+ minichunk, /* n */
+ nread, /* offset low */
+ 0, /* offset high - not implemented */
+ fsize-nread, /* left - only hint to server */
+ bufferp,
+ inbuf,
+ outbuf))
+ {
+ DEBUG(0, ("Error writing remote file\n"));
+ free(inbuf); free(outbuf);
+ return;
+ }
+ DEBUG(5, ("chunk writing fname=%s fnum=%d nread=%d minichunk=%d chunk=%d size=%d\n", finfo.name, fnum, nread, minichunk, chunk, fsize));
+
+ bufferp+=minichunk; nread+=minichunk;
+ chunk-=minichunk;
+ }
+
+ if (nread>=fsize)
+ {
+ if (!smbshut(finfo, fnum, inbuf, outbuf))
+ {
+ DEBUG(0, ("Error closing remote file\n"));
+ free(inbuf);free(outbuf);
+ return;
+ }
+ if (fsize % TBLOCK) bufferp+=TBLOCK - (fsize % TBLOCK);
+ DEBUG(5, ("bufferp is now %d (psn=%d)\n",
+ (long) bufferp, (long)(bufferp - tarbuf)));
+ ntarf++;
+ fsize=0;
+ }
+ } while (bufferp < endofbuffer);
+ }
+
+ DEBUG(0, ("premature eof on tar file ?\n"));
+ DEBUG(0,("total of %d tar files restored to share\n", ntarf));
+
+ free(inbuf); free(outbuf);
+}
+
+/*
+ * samba interactive commands
+ */
+
+/****************************************************************************
+Blocksize command
+***************************************************************************/
+void cmd_block(void)
+{
+ fstring buf;
+ int block;
+
+ if (!next_token(NULL,buf,NULL))
+ {
+ DEBUG(0, ("blocksize <n>\n"));
+ return;
+ }
+
+ block=atoi(buf);
+ if (block < 0 || block > 65535)
+ {
+ DEBUG(0, ("blocksize out of range"));
+ return;
+ }
+
+ blocksize=block;
+ DEBUG(2,("blocksize is now %d\n", blocksize));
+}
+
+/****************************************************************************
+command to set incremental / reset mode
+***************************************************************************/
+void cmd_tarmode(void)
+{
+ fstring buf;
+
+ while (next_token(NULL,buf,NULL)) {
+ if (strequal(buf, "full"))
+ tar_inc=False;
+ else if (strequal(buf, "inc"))
+ tar_inc=True;
+ else if (strequal(buf, "reset"))
+ tar_reset=True;
+ else if (strequal(buf, "noreset"))
+ tar_reset=False;
+ else DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
+ }
+
+ DEBUG(0, ("tarmode is now %s, %s\n",
+ tar_inc ? "incremental" : "full",
+ tar_reset ? "reset" : "noreset"));
+}
+
+/****************************************************************************
+Feeble attrib command
+***************************************************************************/
+void cmd_setmode(void)
+{
+ char *q;
+ fstring buf;
+ pstring fname;
+ int attra[2];
+ int direct=1;
+
+ attra[0] = attra[1] = 0;
+
+ if (!next_token(NULL,buf,NULL))
+ {
+ DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+ return;
+ }
+
+ strcpy(fname, cur_dir);
+ strcat(fname, buf);
+
+ while (next_token(NULL,buf,NULL)) {
+ q=buf;
+
+ while(*q)
+ switch (*q++) {
+ case '+': direct=1;
+ break;
+ case '-': direct=0;
+ break;
+ case 'r': attra[direct]|=aRONLY;
+ break;
+ case 'h': attra[direct]|=aHIDDEN;
+ break;
+ case 's': attra[direct]|=aSYSTEM;
+ break;
+ case 'a': attra[direct]|=aARCH;
+ break;
+ default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+ return;
+ }
+ }
+
+ if (attra[ATTRSET]==0 && attra[ATTRRESET]==0)
+ {
+ DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
+ return;
+ }
+
+DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+ (void) do_setrattr(fname, attra[ATTRSET], ATTRSET);
+ (void) do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
+}
+
+/****************************************************************************
+Principal command for creating / extracting
+***************************************************************************/
+void cmd_tar(char *inbuf, char *outbuf)
+{
+ fstring buf;
+ char **argl;
+ int argcl;
+
+ if (!next_token(NULL,buf,NULL))
+ {
+ DEBUG(0,("tar <c|x>[IXbga] <filename>\n"));
+ return;
+ }
+
+ argl=toktocliplist(&argcl, NULL);
+ if (!tar_parseargs(argcl, argl, buf, 0))
+ return;
+
+ process_tar(inbuf, outbuf);
+
+ free(argl);
+}
+
+/****************************************************************************
+Command line (option) version
+***************************************************************************/
+int process_tar(char *inbuf, char *outbuf)
+{
+ initarbuf();
+ switch(tar_type) {
+ case 'x':
+ do_tarput();
+ free(tarbuf);
+ close(tarhandle);
+ break;
+ case 'r':
+ case 'c':
+ if (clipn && tar_excl) {
+ int i;
+ pstring tarmac;
+
+ for (i=0; i<clipn; i++) {
+ DEBUG(0,("arg %d = %s\n", i, cliplist[i]));
+
+ if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
+ *(cliplist[i]+strlen(cliplist[i])-1)='\0';
+ }
+
+ if (strrchr(cliplist[i], '\\')) {
+ pstring saved_dir;
+
+ strcpy(saved_dir, cur_dir);
+
+ if (*cliplist[i]=='\\') {
+ strcpy(tarmac, cliplist[i]);
+ } else {
+ strcpy(tarmac, cur_dir);
+ strcat(tarmac, cliplist[i]);
+ }
+ strcpy(cur_dir, tarmac);
+ *(strrchr(cur_dir, '\\')+1)='\0';
+
+ do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+ strcpy(cur_dir,saved_dir);
+ } else {
+ strcpy(tarmac, cur_dir);
+ strcat(tarmac, cliplist[i]);
+ do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+ }
+ }
+ } else {
+ pstring mask;
+ strcpy(mask,cur_dir);
+ strcat(mask,"\\*");
+ do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse);
+ }
+
+ if (ntarf) dotareof(tarhandle);
+ close(tarhandle);
+ free(tarbuf);
+
+ DEBUG(0, ("tar: dumped %d tar files\n", ntarf));
+ break;
+ }
+
+ return(0);
+}
+
+/****************************************************************************
+Find a token (filename) in a clip list
+***************************************************************************/
+int clipfind(char **aret, int ret, char *tok)
+{
+ if (aret==NULL) return 0;
+
+ /* ignore leading slashes or dots in token */
+ while(strchr("/\\.", *tok)) tok++;
+
+ while(ret--) {
+ char *pkey=*aret++;
+
+ /* ignore leading slashes or dots in list */
+ while(strchr("/\\.", *pkey)) pkey++;
+
+ if (!strslashcmp(pkey, tok)) return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+Parse tar arguments. Sets tar_type, tar_excl, etc.
+***************************************************************************/
+int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind)
+{
+ char tar_clipfl='\0';
+
+ /* Reset back to defaults - could be from interactive version
+ * reset mode and archive mode left as they are though
+ */
+ tar_type='\0';
+ tar_excl=True;
+
+ while (*Optarg)
+ switch(*Optarg++) {
+ case 'c':
+ tar_type='c';
+ break;
+ case 'x':
+ if (tar_type=='c') {
+ printf("Tar must be followed by only one of c or x.\n");
+ return 0;
+ }
+ tar_type='x';
+ break;
+ case 'b':
+ if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
+ DEBUG(0,("Option b must be followed by valid blocksize\n"));
+ return 0;
+ } else {
+ Optind++;
+ }
+ break;
+ case 'g':
+ tar_inc=True;
+ break;
+ case 'N':
+ if (Optind>=argc) {
+ DEBUG(0,("Option N must be followed by valid file name\n"));
+ return 0;
+ } else {
+ struct stat stbuf;
+ extern time_t newer_than;
+
+ if (sys_stat(argv[Optind], &stbuf) == 0) {
+ newer_than = stbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(LocalTime(&newer_than,GMT_TO_LOCAL))));
+ Optind++;
+ } else {
+ DEBUG(0,("Error setting newer-than time\n"));
+ return 0;
+ }
+ }
+ break;
+ case 'a':
+ tar_reset=True;
+ break;
+ case 'I':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='I';
+ break;
+ case 'X':
+ if (tar_clipfl) {
+ DEBUG(0,("Only one of I,X must be specified\n"));
+ return 0;
+ }
+ tar_clipfl='X';
+ break;
+ default:
+ DEBUG(0,("Unknown tar option\n"));
+ return 0;
+ }
+
+ if (!tar_type) {
+ printf("Option T must be followed by one of c or x.\n");
+ return 0;
+ }
+
+ if (Optind>=argc || !strcmp(argv[Optind], "-")) {
+ /* Sets tar handle to either 0 or 1, as appropriate */
+ tarhandle=(tar_type=='c');
+ } else {
+ tar_excl=tar_clipfl!='X';
+
+ if (Optind+1<argc) {
+ cliplist=argv+Optind+1;
+ clipn=argc-Optind-1;
+ }
+
+ if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY)) == -1)
+ || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0))
+ {
+ DEBUG(0,("Error opening local file %s\n",argv[Optind]));
+ return(0);
+ }
+ }
+
+ return 1;
+}