/* Chown command -- for the Midnight Commander Copyright (C) 1994-2024 Free Software Foundation, Inc. This file is part of the Midnight Commander. The Midnight Commander 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 3 of the License, or (at your option) any later version. The Midnight Commander 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, see . */ /** \file chown.c * \brief Source: chown command */ #include #include #include #include #include #include #include #include #include #include "lib/global.h" #include "lib/tty/tty.h" #include "lib/skin.h" #include "lib/vfs/vfs.h" #include "lib/strutil.h" #include "lib/util.h" #include "lib/widget.h" #include "src/setup.h" /* panels_options */ #include "cmd.h" /* chown_cmd() */ /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ #define GH 12 #define GW 21 #define BUTTONS 5 #define B_SETALL B_USER #define B_SETUSR (B_USER + 1) #define B_SETGRP (B_USER + 2) #define LABELS 5 #define chown_label(n,txt) label_set_text (chown_label [n].l, txt) /*** file scope type declarations ****************************************************************/ /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ static struct { int ret_cmd; button_flags_t flags; int y; int len; const char *text; } chown_but[BUTTONS] = { /* *INDENT-OFF* */ { B_SETALL, NORMAL_BUTTON, 5, 0, N_("Set &all") }, { B_SETGRP, NORMAL_BUTTON, 5, 0, N_("Set &groups") }, { B_SETUSR, NORMAL_BUTTON, 5, 0, N_("Set &users") }, { B_ENTER, DEFPUSH_BUTTON, 3, 0, N_("&Set") }, { B_CANCEL, NORMAL_BUTTON, 3, 0, N_("&Cancel") } /* *INDENT-ON* */ }; /* summary length of three buttons */ static int blen = 0; static struct { int y; WLabel *l; } chown_label[LABELS] = { /* *INDENT-OFF* */ { 4, NULL }, { 6, NULL }, { 8, NULL }, { 10, NULL }, { 12, NULL } /* *INDENT-ON* */ }; static int current_file; static gboolean ignore_all; static WListbox *l_user, *l_group; /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static void chown_init (void) { static gboolean i18n = FALSE; int i; if (i18n) return; i18n = TRUE; #ifdef ENABLE_NLS for (i = 0; i < BUTTONS; i++) chown_but[i].text = _(chown_but[i].text); #endif /* ENABLE_NLS */ for (i = 0; i < BUTTONS; i++) { chown_but[i].len = str_term_width1 (chown_but[i].text) + 3; /* [], spaces and w/o & */ if (chown_but[i].flags == DEFPUSH_BUTTON) chown_but[i].len += 2; /* <> */ if (i < BUTTONS - 2) blen += chown_but[i].len; } blen += 2; } /* --------------------------------------------------------------------------------------------- */ static void chown_refresh (const Widget * h) { int y = 3; int x = 7 + GW * 2; tty_setcolor (COLOR_NORMAL); widget_gotoyx (h, y + 0, x); tty_print_string (_("Name")); widget_gotoyx (h, y + 2, x); tty_print_string (_("Owner name")); widget_gotoyx (h, y + 4, x); tty_print_string (_("Group name")); widget_gotoyx (h, y + 6, x); tty_print_string (_("Size")); widget_gotoyx (h, y + 8, x); tty_print_string (_("Permission")); } /* --------------------------------------------------------------------------------------------- */ static cb_ret_t chown_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) { switch (msg) { case MSG_DRAW: frame_callback (w, NULL, MSG_DRAW, 0, NULL); chown_refresh (WIDGET (w->owner)); return MSG_HANDLED; default: return frame_callback (w, sender, msg, parm, data); } } /* --------------------------------------------------------------------------------------------- */ static WDialog * chown_dlg_create (WPanel * panel) { int single_set; WDialog *ch_dlg; WGroup *g; int lines, cols; int i, y; struct passwd *l_pass; struct group *l_grp; single_set = (panel->marked < 2) ? 3 : 0; lines = GH + 4 + (single_set != 0 ? 2 : 4); cols = GW * 3 + 2 + 6; ch_dlg = dlg_create (TRUE, 0, 0, lines, cols, WPOS_CENTER, FALSE, dialog_colors, NULL, NULL, "[Chown]", _("Chown command")); g = GROUP (ch_dlg); /* draw background */ ch_dlg->bg->callback = chown_bg_callback; group_add_widget (g, groupbox_new (2, 3, GH, GW, _("User name"))); l_user = listbox_new (3, 4, GH - 2, GW - 2, FALSE, NULL); group_add_widget (g, l_user); /* add field for unknown names (numbers) */ listbox_add_item (l_user, LISTBOX_APPEND_AT_END, 0, _(""), NULL, FALSE); /* get and put user names in the listbox */ setpwent (); while ((l_pass = getpwent ()) != NULL) listbox_add_item (l_user, LISTBOX_APPEND_SORTED, 0, l_pass->pw_name, NULL, FALSE); endpwent (); group_add_widget (g, groupbox_new (2, 4 + GW, GH, GW, _("Group name"))); l_group = listbox_new (3, 5 + GW, GH - 2, GW - 2, FALSE, NULL); group_add_widget (g, l_group); /* add field for unknown names (numbers) */ listbox_add_item (l_group, LISTBOX_APPEND_AT_END, 0, _(""), NULL, FALSE); /* get and put group names in the listbox */ setgrent (); while ((l_grp = getgrent ()) != NULL) listbox_add_item (l_group, LISTBOX_APPEND_SORTED, 0, l_grp->gr_name, NULL, FALSE); endgrent (); group_add_widget (g, groupbox_new (2, 5 + GW * 2, GH, GW, _("File"))); /* add widgets for the file information */ for (i = 0; i < LABELS; i++) { chown_label[i].l = label_new (chown_label[i].y, 7 + GW * 2, NULL); group_add_widget (g, chown_label[i].l); } if (single_set == 0) { int x; group_add_widget (g, hline_new (lines - chown_but[0].y - 1, -1, -1)); y = lines - chown_but[0].y; x = (cols - blen) / 2; for (i = 0; i < BUTTONS - 2; i++) { group_add_widget (g, button_new (y, x, chown_but[i].ret_cmd, chown_but[i].flags, chown_but[i].text, NULL)); x += chown_but[i].len + 1; } } i = BUTTONS - 2; y = lines - chown_but[i].y; group_add_widget (g, hline_new (y - 1, -1, -1)); group_add_widget (g, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 - chown_but[i].len, chown_but[i].ret_cmd, chown_but[i].flags, chown_but[i].text, NULL)); i++; group_add_widget (g, button_new (y, WIDGET (ch_dlg)->rect.cols / 2 + 1, chown_but[i].ret_cmd, chown_but[i].flags, chown_but[i].text, NULL)); /* select first listbox */ widget_select (WIDGET (l_user)); return ch_dlg; } /* --------------------------------------------------------------------------------------------- */ static void chown_done (gboolean need_update) { if (need_update) update_panels (UP_OPTIMIZE, UP_KEEPSEL); repaint_screen (); } /* --------------------------------------------------------------------------------------------- */ static const GString * next_file (const WPanel * panel) { while (panel->dir.list[current_file].f.marked == 0) current_file++; return panel->dir.list[current_file].fname; } /* --------------------------------------------------------------------------------------------- */ static gboolean try_chown (const vfs_path_t * p, uid_t u, gid_t g) { const char *fname = NULL; while (mc_chown (p, u, g) == -1 && !ignore_all) { int my_errno = errno; int result; char *msg; if (fname == NULL) fname = x_basename (vfs_path_as_str (p)); msg = g_strdup_printf (_("Cannot chown \"%s\"\n%s"), fname, unix_error_string (my_errno)); result = query_dialog (MSG_ERROR, msg, D_ERROR, 4, _("&Ignore"), _("Ignore &all"), _("&Retry"), _("&Cancel")); g_free (msg); switch (result) { case 0: /* try next file */ return TRUE; case 1: ignore_all = TRUE; /* try next file */ return TRUE; case 2: /* retry this file */ break; case 3: default: /* stop remain files processing */ return FALSE; } } return TRUE; } /* --------------------------------------------------------------------------------------------- */ static gboolean do_chown (WPanel * panel, const vfs_path_t * p, uid_t u, gid_t g) { gboolean ret; ret = try_chown (p, u, g); do_file_mark (panel, current_file, 0); return ret; } /* --------------------------------------------------------------------------------------------- */ static void apply_chowns (WPanel * panel, vfs_path_t * vpath, uid_t u, gid_t g) { gboolean ok; if (!do_chown (panel, vpath, u, g)) return; do { const GString *fname; struct stat sf; fname = next_file (panel); vpath = vfs_path_from_str (fname->str); ok = (mc_stat (vpath, &sf) == 0); if (!ok) { /* if current file was deleted outside mc -- try next file */ /* decrease panel->marked */ do_file_mark (panel, current_file, 0); /* try next file */ ok = TRUE; } else ok = do_chown (panel, vpath, u, g); vfs_path_free (vpath, TRUE); } while (ok && panel->marked != 0); } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void chown_cmd (WPanel * panel) { gboolean need_update; gboolean end_chown; chown_init (); current_file = 0; ignore_all = FALSE; do { /* do while any files remaining */ vfs_path_t *vpath; WDialog *ch_dlg; struct stat sf_stat; const GString *fname; int result; char buffer[BUF_TINY]; uid_t new_user = (uid_t) (-1); gid_t new_group = (gid_t) (-1); do_refresh (); need_update = FALSE; end_chown = FALSE; if (panel->marked != 0) fname = next_file (panel); /* next marked file */ else fname = panel_current_entry (panel)->fname; /* single file */ vpath = vfs_path_from_str (fname->str); if (mc_stat (vpath, &sf_stat) != 0) { vfs_path_free (vpath, TRUE); break; } ch_dlg = chown_dlg_create (panel); /* select in listboxes */ listbox_set_current (l_user, listbox_search_text (l_user, get_owner (sf_stat.st_uid))); listbox_set_current (l_group, listbox_search_text (l_group, get_group (sf_stat.st_gid))); chown_label (0, str_trunc (fname->str, GW - 4)); chown_label (1, str_trunc (get_owner (sf_stat.st_uid), GW - 4)); chown_label (2, str_trunc (get_group (sf_stat.st_gid), GW - 4)); size_trunc_len (buffer, GW - 4, sf_stat.st_size, 0, panels_options.kilobyte_si); chown_label (3, buffer); chown_label (4, string_perm (sf_stat.st_mode)); result = dlg_run (ch_dlg); switch (result) { case B_CANCEL: end_chown = TRUE; break; case B_ENTER: case B_SETALL: { struct group *grp; struct passwd *user; char *text; listbox_get_current (l_group, &text, NULL); grp = getgrnam (text); if (grp != NULL) new_group = grp->gr_gid; listbox_get_current (l_user, &text, NULL); user = getpwnam (text); if (user != NULL) new_user = user->pw_uid; if (result == B_ENTER) { if (panel->marked <= 1) { /* single or last file */ if (mc_chown (vpath, new_user, new_group) == -1) message (D_ERROR, MSG_ERROR, _("Cannot chown \"%s\"\n%s"), fname->str, unix_error_string (errno)); end_chown = TRUE; } else if (!try_chown (vpath, new_user, new_group)) { /* stop multiple files processing */ result = B_CANCEL; end_chown = TRUE; } } else { apply_chowns (panel, vpath, new_user, new_group); end_chown = TRUE; } need_update = TRUE; break; } case B_SETUSR: { struct passwd *user; char *text; listbox_get_current (l_user, &text, NULL); user = getpwnam (text); if (user != NULL) { new_user = user->pw_uid; apply_chowns (panel, vpath, new_user, new_group); need_update = TRUE; end_chown = TRUE; } break; } case B_SETGRP: { struct group *grp; char *text; listbox_get_current (l_group, &text, NULL); grp = getgrnam (text); if (grp != NULL) { new_group = grp->gr_gid; apply_chowns (panel, vpath, new_user, new_group); need_update = TRUE; end_chown = TRUE; } break; } default: break; } if (panel->marked != 0 && result != B_CANCEL) { do_file_mark (panel, current_file, 0); need_update = TRUE; } vfs_path_free (vpath, TRUE); widget_destroy (WIDGET (ch_dlg)); } while (panel->marked != 0 && !end_chown); chown_done (need_update); } /* --------------------------------------------------------------------------------------------- */