// This file is part of Moonfire NVR, a security camera digital video recorder. // Copyright (C) 2017 Scott Lamb // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations including // the two. // // You must obey the GNU General Public License in all respects for all // of the code used other than OpenSSL. If you modify file(s) with this // exception, you may extend this exception to your version of the // file(s), but you are not obligated to do so. If you do not wish to do // so, delete this exception statement from your version. If you delete // this exception statement from all source files in the program, then // also delete it here. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . extern crate cursive; use self::cursive::Cursive; use self::cursive::traits::{Boxable, Identifiable}; use self::cursive::views; use db; use std::sync::Arc; /// Builds a `UserChange` from an active `edit_user_dialog`. fn get_change(siv: &mut Cursive, db: &db::LockedDatabase, id: Option, pw: PasswordChange) -> db::UserChange { let mut change = match id { Some(id) => db.users_by_id().get(&id).unwrap().change(), None => db::UserChange::add_user(String::new()), }; change.username.clear(); change.username += siv.find_id::("username").unwrap().get_content().as_str(); match pw { PasswordChange::Leave => {}, PasswordChange::Set => { let pwd = siv.find_id::("new_pw").unwrap().get_content(); change.set_password(pwd.as_str().into()); }, PasswordChange::Clear => change.clear_password(), }; change } fn press_edit(siv: &mut Cursive, db: &Arc, id: Option, pw: PasswordChange) { let result = { let mut l = db.lock(); let c = get_change(siv, &l, id, pw); l.apply_user_change(c).map(|_| ()) }; if let Err(e) = result { siv.add_layer(views::Dialog::text(format!("Unable to apply change: {}", e)) .title("Error") .dismiss_button("Abort")); } else { siv.pop_layer(); // get rid of the add/edit user dialog. // Recreate the "Edit users" dialog from scratch; it's easier than adding the new entry. siv.pop_layer(); top_dialog(db, siv); } } fn press_delete(siv: &mut Cursive, db: &Arc, id: i32, name: String) { siv.add_layer(views::Dialog::text(format!("Delete user {}?", name)) .button("Delete", { let db = db.clone(); move |s| actually_delete(s, &db, id) }) .title("Delete user").dismiss_button("Cancel")); } fn actually_delete(siv: &mut Cursive, db: &Arc, id: i32) { siv.pop_layer(); // get rid of the add/edit user dialog. let result = { let mut l = db.lock(); l.delete_user(id) }; if let Err(e) = result { siv.add_layer(views::Dialog::text(format!("Unable to delete user: {}", e)) .title("Error") .dismiss_button("Abort")); } else { // Recreate the "Edit users" dialog from scratch; it's easier than adding the new entry. siv.pop_layer(); top_dialog(db, siv); } } #[derive(Copy, Clone)] enum PasswordChange { Leave, Clear, Set, } fn select_set(siv: &mut Cursive) { siv.find_id::>("pw_set").unwrap().select(); } /// Adds or updates a user. /// (The former if `item` is None; the latter otherwise.) fn edit_user_dialog(db: &Arc, siv: &mut Cursive, item: Option) { let username; let id_str; let has_password; let mut pw_group = views::RadioGroup::new(); { let l = db.lock(); let u = item.map(|id| l.users_by_id().get(&id).unwrap()); username = u.map(|u| u.username.clone()).unwrap_or(String::new()); id_str = item.map(|id| id.to_string()).unwrap_or("".to_string()); has_password = u.map(|u| u.has_password()).unwrap_or(false); } let top_list = views::ListView::new() .child("id", views::TextView::new(id_str)) .child("username", views::EditView::new() .content(username.clone()) .with_id("username")); let mut layout = views::LinearLayout::vertical() .child(top_list) .child(views::DummyView) .child(views::TextView::new("password")); if has_password { layout.add_child(pw_group.button(PasswordChange::Leave, "Leave set")); layout.add_child(pw_group.button(PasswordChange::Clear, "Clear")); layout.add_child(views::LinearLayout::horizontal() .child(pw_group.button(PasswordChange::Set, "Set to:") .with_id("pw_set")) .child(views::DummyView) .child(views::EditView::new() .on_edit(|siv, _, _| select_set(siv)) .with_id("new_pw") .full_width())); } else { layout.add_child(pw_group.button(PasswordChange::Leave, "Leave unset")); layout.add_child(views::LinearLayout::horizontal() .child(pw_group.button(PasswordChange::Set, "Reset to:") .with_id("pw_set")) .child(views::DummyView) .child(views::EditView::new() .on_edit(|siv, _, _| select_set(siv)) .with_id("new_pw") .full_width())); } let dialog = views::Dialog::around(layout); let dialog = if let Some(id) = item { dialog.title("Edit user") .button("Edit", { let db = db.clone(); move |s| press_edit(s, &db, item, *pw_group.selection()) }) .button("Delete", { let db = db.clone(); move |s| press_delete(s, &db, id, username.clone()) }) } else { dialog.title("Add user") .button("Add", { let db = db.clone(); move |s| press_edit(s, &db, item, *pw_group.selection()) }) }; siv.add_layer(dialog.dismiss_button("Cancel")); } pub fn top_dialog(db: &Arc, siv: &mut Cursive) { siv.add_layer(views::Dialog::around( views::SelectView::new() .on_submit({ let db = db.clone(); move |siv, &item| edit_user_dialog(&db, siv, item) }) .item("".to_string(), None) .with_all(db.lock() .users_by_id() .iter() .map(|(&id, user)| (format!("{}: {}", id, user.username), Some(id)))) .full_width()) .dismiss_button("Done") .title("Edit cameras")); }