s3-netdomjoin-gui: only gray out labels when not root and not connecting to
[ira/wip.git] / source3 / lib / netapi / examples / netdomjoin-gui / netdomjoin-gui.c
index 7c84d4d8c636e389963c677d2ffe3ca823a88081..032d65c3044e1def1dcc43dee175a63cf6e7dcf2 100644 (file)
 #define SAMBA_IMAGE_PATH "/usr/share/pixmaps/samba/logo.png"
 #define SAMBA_IMAGE_PATH_SMALL "/usr/share/pixmaps/samba/logo-small.png"
 
-#define WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED ( 0x00000020 )
-#define WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE ( 0x00000004 )
-#define WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE ( 0x00000002 )
-#define WKSSVC_JOIN_FLAGS_JOIN_TYPE ( 0x00000001 )
-
-#define NetSetupWorkgroupName ( 2 )
-#define NetSetupDomainName ( 3 )
-
 #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
 
 static gboolean verbose = FALSE;
@@ -85,8 +77,16 @@ typedef struct join_state {
        gboolean settings_changed;
        gboolean hostname_changed;
        uint32_t stored_num_ous;
+       char *target_hostname;
+       uid_t uid;
 } join_state;
 
+static void callback_creds_prompt(GtkWidget *widget,
+                                 gpointer data,
+                                 const char *label_string,
+                                 gpointer cont_fn);
+
+
 static void debug(const char *format, ...)
 {
        va_list args;
@@ -108,12 +108,24 @@ static gboolean callback_delete_event(GtkWidget *widget,
        return FALSE;
 }
 
-static void callback_do_close(GtkWidget *widget,
-                             gpointer data)
+static void callback_do_close_data(GtkWidget *widget,
+                                  gpointer data)
 {
-       debug("callback_do_close called\n");
+       debug("callback_do_close_data called\n");
 
-       gtk_widget_destroy(data);
+       if (data) {
+               gtk_widget_destroy(GTK_WIDGET(data));
+       }
+}
+
+static void callback_do_close_widget(GtkWidget *widget,
+                                    gpointer data)
+{
+       debug("callback_do_close_widget called\n");
+
+       if (widget) {
+               gtk_widget_destroy(widget);
+       }
 }
 
 static void callback_do_freeauth(GtkWidget *widget,
@@ -127,7 +139,8 @@ static void callback_do_freeauth(GtkWidget *widget,
        SAFE_FREE(state->password);
 
        if (state->window_creds_prompt) {
-               gtk_widget_destroy(state->window_creds_prompt);
+               gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt));
+               state->window_creds_prompt = NULL;
        }
 }
 
@@ -141,8 +154,14 @@ static void callback_do_freeauth_and_close(GtkWidget *widget,
        SAFE_FREE(state->account);
        SAFE_FREE(state->password);
 
-       gtk_widget_destroy(state->window_creds_prompt);
-       gtk_widget_destroy(state->window_do_change);
+       if (state->window_creds_prompt) {
+               gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt));
+               state->window_creds_prompt = NULL;
+       }
+       if (state->window_do_change) {
+               gtk_widget_destroy(GTK_WIDGET(state->window_do_change));
+               state->window_do_change = NULL;
+       }
 }
 
 static void free_join_state(struct join_state *s)
@@ -175,7 +194,10 @@ static void callback_apply_description_change(GtkWidget *widget,
 
        info1005.sv1005_comment = state->comment_new;
 
-       status = NetServerSetInfo(NULL, 1005, (uint8_t *)&info1005, &parm_err); 
+       status = NetServerSetInfo(state->target_hostname,
+                                 1005,
+                                 (uint8_t *)&info1005,
+                                 &parm_err);
        if (status) {
                debug("NetServerSetInfo failed with: %s\n",
                        libnetapi_errstr(status));
@@ -186,6 +208,7 @@ static void callback_apply_description_change(GtkWidget *widget,
                                                "Failed to change computer description: %s.",
                                                libnetapi_get_error_string(state->ctx, status));
                gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+               gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_main));
 
                g_signal_connect_swapped(dialog, "response",
                                         G_CALLBACK(gtk_widget_destroy),
@@ -201,8 +224,10 @@ static void callback_apply_description_change(GtkWidget *widget,
 static void callback_do_exit(GtkWidget *widget,
                             gpointer data)
 {
+#if 0
        GtkWidget *dialog;
        gint result;
+#endif
        struct join_state *state = (struct join_state *)data;
 
        if (!state->settings_changed) {
@@ -210,6 +235,7 @@ static void callback_do_exit(GtkWidget *widget,
                return;
        }
 
+#if 0
        dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main),
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
                                        GTK_MESSAGE_QUESTION,
@@ -225,8 +251,14 @@ static void callback_do_exit(GtkWidget *widget,
                default:
                        break;
        }
-       gtk_widget_destroy(dialog);
-       gtk_widget_destroy(state->window_main);
+       if (dialog) {
+               gtk_widget_destroy(GTK_WIDGET(dialog));
+       }
+#endif
+       if (state->window_main) {
+               gtk_widget_destroy(GTK_WIDGET(state->window_main));
+               state->window_main = NULL;
+       }
        do_cleanup(state);
        exit(0);
 }
@@ -248,6 +280,7 @@ static void callback_do_reboot(GtkWidget *widget,
                                        GTK_BUTTONS_OK,
                                        "You must restart this computer for the changes to take effect.");
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+       gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
 #if 0
        g_signal_connect_swapped(dialog, "response",
                                 G_CALLBACK(gtk_widget_destroy),
@@ -257,7 +290,7 @@ static void callback_do_reboot(GtkWidget *widget,
        gtk_widget_show(dialog);
 #else
        gtk_dialog_run(GTK_DIALOG(dialog));
-       gtk_widget_destroy(dialog);
+       gtk_widget_destroy(GTK_WIDGET(dialog));
 #endif
 
        gtk_label_set_text(GTK_LABEL(state->label_reboot),
@@ -271,19 +304,21 @@ static void callback_do_reboot(GtkWidget *widget,
                const char *buffer;
                uint16_t type;
 
-               status = NetGetJoinInformation(NULL, &buffer, &type);
+               status = NetGetJoinInformation(state->target_hostname,
+                                              &buffer,
+                                              &type);
                if (status != 0) {
                        g_print("failed to query status\n");
                        return;
                }
 
                debug("got new status: %s\n", buffer);
-#if 0
+
                SAFE_FREE(state->name_buffer_new);
                state->name_buffer_new = strdup(buffer);
-               SAFE_FREE(buffer);
                state->name_type_new = type;
-#endif
+               state->name_buffer_initial = strdup(buffer);
+               state->name_type_initial = type;
                NetApiBufferFree((void *)buffer);
 
                gtk_label_set_text(GTK_LABEL(state->label_current_name_buffer),
@@ -388,10 +423,13 @@ static void callback_do_storeauth(GtkWidget *widget,
        SAFE_FREE(state->account);
        SAFE_FREE(state->password);
 
-       callback_return_username(state->entry_account, state);
-       callback_return_password(state->entry_password, state);
+       callback_return_username(state->entry_account, (gpointer)state);
+       callback_return_password(state->entry_password, (gpointer)state);
 
-       gtk_widget_destroy(state->window_creds_prompt);
+       if (state->window_creds_prompt) {
+               gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt));
+               state->window_creds_prompt = NULL;
+       }
 }
 
 static void callback_continue(GtkWidget *widget,
@@ -427,9 +465,50 @@ static void callback_do_hostname_change(GtkWidget *widget,
        struct join_state *state = (struct join_state *)data;
 
        switch (state->name_type_initial) {
-               case NetSetupDomainName:
+               case NetSetupDomainName: {
+#if 0
+                       NET_API_STATUS status;
+                       const char *newname;
+                       char *p = NULL;
+
+                       newname = strdup(gtk_label_get_text(GTK_LABEL(state->label_full_computer_name)));
+                       if (!newname) {
+                               return;
+                       }
+
+                       p = strchr(newname, '.');
+                       if (p) {
+                               *p = NULL;
+                       }
+
+                       if (!state->account || !state->password) {
+                               debug("callback_do_hostname_change: no creds yet\n");
+                               callback_creds_prompt(NULL, state,
+                                                     "Enter the name and password of an account with permission to change a computer name in a the domain.",
+                                                     callback_do_storeauth_and_continue);
+                       }
+
+                       if (!state->account || !state->password) {
+                               debug("callback_do_hostname_change: still no creds???\n");
+                               return;
+                       }
+
+                       status = NetRenameMachineInDomain(state->target_hostname,
+                                                         newname,
+                                                         state->account,
+                                                         state->password,
+                                                         NETSETUP_ACCT_CREATE);
+                       SAFE_FREE(newname);
+                       /* we renamed the machine in the domain */
+                       if (status == 0) {
+                               return;
+                       }
+                       str = libnetapi_get_error_string(state->ctx, status);
+#else
                        str = "To be implemented: call NetRenameMachineInDomain\n";
+#endif
                        break;
+               }
                case NetSetupWorkgroupName:
                        str = "To be implemented: call SetComputerNameEx\n";
                        break;
@@ -444,6 +523,7 @@ static void callback_do_hostname_change(GtkWidget *widget,
                                        str);
 
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+       gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_main));
        g_signal_connect_swapped(dialog, "response",
                                 G_CALLBACK(gtk_widget_destroy),
                                 dialog);
@@ -473,9 +553,11 @@ static void callback_creds_prompt(GtkWidget *widget,
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
        gtk_widget_set_size_request(GTK_WIDGET(window), 380, 280);
        gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL);
+       gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(state->window_do_change));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
 
        g_signal_connect(G_OBJECT(window), "delete_event",
-                        G_CALLBACK(callback_do_close), window);
+                        G_CALLBACK(callback_do_close_widget), NULL);
 
        state->window_creds_prompt = window;
        gtk_container_set_border_width(GTK_CONTAINER(window), 10);
@@ -609,9 +691,9 @@ static void callback_do_join(GtkWidget *widget,
        if (state->name_type_new == NetSetupDomainName) {
                domain_join = TRUE;
                join_creds_required = TRUE;
-               join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
-                            WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
-                            WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; /* for testing */
+               join_flags = NETSETUP_JOIN_DOMAIN |
+                            NETSETUP_ACCT_CREATE |
+                            NETSETUP_DOMAIN_JOIN_IF_JOINED; /* for testing */
        }
 
        if ((state->name_type_initial == NetSetupDomainName) &&
@@ -619,8 +701,9 @@ static void callback_do_join(GtkWidget *widget,
                try_unjoin = TRUE;
                unjoin_creds_required = TRUE;
                join_creds_required = FALSE;
-               unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
-                              WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+               unjoin_flags = NETSETUP_JOIN_DOMAIN |
+                              NETSETUP_ACCT_DELETE |
+                              NETSETUP_IGNORE_UNSUPPORTED_FLAGS;
        }
 
        if (try_unjoin) {
@@ -641,7 +724,7 @@ static void callback_do_join(GtkWidget *widget,
                        }
                }
 
-               status = NetUnjoinDomain(NULL,
+               status = NetUnjoinDomain(state->target_hostname,
                                         state->account,
                                         state->password,
                                         unjoin_flags);
@@ -650,6 +733,9 @@ static void callback_do_join(GtkWidget *widget,
                        err_str = libnetapi_get_error_string(state->ctx, status);
                        g_print("callback_do_join: failed to unjoin (%s)\n",
                                err_str);
+#if 0
+
+       /* in fact we shouldn't annoy the user with an error message here */
 
                        dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent),
                                                        GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -662,15 +748,52 @@ static void callback_do_join(GtkWidget *widget,
                        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
                        gtk_dialog_run(GTK_DIALOG(dialog));
                        gtk_widget_destroy(dialog);
+#endif
                }
 
        }
 
+       /* before prompting for creds, make sure we can find a dc */
+
+       if (domain_join) {
+
+               struct DOMAIN_CONTROLLER_INFO *dc_info = NULL;
+
+               status = DsGetDcName(NULL,
+                                    state->name_buffer_new,
+                                    NULL,
+                                    NULL,
+                                    0,
+                                    &dc_info);
+               if (status != 0) {
+                       err_str = libnetapi_get_error_string(state->ctx, status);
+                       g_print("callback_do_join: failed find dc (%s)\n", err_str);
+
+                       dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent),
+                                                       GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                       GTK_MESSAGE_ERROR,
+                                                       GTK_BUTTONS_CLOSE,
+                                                       "Failed to find a domain controller for domain: \"%s\": %s",
+                                                       state->name_buffer_new,
+                                                       err_str);
+
+                       gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+                       gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
+                       g_signal_connect_swapped(dialog, "response",
+                                                G_CALLBACK(gtk_widget_destroy),
+                                                dialog);
+
+                       gtk_widget_show(dialog);
+
+                       return;
+               }
+       }
+
        if (join_creds_required) {
                if (!state->account || !state->password) {
                        debug("callback_do_join: no creds yet\n");
                        callback_creds_prompt(NULL, state,
-                                             "Enter the name and password of an account with permission to leave the domain.",
+                                             "Enter the name and password of an account with permission to join the domain.",
                                              callback_do_storeauth_and_continue);
                }
 
@@ -692,7 +815,7 @@ static void callback_do_join(GtkWidget *widget,
        }
        debug("\n");
 
-       status = NetJoinDomain(NULL,
+       status = NetJoinDomain(state->target_hostname,
                               state->name_buffer_new,
                               account_ou,
                               state->account,
@@ -713,6 +836,7 @@ static void callback_do_join(GtkWidget *widget,
                                                err_str);
 
                gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+               gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
                g_signal_connect_swapped(dialog, "response",
                                         G_CALLBACK(gtk_widget_destroy),
                                         dialog);
@@ -735,6 +859,7 @@ static void callback_do_join(GtkWidget *widget,
                                        new_workgroup_type);
 
        gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+       gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
        gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy(dialog);
 
@@ -758,18 +883,24 @@ static void callback_enter_hostname_and_unlock(GtkWidget *widget,
        if (strcasecmp(state->my_hostname, entry_text) == 0) {
                state->hostname_changed = FALSE;
                gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE);
-               return;
+               /* return; */
+       } else {
+               state->hostname_changed = TRUE;
        }
-       state->hostname_changed = TRUE;
+
        if (state->name_type_initial == NetSetupDomainName) {
-               asprintf(&str, "%s.%s", entry_text, state->my_dnsdomain);
+               if (asprintf(&str, "%s.%s", entry_text, state->my_dnsdomain) == -1) {
+                       return;
+               }
        } else {
-               asprintf(&str, "%s.", entry_text);
+               if (asprintf(&str, "%s.", entry_text) == -1) {
+                       return;
+               }
        }
        gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), str);
        free(str);
 
-       if (state->hostname_changed && str && str[0] != 0 && str[0] != '.') {
+       if (state->hostname_changed && entry_text && entry_text[0] != 0 && entry_text[0] != '.') {
                gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE);
        }
 }
@@ -779,7 +910,7 @@ static void callback_enter_computer_description_and_unlock(GtkWidget *widget,
 {
        const gchar *entry_text = NULL;
        struct join_state *state = (struct join_state *)data;
-       int string_unchanged = 0;
+       int string_unchanged = FALSE;
 
        entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
        debug("callback_enter_computer_description_and_unlock: %s\n",
@@ -792,8 +923,8 @@ static void callback_enter_computer_description_and_unlock(GtkWidget *widget,
                return;
        }
 #endif
-       if (entry_text && strcasecmp(state->comment, entry_text) == 0) {
-               string_unchanged = 1;
+       if (entry_text && state->comment && strcasecmp(state->comment, entry_text) == 0) {
+               string_unchanged = TRUE;
                gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply),
                                         FALSE);
                return;
@@ -893,6 +1024,9 @@ static void callback_do_getous(GtkWidget *widget,
        const char **ous = NULL;
        int i;
        const char *domain = NULL;
+       struct DOMAIN_CONTROLLER_INFO *dc_info = NULL;
+       const char *err_str = NULL;
+       GtkWidget *dialog;
 
        struct join_state *state = (struct join_state *)data;
 
@@ -900,6 +1034,35 @@ static void callback_do_getous(GtkWidget *widget,
 
        domain = state->name_buffer_new ? state->name_buffer_new : state->name_buffer_initial;
 
+       status = DsGetDcName(NULL,
+                            domain,
+                            NULL,
+                            NULL,
+                            0,
+                            &dc_info);
+       if (status != 0) {
+               err_str = libnetapi_get_error_string(state->ctx, status);
+               g_print("callback_do_getous: failed find dc (%s)\n", err_str);
+
+               dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent),
+                                               GTK_DIALOG_DESTROY_WITH_PARENT,
+                                               GTK_MESSAGE_ERROR,
+                                               GTK_BUTTONS_CLOSE,
+                                               "Failed to find a domain controller for domain: \"%s\": %s",
+                                               domain,
+                                               err_str);
+
+               gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+               gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
+               g_signal_connect_swapped(dialog, "response",
+                                        G_CALLBACK(gtk_widget_destroy),
+                                        dialog);
+
+               gtk_widget_show(dialog);
+
+               return;
+       }
+
        if (!state->account || !state->password) {
                debug("callback_do_getous: no creds yet\n");
                callback_creds_prompt(NULL, state,
@@ -912,12 +1075,12 @@ static void callback_do_getous(GtkWidget *widget,
                return;
        }
 
-       status = NetGetJoinableOUs(NULL, domain,
+       status = NetGetJoinableOUs(state->target_hostname,
+                                  domain,
                                   state->account,
                                   state->password,
                                   &num_ous, &ous);
        if (status != NET_API_STATUS_SUCCESS) {
-               GtkWidget *dialog;
                callback_do_freeauth(NULL, state);
                debug("failed to call NetGetJoinableOUs: %s\n",
                        libnetapi_get_error_string(state->ctx, status));
@@ -928,6 +1091,7 @@ static void callback_do_getous(GtkWidget *widget,
                                                "Failed to query joinable OUs: %s",
                                                libnetapi_get_error_string(state->ctx, status));
                gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+               gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(state->window_do_change));
                gtk_dialog_run(GTK_DIALOG(dialog));
                gtk_widget_destroy(dialog);
                return;
@@ -993,9 +1157,11 @@ static void callback_do_change(GtkWidget *widget,
        gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
        gtk_widget_set_size_request(GTK_WIDGET(window), 480, 650);
        gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL);
+       gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(state->window_main));
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
 
        g_signal_connect(G_OBJECT(window), "delete_event",
-                        G_CALLBACK(callback_do_close), window);
+                        G_CALLBACK(callback_do_close_widget), NULL);
 
        gtk_container_set_border_width(GTK_CONTAINER(window), 10);
 
@@ -1041,10 +1207,14 @@ static void callback_do_change(GtkWidget *widget,
                char *str = NULL;
                entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
                if (state->name_type_initial == NetSetupDomainName) {
-                       asprintf(&str, "%s.%s", entry_text,
-                                state->my_dnsdomain);
+                       if (asprintf(&str, "%s.%s", entry_text,
+                                state->my_dnsdomain) == -1) {
+                               return;
+                       }
                } else {
-                       asprintf(&str, "%s.", entry_text);
+                       if (asprintf(&str, "%s.", entry_text) == -1) {
+                               return;
+                       }
                }
                gtk_label_set_text(GTK_LABEL(state->label_full_computer_name),
                                   str);
@@ -1189,6 +1359,8 @@ static void callback_do_about(GtkWidget *widget,
        GError    *error = NULL;
        GtkWidget *about;
 
+       struct join_state *state = (struct join_state *)data;
+
        debug("callback_do_about called\n");
 
        logo = gdk_pixbuf_new_from_file(SAMBA_IMAGE_PATH,
@@ -1212,6 +1384,7 @@ static void callback_do_about(GtkWidget *widget,
        }
        gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), "Samba gtk domain join utility");
        gtk_window_set_modal(GTK_WINDOW(about), TRUE);
+       gtk_window_set_transient_for(GTK_WINDOW(about), GTK_WINDOW(state->window_main));
        g_signal_connect_swapped(about, "response",
                                 G_CALLBACK(gtk_widget_destroy),
                                 about);
@@ -1258,6 +1431,7 @@ static int draw_main_window(struct join_state *state)
        gtk_widget_set_size_request(GTK_WIDGET(window), 600, 600);
        gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
        gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL);
+       gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
 
        g_signal_connect(G_OBJECT(window), "delete_event",
                         G_CALLBACK(callback_delete_event), NULL);
@@ -1315,6 +1489,10 @@ static int draw_main_window(struct join_state *state)
                /* Entry */
                entry = gtk_entry_new();
                gtk_entry_set_max_length(GTK_ENTRY(entry), 256);
+
+               if (!state->target_hostname && state->uid != 0) {
+                       gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE);
+               }
                g_signal_connect(G_OBJECT(entry), "changed",
                                 G_CALLBACK(callback_enter_computer_description_and_unlock),
                                 state);
@@ -1345,10 +1523,14 @@ static int draw_main_window(struct join_state *state)
                /* Label */
                char *str = NULL;
                if (state->name_type_initial == NetSetupDomainName) {
-                       asprintf(&str, "%s.%s", state->my_hostname,
-                                state->my_dnsdomain);
+                       if (asprintf(&str, "%s.%s", state->my_hostname,
+                                state->my_dnsdomain) == -1) {
+                               return -1;
+                       }
                } else {
-                       asprintf(&str, "%s.", state->my_hostname);
+                       if (asprintf(&str, "%s.", state->my_hostname) == -1) {
+                               return -1;
+                       }
                }
 
                label = gtk_label_new(str);
@@ -1397,6 +1579,9 @@ static int draw_main_window(struct join_state *state)
                         G_CALLBACK(callback_do_change),
                         (gpointer)state);
        gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+       if (!state->target_hostname && state->uid != 0) {
+               gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+       }
        gtk_widget_show(button);
 
        /* Label (hidden) */
@@ -1404,6 +1589,11 @@ static int draw_main_window(struct join_state *state)
        gtk_label_set_line_wrap(GTK_LABEL(state->label_reboot), TRUE);
        gtk_misc_set_alignment(GTK_MISC(state->label_reboot), 0, 0);
        gtk_box_pack_start(GTK_BOX(vbox), state->label_reboot, TRUE, TRUE, 0);
+       if (!state->target_hostname && state->uid != 0) {
+               gtk_label_set_text(GTK_LABEL(state->label_reboot),
+                          "You cannot change computer description as you're not running with root permissions");
+       }
+
        gtk_widget_show(state->label_reboot);
 
 #if 0
@@ -1448,7 +1638,7 @@ static int draw_main_window(struct join_state *state)
                gtk_container_add(GTK_CONTAINER(bbox2), button2);
                g_signal_connect(G_OBJECT(button2), "clicked",
                                 G_CALLBACK(callback_do_about),
-                                window);
+                                state);
 #if 0
                button2 = gtk_button_new_from_stock(GTK_STOCK_HELP);
                gtk_container_add(GTK_CONTAINER(bbox2), button2);
@@ -1468,7 +1658,7 @@ static int init_join_state(struct join_state **state)
 {
        struct join_state *s;
 
-       s = malloc(sizeof(struct join_state));
+       s = (struct join_state *)malloc(sizeof(struct join_state));
        if (!s) {
                return -1;
        }
@@ -1480,8 +1670,60 @@ static int init_join_state(struct join_state **state)
        return 0;
 }
 
+static NET_API_STATUS get_server_properties(struct join_state *state)
+{
+       struct SERVER_INFO_101 *info101 = NULL;
+       struct SERVER_INFO_1005 *info1005 = NULL;
+       NET_API_STATUS status;
+
+       status = NetServerGetInfo(state->target_hostname,
+                                 101,
+                                 (uint8_t **)&info101);
+       if (status == 0) {
+               state->comment = strdup(info101->sv101_comment);
+               if (!state->comment) {
+                       return -1;
+               }
+               SAFE_FREE(state->my_hostname);
+               state->my_hostname = strdup(info101->sv101_name);
+               if (!state->my_hostname) {
+                       return -1;
+               }
+               NetApiBufferFree(info101);
+               return NET_API_STATUS_SUCCESS;
+       }
+
+       switch (status) {
+               case 124: /* WERR_UNKNOWN_LEVEL */
+               case 50: /* WERR_NOT_SUPPORTED */
+                       break;
+               default:
+                       goto failed;
+       }
+
+       status = NetServerGetInfo(state->target_hostname,
+                                 1005,
+                                 (uint8_t **)&info1005);
+       if (status == 0) {
+               state->comment = strdup(info1005->sv1005_comment);
+               if (!state->comment) {
+                       return -1;
+               }
+               NetApiBufferFree(info1005);
+               return NET_API_STATUS_SUCCESS;
+       }
+
+ failed:
+       printf("NetServerGetInfo failed with: %s\n",
+               libnetapi_get_error_string(state->ctx, status));
+
+       return status;
+}
+
 static int initialize_join_state(struct join_state *state,
-                                const char *debug_level)
+                                const char *debug_level,
+                                const char *target_hostname,
+                                const char *target_username)
 {
        struct libnetapi_ctx *ctx = NULL;
        NET_API_STATUS status = 0;
@@ -1495,6 +1737,30 @@ static int initialize_join_state(struct join_state *state,
                libnetapi_set_debuglevel(ctx, debug_level);
        }
 
+       if (target_hostname) {
+               state->target_hostname = strdup(target_hostname);
+               if (!state->target_hostname) {
+                       return -1;
+               }
+       }
+
+       if (target_username) {
+               char *puser = strdup(target_username);
+               char *p = NULL;
+
+               if ((p = strchr(puser,'%'))) {
+                       size_t len;
+                       *p = 0;
+                       libnetapi_set_username(ctx, puser);
+                       libnetapi_set_password(ctx, p+1);
+                       len = strlen(p+1);
+                       memset(strchr(target_username,'%')+1,'X',len);
+               } else {
+                       libnetapi_set_username(ctx, puser);
+               }
+               free(puser);
+       }
+
        {
                char my_hostname[HOST_NAME_MAX];
                const char *p = NULL;
@@ -1541,7 +1807,9 @@ static int initialize_join_state(struct join_state *state,
        {
                const char *buffer = NULL;
                uint16_t type = 0;
-               status = NetGetJoinInformation(NULL, &buffer, &type);
+               status = NetGetJoinInformation(state->target_hostname,
+                                              &buffer,
+                                              &type);
                if (status != 0) {
                        printf("NetGetJoinInformation failed with: %s\n",
                                libnetapi_get_error_string(state->ctx, status));
@@ -1556,43 +1824,12 @@ static int initialize_join_state(struct join_state *state,
                NetApiBufferFree((void *)buffer);
        }
 
-       {
-               struct SERVER_INFO_1005 *info1005 = NULL;
-               uint8_t *buffer = NULL;
-
-               status = NetServerGetInfo(NULL, 1005, &buffer);
-               if (status != 0) {
-                       printf("NetServerGetInfo failed with: %s\n",
-                               libnetapi_get_error_string(state->ctx, status));
-                       return status;
-               }
-
-               info1005 = (struct SERVER_INFO_1005 *)buffer;
-
-               state->comment = strdup(info1005->sv1005_comment);
-               if (!state->comment) {
-                       return -1;
-               }
-               NetApiBufferFree(buffer);
+       status = get_server_properties(state);
+       if (status != 0) {
+               return -1;
        }
-#if 0
-       {
-               struct srvsvc_NetSrvInfo100 *info100 = NULL;
-               uint8_t *buffer = NULL;
-
-               status = NetServerGetInfo(NULL, 100, &buffer);
-               if (status) {
-                       return status;
-               }
-
-               info100 = (struct srvsvc_NetSrvInfo100 *)buffer;
 
-               state->comment = strdup(info100->comment);
-               if (!state->comment) {
-                       return -1;
-               }
-       }
-#endif
+       state->uid = geteuid();
 
        state->ctx = ctx;
 
@@ -1603,6 +1840,8 @@ int main(int argc, char **argv)
 {
        GOptionContext *context = NULL;
        static const char *debug_level = NULL;
+       static const char *target_hostname = NULL;
+       static const char *target_username = NULL;
        struct join_state *state = NULL;
        GError *error = NULL;
        int ret = 0;
@@ -1610,6 +1849,8 @@ int main(int argc, char **argv)
        static GOptionEntry entries[] = {
                { "debug", 'd', 0, G_OPTION_ARG_STRING, &debug_level, "Debug level (for samba)", "N" },
                { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose output", 0 },
+               { "target", 'S', 0, G_OPTION_ARG_STRING, &target_hostname, "Target hostname", 0 },
+               { "username", 'U', 0, G_OPTION_ARG_STRING, &target_username, "Target hostname", 0 },
                { NULL }
        };
 
@@ -1627,7 +1868,9 @@ int main(int argc, char **argv)
                return ret;
        }
 
-       ret = initialize_join_state(state, debug_level);
+       ret = initialize_join_state(state, debug_level,
+                                   target_hostname,
+                                   target_username);
        if (ret) {
                return ret;
        }