/*
 * Copyright (c) [2012-2015] Novell, Inc.
 * Copyright (c) [2018-2025] SUSE LLC
 *
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * 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, contact Novell, Inc.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact information at www.novell.com.
 */


#include <cstring>
#include <sys/types.h>

#include <snapper/LoggerImpl.h>
#include <snapper/AppUtil.h>
#include <snapper/SnapperDefines.h>

#include "MetaSnapper.h"
#include "Client.h"


MetaSnappers meta_snappers;


MetaSnapper::MetaSnapper(const ConfigInfo& config_info)
    : config_info(config_info)
{
    set_permissions();
}


MetaSnapper::~MetaSnapper()
{
}


void
MetaSnapper::setConfigInfo(const map<string, string>& raw)
{
    for (map<string, string>::const_iterator it = raw.begin(); it != raw.end(); ++it)
	config_info.set_value(it->first, it->second);

    getSnapper()->setConfigInfo(raw);

    if (raw.find(KEY_ALLOW_USERS) != raw.end() || raw.find(KEY_ALLOW_GROUPS) != raw.end())
	set_permissions();
}


void
MetaSnapper::updateConfigInfo(const char* key)
{
    string value;

    if (getSnapper()->getConfigInfo().get_value(key, value))
	 config_info.set_value(key, value);
}


void
MetaSnapper::set_permissions()
{
    allowed_uids.clear();

    vector<string> users;
    if (config_info.get_value(KEY_ALLOW_USERS, users))
    {
	for (const string& user : users)
	{
	    uid_t tmp;
	    if (get_user_uid(user.c_str(), tmp))
		allowed_uids.push_back(tmp);
	}
    }

    sort(allowed_uids.begin(), allowed_uids.end());
    allowed_uids.erase(unique(allowed_uids.begin(), allowed_uids.end()), allowed_uids.end());

    allowed_gids.clear();

    vector<string> groups;
    if (config_info.get_value(KEY_ALLOW_GROUPS, groups))
    {
	for (const string& group : groups)
	{
	    gid_t tmp;
	    if (get_group_gid(group.c_str(), tmp))
		allowed_gids.push_back(tmp);
	}
    }

    sort(allowed_gids.begin(), allowed_gids.end());
    allowed_gids.erase(unique(allowed_gids.begin(), allowed_gids.end()), allowed_gids.end());
}


Snapper*
MetaSnapper::getSnapper()
{
    if (!snapper)
	snapper = make_unique<Snapper>(config_info.get_config_name(), "/");

    update_use_time();

    return snapper.get();
}


void
MetaSnapper::unload()
{
    snapper.reset();
}


bool
MetaSnapper::is_locked(const Clients& clients) const
{
    for (const Client& client : clients)
	if (client.has_lock(configName()))
	    return true;

    return false;
}


MetaSnappers::MetaSnappers()
{
}


MetaSnappers::~MetaSnappers()
{
}


void
MetaSnappers::init()
{
    list<ConfigInfo> config_infos = Snapper::getConfigs("/");

    for (const ConfigInfo& config_info : config_infos)
	entries.emplace_back(config_info);
}


void
MetaSnappers::unload()
{
    for (MetaSnapper& meta_snapper : entries)
	meta_snapper.unload();
}


MetaSnappers::iterator
MetaSnappers::find(const string& config_name)
{
    for (iterator it = entries.begin(); it != entries.end(); ++it)
	if (it->configName() == config_name)
	    return it;

    SN_THROW(UnknownConfig());
    __builtin_unreachable();
}


void
MetaSnappers::createConfig(const string& config_name, const string& subvolume, const string& fstype,
			   const string& template_name, Plugins::Report& report)
{
    Snapper::createConfig(config_name, "/", subvolume, fstype, template_name, report);

    ConfigInfo config_info = Snapper::getConfig(config_name, "/");

    entries.emplace_back(config_info);
}


void
MetaSnappers::deleteConfig(iterator it, Plugins::Report& report)
{
    Snapper::deleteConfig(it->configName(), "/", report);

    entries.erase(it);
}
