a little hack to smbclient to support extracting NT error codes
[kai/samba.git] / source3 / client / client.c
index fbd208f91ac940f59e745e4b2e6dcb5e791cba88..403eea2fc99f5aaeb7b7151e0fc03fe7d3939ef1 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    SMB client
-   Copyright (C) Andrew Tridgell 1994-1995
+   Copyright (C) Andrew Tridgell 1994-1997
    
    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
@@ -33,10 +33,11 @@ pstring cur_dir = "\\";
 pstring cd_path = "";
 pstring service="";
 pstring desthost="";
-pstring myname = "";
+extern pstring myname;
+extern pstring myhostname;
 pstring password = "";
 pstring username="";
-pstring workgroup=WORKGROUP;
+pstring workgroup="";
 char *cmdstr="";
 BOOL got_pass = False;
 BOOL connect_as_printer = False;
@@ -85,7 +86,7 @@ static BOOL call_api(int prcnt,int drcnt,
                     int *rprcnt,int *rdrcnt,
                     char *param,char *data,
                     char **rparam,char **rdata);
-
+static BOOL do_this_one(file_info *finfo);
 
 /* clitar bits insert */
 extern int blocksize;
@@ -135,17 +136,15 @@ int get_total_time_ms = 0;
 int put_total_size = 0;
 int put_total_time_ms = 0;
 
+/* totals globals */
+int dir_total = 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)
+static BOOL setup_term_code (char *code)
 {
     int new;
     new = interpret_coding_system (code, UNKNOWN_CODE);
@@ -155,10 +154,8 @@ setup_term_code (char *code)
     }
     return False;
 }
-#else
 #define CNV_LANG(s) dos2unix_format(s,False)
 #define CNV_INPUT(s) unix2dos_format(s,True)
-#endif
 
 /****************************************************************************
 setup basics in a outgoing packet
@@ -168,7 +165,7 @@ void setup_pkt(char *outbuf)
   SSVAL(outbuf,smb_pid,pid);
   SSVAL(outbuf,smb_uid,uid);
   SSVAL(outbuf,smb_mid,mid);
-  if (Protocol > PROTOCOL_CORE)
+  if (Protocol > PROTOCOL_COREPLUS)
     {
       SCVAL(outbuf,smb_flg,0x8);
       SSVAL(outbuf,smb_flg2,0x1);
@@ -230,7 +227,8 @@ static int readfile(char *b, int size, int n, FILE *f)
          n++;
        }
       
-      b[i++] = c;
+      if(i < n)
+        b[i++] = c;
     }
   
   return(i);
@@ -277,6 +275,18 @@ static BOOL chkpath(char *path,BOOL report)
   *p++ = 4;
   strcpy(p,path2);
 
+#if 0
+  {
+         /* this little bit of code can be used to extract NT error codes.
+            Just feed a bunch of "cd foo" commands to smbclient then watch
+            in netmon (tridge) */
+         static int code=0;
+         SIVAL(outbuf, smb_rcls, code | 0xC0000000);
+         SSVAL(outbuf, smb_flg2, SVAL(outbuf, smb_flg2) | (1<<14));
+         code++;
+  }
+#endif
+
   send_smb(Client,outbuf);
   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
 
@@ -473,12 +483,15 @@ static void cmd_cd(char *inbuf,char *outbuf)
   ****************************************************************************/
 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),
+  if (do_this_one(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))));
+    dir_total += finfo->size;
+  }
 }
 
 
@@ -520,7 +533,7 @@ static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*
       loop_count++;
       if (loop_count > 200)
        {
-         DEBUG(0,("ERROR: Looping in FIND_NEXT??\n"));
+         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
          break;
        }
 
@@ -768,7 +781,7 @@ static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (
       memcpy(p,status,21);
 
       send_smb(Client,outbuf);
-      receive_smb(Client,inbuf,CLIENT_TIMEOUT,False);
+      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
 
       if (CVAL(inbuf,smb_rcls) != 0) 
        DEBUG(0,("Error closing search: %s\n",smb_errstr(inbuf)));      
@@ -827,6 +840,34 @@ static BOOL do_this_one(file_info *finfo)
   return(True);
 }
 
+
+/*****************************************************************************
+ Convert a character pointer in a call_api() response to a form we can use.
+ This function contains code to prevent core dumps if the server returns 
+ invalid data.
+*****************************************************************************/
+static char *fix_char_ptr(unsigned int datap, unsigned int converter, char *rdata, int rdrcnt)
+{
+if( datap == 0 )               /* turn NULL pointers */
+  {                            /* into zero length strings */
+  return "";
+  }
+else
+  {
+  unsigned int offset = datap - converter;
+
+  if( offset >= rdrcnt )
+    {
+      DEBUG(1,("bad char ptr: datap=%u, converter=%u, rdata=%lu, rdrcnt=%d>", datap, converter, (unsigned long)rdata, rdrcnt));
+    return "<ERROR>";
+    }
+  else
+    {
+    return &rdata[offset];
+    }
+  }
+}
+
 /****************************************************************************
 interpret a short filename structure
 The length of the structure is returned
@@ -1076,6 +1117,7 @@ static void cmd_dir(char *inbuf,char *outbuf)
   fstring buf;
   char *p=buf;
 
+  dir_total = 0;
   strcpy(mask,cur_dir);
   if(mask[strlen(mask)-1]!='\\')
     strcat(mask,"\\");
@@ -1094,6 +1136,8 @@ static void cmd_dir(char *inbuf,char *outbuf)
   do_dir(inbuf,outbuf,mask,attribute,NULL,recurse);
 
   do_dskattr();
+
+  DEBUG(3, ("Total bytes listed: %d\n", dir_total));
 }
 
 
@@ -1493,7 +1537,7 @@ static void do_get(char *rname,char *lname,file_info *finfo1)
     get_total_time_ms += this_time;
     get_total_size += finfo.size;
 
-    DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+    DEBUG(1,("(%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)));
   }
@@ -1623,7 +1667,7 @@ static void cmd_more(void)
 
   strcpy(rname,cur_dir);
   strcat(rname,"\\");
-  sprintf(tmpname,"/tmp/smbmore.%d",getpid());
+  sprintf(tmpname,"%s/smbmore.%d",tmpdir(),(int)getpid());
   strcpy(lname,tmpname);
 
   if (!next_token(NULL,rname+strlen(rname),NULL)) {
@@ -1994,7 +2038,7 @@ static void do_put(char *rname,char *lname,file_info *finfo)
     put_total_time_ms += this_time;
     put_total_size += finfo->size;
 
-    DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
+    DEBUG(1,("(%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)));
   }
@@ -2097,7 +2141,7 @@ static void cmd_mput(void)
       pstring tmpname;
       FILE *f;
       
-      sprintf(tmpname,"/tmp/ls.smb.%d",(int)getpid());
+      sprintf(tmpname,"%s/ls.smb.%d",tmpdir(),(int)getpid());
       if (recurse)
        sprintf(cmd,"find . -name \"%s\" -print > %s",p,tmpname);
       else
@@ -2177,7 +2221,7 @@ static void do_cancel(int job)
   bzero(param,sizeof(param));
 
   p = param;
-  SSVAL(p,0,81);               /* api number */
+  SSVAL(p,0,81);               /* DosPrintJobDel() */
   p += 2;
   strcpy(p,"W");
   p = skip_string(p,1);
@@ -2424,7 +2468,8 @@ static void cmd_print(char *inbuf,char *outbuf )
 }
 
 /****************************************************************************
-print a file
+show a print queue - this is deprecated as it uses the old smb that
+has limited support - the correct call is the cmd_p_queue_4() after this.
 ****************************************************************************/
 static void cmd_queue(char *inbuf,char *outbuf )
 {
@@ -2485,6 +2530,222 @@ static void cmd_queue(char *inbuf,char *outbuf )
 }
 
 
+/****************************************************************************
+show information about a print queue
+****************************************************************************/
+static void cmd_p_queue_4(char *inbuf,char *outbuf )
+{
+  char *rparam = NULL;
+  char *rdata = NULL;
+  char *p;
+  int rdrcnt, rprcnt;
+  pstring param;
+  int result_code=0;
+
+  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"));
+    }
+  
+  bzero(param,sizeof(param));
+
+  p = param;
+  SSVAL(p,0,76);                        /* API function number 76 (DosPrintJobEnum) */
+  p += 2;
+  strcpy(p,"zWrLeh");                   /* parameter description? */
+  p = skip_string(p,1);
+  strcpy(p,"WWzWWDDzz");                /* returned data format */
+  p = skip_string(p,1);
+  strcpy(p,strrchr(service,'\\')+1);    /* name of queue */
+  p = skip_string(p,1);
+  SSVAL(p,0,2);                 /* API function level 2, PRJINFO_2 data structure */
+  SSVAL(p,2,1000);                      /* size of bytes of returned data buffer */
+  p += 4;
+  strcpy(p,"");                         /* subformat */
+  p = skip_string(p,1);
+
+  DEBUG(1,("Calling DosPrintJobEnum()...\n"));
+  if( call_api(PTR_DIFF(p,param), 0,
+               10, 4096,
+               &rprcnt, &rdrcnt,
+               param, NULL,
+               &rparam, &rdata) )
+    {
+      int converter;
+      result_code = SVAL(rparam,0);
+      converter = SVAL(rparam,2);             /* conversion factor */
+
+      DEBUG(2,("returned %d bytes of parameters, %d bytes of data, %d records\n", rprcnt, rdrcnt, SVAL(rparam,4) ));
+
+      if (result_code == 0)                   /* if no error, */
+        {
+          int i;
+          uint16 JobId;
+          uint16 Priority;
+          uint32 Size;
+          char *UserName;
+          char *JobName;
+          char *JobTimeStr;
+          time_t JobTime;
+          char PrinterName[20];
+             
+          strcpy(PrinterName,strrchr(service,'\\')+1);       /* name of queue */
+          strlower(PrinterName);                             /* in lower case */
+
+          p = rdata;                          /* received data */
+          for( i = 0; i < SVAL(rparam,4); ++i)
+            {
+              JobId = SVAL(p,0);
+              Priority = SVAL(p,2);
+              UserName = fix_char_ptr(SVAL(p,4), converter, rdata, rdrcnt);
+              strlower(UserName);
+              Priority = SVAL(p,2);
+              JobTime = make_unix_date3( p + 12);
+              JobTimeStr = asctime(LocalTime( &JobTime));
+              Size = IVAL(p,16);
+              JobName = fix_char_ptr(SVAL(p,24), converter, rdata, rdrcnt);
+            
+
+              printf("%s-%u    %s    priority %u   %s    %s   %u bytes\n", 
+               PrinterName, JobId, UserName,
+                Priority, JobTimeStr, JobName, Size);
+   
+#if 0 /* DEBUG code */
+              printf("Job Id: \"%u\"\n", SVAL(p,0));
+              printf("Priority: \"%u\"\n", SVAL(p,2));
+            
+              printf("User Name: \"%s\"\n", fix_char_ptr(SVAL(p,4), converter, rdata, rdrcnt) );
+              printf("Position: \"%u\"\n", SVAL(p,8));
+              printf("Status: \"%u\"\n", SVAL(p,10));
+            
+              JobTime = make_unix_date3( p + 12);
+              printf("Submitted: \"%s\"\n", asctime(LocalTime(&JobTime)));
+              printf("date: \"%u\"\n", SVAL(p,12));
+
+              printf("Size: \"%u\"\n", SVAL(p,16));
+              printf("Comment: \"%s\"\n", fix_char_ptr(SVAL(p,20), converter, rdata, rdrcnt) );
+              printf("Document: \"%s\"\n", fix_char_ptr(SVAL(p,24), converter, rdata, rdrcnt) );
+#endif /* DEBUG CODE */ 
+              p += 28;
+            }
+        }
+    }
+  else                  /* call_api() failed */
+    {
+      printf("Failed, error = %d\n", result_code);
+    }
+
+  /* If any parameters or data were returned, free the storage. */
+  if(rparam) free(rparam);
+  if(rdata) free(rdata);
+
+  return;
+}
+
+/****************************************************************************
+show information about a print queue
+****************************************************************************/
+static void cmd_qinfo(char *inbuf,char *outbuf )
+{
+  char *rparam = NULL;
+  char *rdata = NULL;
+  char *p;
+  int rdrcnt, rprcnt;
+  pstring param;
+  int result_code=0;
+  
+  bzero(param,sizeof(param));
+
+  p = param;
+  SSVAL(p,0,70);                       /* API function number 70 (DosPrintQGetInfo) */
+  p += 2;
+  strcpy(p,"zWrLh");                   /* parameter description? */
+  p = skip_string(p,1);
+  strcpy(p,"zWWWWzzzzWWzzl");          /* returned data format */
+  p = skip_string(p,1);
+  strcpy(p,strrchr(service,'\\')+1);   /* name of queue */
+  p = skip_string(p,1);
+  SSVAL(p,0,3);                                /* API function level 3, just queue info, no job info */
+  SSVAL(p,2,1000);                     /* size of bytes of returned data buffer */
+  p += 4;
+  strcpy(p,"");                                /* subformat */
+  p = skip_string(p,1);
+
+  DEBUG(1,("Calling DosPrintQueueGetInfo()...\n"));
+  if( call_api(PTR_DIFF(p,param), 0,
+              10, 4096,
+              &rprcnt, &rdrcnt,
+              param, NULL,
+              &rparam, &rdata) )
+       {
+       int converter;
+       result_code = SVAL(rparam,0);
+       converter = SVAL(rparam,2);             /* conversion factor */
+
+       DEBUG(2,("returned %d bytes of parameters, %d bytes of data, %d records\n", rprcnt, rdrcnt, SVAL(rparam,4) ));
+
+       if (result_code == 0)                   /* if no error, */
+           {
+           p = rdata;                          /* received data */
+
+           printf("Name: \"%s\"\n", fix_char_ptr(SVAL(p,0), converter, rdata, rdrcnt) );
+           printf("Priority: %u\n", SVAL(p,4) );
+           printf("Start time: %u\n", SVAL(p,6) );
+           printf("Until time: %u\n", SVAL(p,8) );
+           printf("Seperator file: \"%s\"\n", fix_char_ptr(SVAL(p,12), converter, rdata, rdrcnt) );
+           printf("Print processor: \"%s\"\n", fix_char_ptr(SVAL(p,16), converter, rdata, rdrcnt) );
+           printf("Parameters: \"%s\"\n", fix_char_ptr(SVAL(p,20), converter, rdata, rdrcnt) );
+           printf("Comment: \"%s\"\n", fix_char_ptr(SVAL(p,24), converter, rdata, rdrcnt) );
+           printf("Status: %u\n", SVAL(p,28) );
+           printf("Jobs: %u\n", SVAL(p,30) );
+           printf("Printers: \"%s\"\n", fix_char_ptr(SVAL(p,32), converter, rdata, rdrcnt) );
+           printf("Drivername: \"%s\"\n", fix_char_ptr(SVAL(p,36), converter, rdata, rdrcnt) );
+
+           /* Dump the driver data */
+           {
+           int count, x, y, c;
+           char *ddptr;
+
+           ddptr = rdata + SVAL(p,40) - converter;
+           if( SVAL(p,40) == 0 ) {count = 0;} else {count = IVAL(ddptr,0);}
+           printf("Driverdata: size=%d, version=%u\n", count, IVAL(ddptr,4) );
+
+           for(x=8; x < count; x+=16)
+               {
+               for(y=0; y < 16; y++)
+                   {
+                   if( (x+y) < count )
+                       printf("%2.2X ", CVAL(ddptr,(x+y)) );
+                   else
+                       fputs("   ", stdout);
+                   }
+               for(y=0; y < 16 && (x+y) < count; y++)
+                   {
+                   c = CVAL(ddptr,(x+y));
+                   if(isprint(c))
+                       fputc(c, stdout);
+                   else
+                       fputc('.', stdout);
+                   }
+               fputc('\n', stdout);
+               }
+           }
+           
+           }
+       }
+  else                 /* call_api() failed */
+       {
+       printf("Failed, error = %d\n", result_code);
+       }
+
+  /* If any parameters or data were returned, free the storage. */
+  if(rparam) free(rparam);
+  if(rdata) free(rdata);
+
+  return;
+}
+
 /****************************************************************************
 delete some files
 ****************************************************************************/
@@ -2790,7 +3051,7 @@ static BOOL send_session_request(char *inbuf,char *outbuf)
 
   /* put in the destination name */
   p = outbuf+len;
-  name_mangle(dest,p,name_type);
+  name_mangle(dest,p,name_type); /* 0x20 is the SMB server NetBIOS type. */
   len += name_len(p);
 
   /* and my name */
@@ -2825,7 +3086,7 @@ static BOOL send_session_request(char *inbuf,char *outbuf)
       putip((char *)&dest_ip,inbuf+4);
 
       close_sockets();
-      Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+      Client = open_socket_out(SOCK_STREAM, &dest_ip, port, LONG_CONNECT_TIMEOUT);
       if (Client == -1)
         return False;
 
@@ -2874,6 +3135,21 @@ static BOOL send_session_request(char *inbuf,char *outbuf)
   return(True);
 }
 
+static 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}
+};
+
 
 /****************************************************************************
 send a login command
@@ -2887,26 +3163,11 @@ static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setu
   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;
+  int tries=0;
 
   if (was_null)
     {
@@ -3044,21 +3305,21 @@ static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setu
   else
     pass = (char *)getpass("Password: ");
 
+  /* use a blank username for the 2nd try with a blank password */
+  if (tries++ && !*pass)
+    *username = 0;
+
   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);
+       SMBencrypt((uchar *)pass,(uchar *)cryptkey,(uchar *)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;} 
@@ -3169,19 +3430,17 @@ static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setu
     fstring pword;
     strcpy(pword,pass);
 
-#ifdef SMB_PASSWD
     if (doencrypt && *pass) {
       passlen=24;
-      SMBencrypt(pass,cryptkey,pword);      
+      SMBencrypt((uchar *)pass,(uchar *)cryptkey,(uchar *)pword);      
     }
-#endif
 
     /* if in user level security then don't send a password now */
     if ((sec_mode & 1)) {
       strcpy(pword, ""); passlen=1; 
     }
 
-    if (Protocol <= PROTOCOL_CORE) {
+    if (Protocol <= PROTOCOL_COREPLUS) {
       set_message(outbuf,0,6 + strlen(service) + passlen + strlen(dev),True);
       CVAL(outbuf,smb_com) = SMBtcon;
       setup_pkt(outbuf);
@@ -3241,7 +3500,7 @@ static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setu
     }
   
 
-  if (Protocol <= PROTOCOL_CORE) {
+  if (Protocol <= PROTOCOL_COREPLUS) {
     max_xmit = SVAL(inbuf,smb_vwv0);
 
     cnum = SVAL(inbuf,smb_vwv1);
@@ -3440,8 +3699,13 @@ try and browse available connections on a host
 static BOOL browse_host(BOOL sort)
 {
 #ifdef NOSTRCASECMP
+/* If strcasecmp is already defined, remove it. */
+#ifdef strcasecmp
+#undef strcasecmp
+#endif /* strcasecmp */
 #define strcasecmp StrCaseCmp
-#endif
+#endif /* NOSTRCASECMP */
+
   extern int strcasecmp();
 
   char *rparam = NULL;
@@ -3545,7 +3809,7 @@ static void server_info()
   bzero(param,sizeof(param));
 
   p = param;
-  SSVAL(p,0,63); /* api number */
+  SSVAL(p,0,63);               /* NetServerGetInfo()? */
   p += 2;
   strcpy(p,"WrLh");
   p = skip_string(p,1);
@@ -3586,38 +3850,52 @@ static void server_info()
 /****************************************************************************
 try and browse available connections on a host
 ****************************************************************************/
-static BOOL list_servers()
+static BOOL list_servers(char *wk_grp)
 {
   char *rparam = NULL;
   char *rdata = NULL;
   int rdrcnt,rprcnt;
-  char *p;
+  char *p,*svtype_p;
   pstring param;
   int uLevel = 1;
   int count = 0;
   BOOL ok = False;
+  BOOL generic_request = False;
+
+
+  if (strequal(wk_grp,"WORKGROUP")) {
+    /* we won't specify a workgroup */
+    generic_request = True;
+  } 
 
   /* now send a SMBtrans command with api ServerEnum? */
   p = param;
   SSVAL(p,0,0x68); /* api number */
   p += 2;
-  strcpy(p,"WrLehDO");
+
+  strcpy(p,generic_request?"WrLehDO":"WrLehDz");
   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 */
+  SSVAL(p,2,BUFFER_SIZE - SAFETY_MARGIN); /* buf length */
   p += 4;
 
-  SIVAL(p,0,SV_TYPE_ALL);
+  svtype_p = p;
+  p += 4;
+
+  if (!generic_request) {
+    strcpy(p, wk_grp);
+    p = skip_string(p,1);
+  }
+
+  /* first ask for a list of servers in this workgroup */
+  SIVAL(svtype_p,0,SV_TYPE_ALL);
 
   if (call_api(PTR_DIFF(p+4,param),0,
-              8,10000,
+              8,BUFFER_SIZE - SAFETY_MARGIN,
               &rprcnt,&rdrcnt,
               param,NULL,
               &rparam,&rdata))
@@ -3652,10 +3930,11 @@ static BOOL list_servers()
   if (rparam) {free(rparam); rparam = NULL;}
   if (rdata) {free(rdata); rdata = NULL;}
 
-  SIVAL(p,0,SV_TYPE_DOMAIN_ENUM);
+  /* now ask for a list of workgroups */
+  SIVAL(svtype_p,0,SV_TYPE_DOMAIN_ENUM);
 
   if (call_api(PTR_DIFF(p+4,param),0,
-              8,10000,
+              8,BUFFER_SIZE - SAFETY_MARGIN,
               &rprcnt,&rdrcnt,
               param,NULL,
               &rparam,&rdata))
@@ -3720,6 +3999,7 @@ struct
   {"md",cmd_mkdir,"<directory> make a directory"},
   {"rmdir",cmd_rmdir,"<directory> remove a directory"},
   {"rd",cmd_rmdir,"<directory> remove a directory"},
+  {"pq",cmd_p_queue_4,"enumerate the print queue"},
   {"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"},  
@@ -3727,6 +4007,7 @@ struct
   {"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"},
+  {"qinfo",cmd_qinfo,"show print queue information"},
   {"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"},
@@ -3837,11 +4118,10 @@ static BOOL open_sockets(int port )
       strcpy(desthost,host);
     }
 
-  if (*myname == 0)
-    {
+  if (*myname == 0) {
       get_myname(myname,NULL);
-      strupper(myname);
-    }
+  }
+  strupper(myname);
 
   DEBUG(3,("Opening sockets\n"));
 
@@ -3857,10 +4137,11 @@ static BOOL open_sockets(int port )
        /* Try and resolve the name with the netbios server */
        int             bcast;
 
-       if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3)) != -1) {
+       if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3,
+                                   interpret_addr(lp_socket_address()))) != -1) {
          set_socket_options(bcast, "SO_BROADCAST");
 
-         if (name_query(bcast, host, 0x20, True, True, *iface_bcast(dest_ip),
+         if (name_query(bcast, host, name_type, True, True, *iface_bcast(dest_ip),
                         &dest_ip,0)) {
            failed = False;
          }
@@ -3874,7 +4155,7 @@ static BOOL open_sockets(int port )
       }
     }
 
-  Client = open_socket_out(SOCK_STREAM, &dest_ip, port);
+  Client = open_socket_out(SOCK_STREAM, &dest_ip, port, LONG_CONNECT_TIMEOUT);
   if (Client == -1)
     return False;
 
@@ -3924,13 +4205,11 @@ static void wait_keyboard(char *buffer)
 #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);
+
+    set_blocking(fileno(stdin), False);        
        readret = read_data( fileno(stdin), &ch, 1);
-       fcntl(fileno(stdin), F_SETFL, f_flags);
+       set_blocking(fileno(stdin), True);
        if (readret == -1)
          {
            if (errno != EAGAIN)
@@ -4056,7 +4335,7 @@ static BOOL process(char *base_directory)
       bzero(OutBuffer,smb_size);
 
       /* display a prompt */
-      DEBUG(1,("smb: %s> ", CNV_LANG(cur_dir)));
+      DEBUG(0,("smb: %s> ", CNV_LANG(cur_dir)));
       fflush(dbf);
 
 #ifdef CLIX
@@ -4113,10 +4392,6 @@ static 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"));
@@ -4132,16 +4407,12 @@ static void usage(char *pname)
   DEBUG(0,("\t-U username           set the network username\n"));
   DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
   DEBUG(0,("\t-c command string     execute semicolon separated commands\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
 ****************************************************************************/
@@ -4157,6 +4428,15 @@ static void usage(char *pname)
   pstring query_host;
   BOOL message = False;
   extern char tar_type;
+  static pstring servicesf = CONFIGFILE;
+  pstring term_code;
+  char *p;
+
+#ifdef KANJI
+  strcpy(term_code, KANJI);
+#else /* KANJI */
+  *term_code = 0;
+#endif /* KANJI */
 
   *query_host = 0;
   *base_directory = 0;
@@ -4176,10 +4456,27 @@ static void usage(char *pname)
   umask(myumask);
 
   if (getenv("USER"))
+  {
+    strcpy(username,getenv("USER"));
+
+    /* modification to support userid%passwd syntax in the USER var
+       25.Aug.97, jdblair@uab.edu */
+
+    if ((p=strchr(username,'%')))
     {
-      strcpy(username,getenv("USER"));
-      strupper(username);
+      *p = 0;
+      strcpy(password,p+1);
+      got_pass = True;
+      memset(strchr(getenv("USER"),'%')+1,'X',strlen(password));
     }
+    strupper(username);
+  }
+
+ /* modification to support PASSWD environmental var
+  25.Aug.97, jdblair@uab.edu */
+
+  if (getenv("PASSWD"))
+    strcpy(password,getenv("PASSWD"));
 
   if (*username == 0 && getenv("LOGNAME"))
     {
@@ -4197,6 +4494,8 @@ static void usage(char *pname)
     {
 
       strcpy(service,argv[1]);  
+      /* Convert any '/' characters in the service name to '\' characters */
+      string_replace( service, '/','\\');
       argc--;
       argv++;
 
@@ -4226,11 +4525,8 @@ static void usage(char *pname)
        }
     }
 
-#ifdef KANJI
-  setup_term_code (KANJI);
-#endif
   while ((opt = 
-         getopt(argc, argv,"B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
+         getopt(argc, argv,"s:B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:c:")) != EOF)
     switch (opt)
       {
       case 'm':
@@ -4240,7 +4536,7 @@ static void usage(char *pname)
        strcpy(user_socket_options,optarg);
        break;  
       case 'M':
-       name_type = 3;
+       name_type = 0x03; /* messages are sent to NetBIOS name type 0x3 */
        strcpy(desthost,optarg);
        strupper(desthost);
        message = True;
@@ -4320,14 +4616,11 @@ static void usage(char *pname)
        usage(pname);
        exit(0);
        break;
+      case 's':
+       strcpy(servicesf, optarg);
+       break;
       case 't':
-#ifdef KANJI
-       if (!setup_term_code (optarg)) {
-           DEBUG(0, ("%s: unknown terminal code name\n", optarg));
-           usage (pname);
-           exit (1);
-       }
-#endif
+        strcpy(term_code, optarg);
        break;
       default:
        usage(pname);
@@ -4343,6 +4636,30 @@ static void usage(char *pname)
 
   DEBUG(3,("%s client started (version %s)\n",timestring(),VERSION));
 
+  if(!get_myname(myhostname,NULL))
+  {
+    DEBUG(0,("Failed to get my hostname.\n"));
+  }
+
+  if (!lp_load(servicesf,True)) {
+    fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf);
+  }
+
+  codepage_initialise(lp_client_code_page());
+
+  if(lp_client_code_page() == KANJI_CODEPAGE)
+  {
+        if (!setup_term_code (term_code))
+    {
+            DEBUG(0, ("%s: unknown terminal code name\n", optarg));
+            usage (pname);
+            exit (1);
+        }
+  }
+
+  if (*workgroup == 0)
+    strcpy(workgroup,lp_workgroup());
+
   load_interfaces();
   get_myname(*myname?NULL:myname,NULL);  
   strupper(myname);
@@ -4392,9 +4709,9 @@ static void usage(char *pname)
            sleep(1);
            browse_host(True);
          }
-         if (!list_servers()) {
+         if (!list_servers(workgroup)) {
            sleep(1);
-           list_servers();
+           list_servers(workgroup);
          }
 
          send_logout();
@@ -4530,7 +4847,7 @@ err_code_struct hard_msgs[] = {
   {"ERRwrite",29,"Write fault."},
   {"ERRread",30,"Read fault."},
   {"ERRgeneral",31,"General failure."},
-  {"ERRbadshare",32,"A open conflicts with an existing open."},
+  {"ERRbadshare",32,"An 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."},
@@ -4588,6 +4905,6 @@ char *smb_errstr(char *inbuf)
        return ret;
       }
   
-  sprintf(ret,"ERROR: Unknown error (%d,%d)",class,num);
+  sprintf(ret,"Error: Unknown error (%d,%d)",class,num);
   return(ret);
 }