Move the bang (!) command back to the bottom of the list, allowing smbclient
[ira/wip.git] / source3 / client / client.c
index 7baab4b2043a32bdee62a3b6f8ad9ea05ec94872..f1059f8ee1ffd8a227fda1b1b8b213623500dfb0 100644 (file)
@@ -32,6 +32,7 @@ const char prog_name[] = "smbclient";
 
 struct cli_state *cli;
 extern BOOL in_client;
+extern BOOL AllowDebugChange;
 static int port = 0;
 pstring cur_dir = "\\";
 pstring cd_path = "";
@@ -45,7 +46,6 @@ static char *cmdstr;
 static BOOL got_pass;
 static int io_bufsize = 64512;
 static BOOL use_kerberos;
-extern struct in_addr ipzero;
 
 static int name_type = 0x20;
 static int max_protocol = PROTOCOL_NT1;
@@ -64,8 +64,6 @@ static int cmd_help(void);
 time_t newer_than = 0;
 int archive_level = 0;
 
-extern pstring debugf;
-
 BOOL translation = False;
 
 static BOOL have_ip;
@@ -1560,6 +1558,148 @@ static int cmd_rmdir(void)
        return 0;
 }
 
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+
+static int cmd_link(void)
+{
+       pstring src,dest;
+       fstring buf,buf2;
+  
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+  
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("link <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if (!cli_unix_hardlink(cli, src, dest)) {
+               d_printf("%s linking files (%s -> %s)\n", cli_errstr(cli), src, dest);
+               return 1;
+       }  
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(void)
+{
+       pstring src,dest;
+       fstring buf,buf2;
+  
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       pstrcpy(dest,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("symlink <src> <dest>\n");
+               return 1;
+       }
+
+       pstrcat(src,buf);
+       pstrcat(dest,buf2);
+
+       if (!cli_unix_symlink(cli, src, dest)) {
+               d_printf("%s symlinking files (%s -> %s)\n",
+                       cli_errstr(cli), src, dest);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(void)
+{
+       pstring src;
+       mode_t mode;
+       fstring buf, buf2;
+  
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2))) {
+               d_printf("chmod mode file\n");
+               return 1;
+       }
+
+       mode = (mode_t)strtol(buf, NULL, 8);
+       pstrcat(src,buf2);
+
+       if (!cli_unix_chmod(cli, src, mode)) {
+               d_printf("%s chmod file %s 0%o\n",
+                       cli_errstr(cli), src, (unsigned int)mode);
+               return 1;
+       } 
+
+       return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(void)
+{
+       pstring src;
+       uid_t uid;
+       gid_t gid;
+       fstring buf, buf2, buf3;
+  
+       if (!SERVER_HAS_UNIX_CIFS(cli)) {
+               d_printf("Server doesn't support UNIX CIFS calls.\n");
+               return 1;
+       }
+
+       pstrcpy(src,cur_dir);
+       
+       if (!next_token(NULL,buf,NULL,sizeof(buf)) || 
+           !next_token(NULL,buf2,NULL, sizeof(buf2)) ||
+           !next_token(NULL,buf3,NULL, sizeof(buf3))) {
+               d_printf("chown uid gid file\n");
+               return 1;
+       }
+
+       uid = (uid_t)atoi(buf);
+       gid = (gid_t)atoi(buf2);
+       pstrcat(src,buf3);
+
+       if (!cli_unix_chown(cli, src, uid, gid)) {
+               d_printf("%s chown file %s uid=%d, gid=%d\n",
+                       cli_errstr(cli), src, (int)uid, (int)gid);
+               return 1;
+       } 
+
+       return 0;
+}
+
 /****************************************************************************
 rename some files
 ****************************************************************************/
@@ -1753,6 +1893,9 @@ static void browse_fn(const char *name, uint32 m,
           case STYPE_IPC:
             fstrcpy(typestr,"IPC"); break;
         }
+       /* FIXME: If the remote machine returns non-ascii characters
+          in any of these fields, they can corrupt the output.  We
+          should remove them. */
        d_printf("\t%-15.15s%-10.10s%s\n",
                name,typestr,comment);
 }
@@ -1821,10 +1964,14 @@ struct
   char compl_args[2];      /* Completion argument info */
 } commands[] = 
 {
+  {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+  {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
   {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
   {"blocksize",cmd_block,"blocksize <number> (default 20)",{COMPL_NONE,COMPL_NONE}},
   {"cancel",cmd_cancel,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
   {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
+  {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
+  {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
   {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
   {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
@@ -1833,6 +1980,7 @@ struct
   {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
   {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
   {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
+  {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
   {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},  
   {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
   {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
@@ -1857,11 +2005,12 @@ struct
   {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
   {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
   {"setmode",cmd_setmode,"filename <setmode string> change modes of file",{COMPL_REMOTE,COMPL_NONE}},
+  {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
   {"tar",cmd_tar,"tar <c|x>[IXFqbgNan] current directory to/from <file name>",{COMPL_NONE,COMPL_NONE}},
   {"tarmode",cmd_tarmode,"<full|inc|reset|noreset> tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}},
   {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
-  {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
-  {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+  
+  /* Yes, this must be here, see crh's comment above. */
   {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
   {"",NULL,NULL,{COMPL_NONE,COMPL_NONE}}
 };
@@ -2085,7 +2234,6 @@ struct cli_state *do_connect(const char *server, const char *share)
        struct nmb_name called, calling;
        const char *server_n;
        struct in_addr ip;
-       extern struct in_addr ipzero;
        fstring servicename;
        char *sharename;
        
@@ -2102,13 +2250,13 @@ struct cli_state *do_connect(const char *server, const char *share)
 
        server_n = server;
        
-       ip = ipzero;
+       zero_ip(&ip);
 
        make_nmb_name(&calling, global_myname, 0x0);
        make_nmb_name(&called , server, name_type);
 
  again:
-       ip = ipzero;
+       zero_ip(&ip);
        if (have_ip) ip = dest_ip;
 
        /* have to open a new connection */
@@ -2126,7 +2274,6 @@ struct cli_state *do_connect(const char *server, const char *share)
                d_printf("session request to %s failed (%s)\n", 
                         called.name, cli_errstr(c));
                cli_shutdown(c);
-               SAFE_FREE(c);
                if ((p=strchr_m(called.name, '.'))) {
                        *p = 0;
                        goto again;
@@ -2143,7 +2290,6 @@ struct cli_state *do_connect(const char *server, const char *share)
        if (!cli_negprot(c)) {
                d_printf("protocol negotiation failed\n");
                cli_shutdown(c);
-               SAFE_FREE(c);
                return NULL;
        }
 
@@ -2159,27 +2305,21 @@ struct cli_state *do_connect(const char *server, const char *share)
                               password, strlen(password),
                               workgroup)) {
                /* if a password was not supplied then try again with a null username */
-               if (password[0] || !username[0] || 
+               if (password[0] || !username[0] || use_kerberos ||
                    !cli_session_setup(c, "", "", 0, "", 0, workgroup)) { 
                        d_printf("session setup failed: %s\n", cli_errstr(c));
                        cli_shutdown(c);
-                       SAFE_FREE(c);
                        return NULL;
                }
                d_printf("Anonymous login successful\n");
        }
 
-       /*
-        * These next two lines are needed to emulate
-        * old client behaviour for people who have
-        * scripts based on client output.
-        * QUESTION ? Do we want to have a 'client compatibility
-        * mode to turn these on/off ? JRA.
-        */
-
-       if (*c->server_domain || *c->server_os || *c->server_type){
+       if (*c->server_domain) {
                DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
                        c->server_domain,c->server_os,c->server_type));
+       } else if (*c->server_os || *c->server_type){
+               DEBUG(1,("OS=[%s] Server=[%s]\n",
+                        c->server_os,c->server_type));
        }               
        
        DEBUG(4,(" session setup ok\n"));
@@ -2188,7 +2328,6 @@ struct cli_state *do_connect(const char *server, const char *share)
                            password, strlen(password)+1)) {
                d_printf("tree connect failed: %s\n", cli_errstr(c));
                cli_shutdown(c);
-               SAFE_FREE(c);
                return NULL;
        }
 
@@ -2366,12 +2505,12 @@ static int do_message_op(void)
        struct in_addr ip;
        struct nmb_name called, calling;
 
-       ip = ipzero;
+        zero_ip(&ip);
 
        make_nmb_name(&calling, global_myname, 0x0);
        make_nmb_name(&called , desthost, name_type);
 
-       ip = ipzero;
+        zero_ip(&ip);
        if (have_ip) ip = dest_ip;
 
        if (!(cli=cli_initialise(NULL)) || (cli_set_port(cli, port) != port) || !cli_connect(cli, desthost, &ip)) {
@@ -2408,6 +2547,7 @@ static int do_message_op(void)
        extern char tar_type;
        pstring term_code;
        pstring new_name_resolve_order;
+       pstring logfile;
        char *p;
        int rc = 0;
 
@@ -2423,6 +2563,7 @@ static int do_message_op(void)
        *new_name_resolve_order = 0;
 
        DEBUGLEVEL = 2;
+       AllowDebugChange = False;
  
        setup_logging(pname,True);
 
@@ -2509,7 +2650,13 @@ static int do_message_op(void)
                usage(pname);
                exit(1);
        }
-  
+
+       /* FIXME: At the moment, if the user should happen to give the
+        * options ahead of the service name (in standard Unix
+        * fashion) then smbclient just spits out the usage message
+        * with no explanation of what in particular was wrong.  Is
+        * there any reason we can't just parse out the service name
+        * and password after running getopt?? -- mbp */
        if (*argv[1] != '-') {
                pstrcpy(service,argv[1]);  
                /* Convert any '/' characters in the service name to '\' characters */
@@ -2575,7 +2722,8 @@ static int do_message_op(void)
                        port = atoi(optarg);
                        break;
                case 'l':
-                       slprintf(debugf,sizeof(debugf)-1, "%s.client",optarg);
+                       slprintf(logfile,sizeof(logfile)-1, "%s.client",optarg);
+                       lp_set_logfile(logfile);
                        break;
                case 'h':
                        usage(pname);
@@ -2584,7 +2732,7 @@ static int do_message_op(void)
                case 'I':
                        {
                                dest_ip = *interpret_addr2(optarg);
-                               if (zero_ip(dest_ip))
+                               if (is_zero_ip(dest_ip))
                                        exit(1);
                                have_ip = True;
                        }
@@ -2654,7 +2802,8 @@ static int do_message_op(void)
                                        }
                                        else if (strwicmp("username", param) == 0)
                                                pstrcpy(username, val);
-                                               
+                                       else if (strwicmp("domain", param) == 0)
+                                               pstrcpy(workgroup,val);
                                        memset(buf, 0, sizeof(buf));
                                }
                                x_fclose(auth);
@@ -2692,7 +2841,7 @@ static int do_message_op(void)
                        io_bufsize = MAX(1, atoi(optarg));
                        break;
                case 'k':
-#if HAVE_KRB5
+#ifdef HAVE_KRB5
                        use_kerberos = True;
                        got_pass = True;
 #else