1223 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1223 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								#include <algorithm>
							 | 
						||
| 
								 | 
							
								#include <utility>
							 | 
						||
| 
								 | 
							
								#include <log/LogUtils.h>
							 | 
						||
| 
								 | 
							
								#include <misc/std_unique_ptr.h>
							 | 
						||
| 
								 | 
							
								#include <Properties.h>
							 | 
						||
| 
								 | 
							
								#include "ServerManager.h"
							 | 
						||
| 
								 | 
							
								#include "src/server/VoiceServer.h"
							 | 
						||
| 
								 | 
							
								#include "InstanceHandler.h"
							 | 
						||
| 
								 | 
							
								#include "InstanceHandler.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								using namespace std;
							 | 
						||
| 
								 | 
							
								using namespace ts;
							 | 
						||
| 
								 | 
							
								using namespace ts::server;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define PREFIX string("[SNAPSHOT] ")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inline std::string avArguments(const ts::Command& cmd, int index){
							 | 
						||
| 
								 | 
							
									stringstream res;
							 | 
						||
| 
								 | 
							
									for(const auto &key : cmd[index].keys())
							 | 
						||
| 
								 | 
							
										res << key << "=" << cmd[index][key].as<std::string>() << " ";
							 | 
						||
| 
								 | 
							
									return res.str();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//TeamSpeak: permid=i_channel_needed_permission_modify_power permvalue=75 permskip=0 permnegated=0
							 | 
						||
| 
								 | 
							
								//TeaSpeak: perm=i_channel_needed_permission_modify_power value=75 granted=75 flag_skip=0 flag_negated=0
							 | 
						||
| 
								 | 
							
								struct SnapshotPermissionEntry {
							 | 
						||
| 
								 | 
							
									std::shared_ptr<permission::PermissionTypeEntry> type = permission::PermissionTypeEntry::unknown;
							 | 
						||
| 
								 | 
							
									permission::PermissionValue value = permNotGranted;
							 | 
						||
| 
								 | 
							
									permission::PermissionValue grant = permNotGranted;
							 | 
						||
| 
								 | 
							
									bool negated = false;
							 | 
						||
| 
								 | 
							
									bool skipped = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									SnapshotPermissionEntry(const std::shared_ptr<permission::PermissionTypeEntry>& type, permission::PermissionValue value, permission::PermissionValue grant) : type(std::move(type)), value(value), grant(grant) {}
							 | 
						||
| 
								 | 
							
									SnapshotPermissionEntry(const std::shared_ptr<permission::PermissionTypeEntry>& type, permission::PermissionValue value, permission::PermissionValue grant, bool negated, bool skipped) : type(type), value(value), grant(grant), negated(negated), skipped(skipped) {}
							 | 
						||
| 
								 | 
							
									SnapshotPermissionEntry() = default;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static deque<std::unique_ptr<SnapshotPermissionEntry>> parse(const Command& cmd, int& index, permission::teamspeak::GroupType type, int version, const std::vector<std::string>& ends, bool ignore_first = false) {
							 | 
						||
| 
								 | 
							
										deque<std::unique_ptr<SnapshotPermissionEntry>> result{};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										while(true) {
							 | 
						||
| 
								 | 
							
											bool end_reached = false;
							 | 
						||
| 
								 | 
							
											for(const auto& e : ends)
							 | 
						||
| 
								 | 
							
												if(cmd[index].has(e)) { end_reached = true; break; }
							 | 
						||
| 
								 | 
							
											if(end_reached || cmd.bulkCount() < index) {
							 | 
						||
| 
								 | 
							
												if(ignore_first) ignore_first = false;
							 | 
						||
| 
								 | 
							
												else break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if(version == 0) {
							 | 
						||
| 
								 | 
							
												auto permission_name = cmd[index]["permid"].string();
							 | 
						||
| 
								 | 
							
												for(const auto& mapped : permission::teamspeak::map_key(permission_name, type)) {
							 | 
						||
| 
								 | 
							
													auto permission = permission::resolvePermissionData(mapped);
							 | 
						||
| 
								 | 
							
													if(permission->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
														logError(0, "Failed to parse permission {}. Original: {}", mapped, permission_name);
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													bool found = false;
							 | 
						||
| 
								 | 
							
													for(auto& e : result)
							 | 
						||
| 
								 | 
							
														if(e->type->type == permission->type) {
							 | 
						||
| 
								 | 
							
															found = true;
							 | 
						||
| 
								 | 
							
															if(permission->grantName() == mapped)
							 | 
						||
| 
								 | 
							
																e->grant = cmd[index]["permvalue"];
							 | 
						||
| 
								 | 
							
															else {
							 | 
						||
| 
								 | 
							
																e->value = cmd[index]["permvalue"];
							 | 
						||
| 
								 | 
							
																e->negated = cmd[index]["permnegated"];
							 | 
						||
| 
								 | 
							
																e->skipped = cmd[index]["permskip"];
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													if(!found) {
							 | 
						||
| 
								 | 
							
														auto entry = make_unique<SnapshotPermissionEntry>();
							 | 
						||
| 
								 | 
							
														entry->type = permission;
							 | 
						||
| 
								 | 
							
														if(permission->grantName() == mapped)
							 | 
						||
| 
								 | 
							
															entry->grant = cmd[index]["permvalue"];
							 | 
						||
| 
								 | 
							
														else {
							 | 
						||
| 
								 | 
							
															entry->value = cmd[index]["permvalue"];
							 | 
						||
| 
								 | 
							
															entry->negated = cmd[index]["permnegated"];
							 | 
						||
| 
								 | 
							
															entry->skipped = cmd[index]["permskip"];
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														result.push_back(std::move(entry));
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											} else if(version >= 1) {
							 | 
						||
| 
								 | 
							
												auto permission_name = cmd[index]["perm"].string();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												auto permission = permission::resolvePermissionData(permission_name);
							 | 
						||
| 
								 | 
							
												if(permission->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
													logError(0, "Failed to parse permission {}", permission_name);
							 | 
						||
| 
								 | 
							
													index++;
							 | 
						||
| 
								 | 
							
													continue;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												auto entry = make_unique<SnapshotPermissionEntry>();
							 | 
						||
| 
								 | 
							
												entry->type = permission;
							 | 
						||
| 
								 | 
							
												entry->value = cmd[index]["value"];
							 | 
						||
| 
								 | 
							
												entry->grant = cmd[index]["grant"];
							 | 
						||
| 
								 | 
							
												entry->skipped = cmd[index]["flag_skip"];
							 | 
						||
| 
								 | 
							
												entry->negated = cmd[index]["flag_negated"];
							 | 
						||
| 
								 | 
							
												result.push_back(std::move(entry));
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												logError(0, "Failed to parse snapshot permission entry! Invalid version!");
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											index++;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										deque<std::unique_ptr<SnapshotPermissionEntry>> addings{};
							 | 
						||
| 
								 | 
							
										for(const auto& perm : result) {
							 | 
						||
| 
								 | 
							
											if(perm->type->type == permission::i_group_auto_update_type) { //Migrate this type
							 | 
						||
| 
								 | 
							
												for(const auto& e : permission::update::migrate)
							 | 
						||
| 
								 | 
							
													if(e.type == perm->value) {
							 | 
						||
| 
								 | 
							
														auto _type = permission::resolvePermissionData(e.permission.name);
							 | 
						||
| 
								 | 
							
														if(_type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
															logError(0, "Invalid update permission for update group {}. Permission name: {}", e.type, e.permission.name);
							 | 
						||
| 
								 | 
							
														} else {
							 | 
						||
| 
								 | 
							
															addings.push_back(make_unique<SnapshotPermissionEntry>(_type, e.permission.value, e.permission.granted, e.permission.negated, e.permission.skipped));
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for(auto& a : addings)
							 | 
						||
| 
								 | 
							
											result.push_back(move(a));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return result;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									void write(Command& cmd, int& index, permission::teamspeak::GroupType type, int version) {
							 | 
						||
| 
								 | 
							
										if(version == 0) {
							 | 
						||
| 
								 | 
							
											if(this->value != permNotGranted) {
							 | 
						||
| 
								 | 
							
												for(const auto& name : permission::teamspeak::unmap_key(this->type->name, type)) {
							 | 
						||
| 
								 | 
							
													cmd[index]["permid"] = name;
							 | 
						||
| 
								 | 
							
													cmd[index]["permvalue"] = this->value;
							 | 
						||
| 
								 | 
							
													cmd[index]["permskip"] = this->skipped;
							 | 
						||
| 
								 | 
							
													cmd[index]["permnegated"] = this->negated;
							 | 
						||
| 
								 | 
							
													index++;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if(this->grant != permNotGranted) {
							 | 
						||
| 
								 | 
							
												for(const auto& name : permission::teamspeak::unmap_key(this->type->grantName(), type)) {
							 | 
						||
| 
								 | 
							
													cmd[index]["permid"] = name;
							 | 
						||
| 
								 | 
							
													cmd[index]["permvalue"] = this->grant;
							 | 
						||
| 
								 | 
							
													cmd[index]["permskip"] = false;
							 | 
						||
| 
								 | 
							
													cmd[index]["permnegated"] = false;
							 | 
						||
| 
								 | 
							
													index++;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if(version >= 1) {
							 | 
						||
| 
								 | 
							
											cmd[index]["perm"] = this->type->name;
							 | 
						||
| 
								 | 
							
											cmd[index]["value"] = this->value;
							 | 
						||
| 
								 | 
							
											cmd[index]["grant"] = this->grant;
							 | 
						||
| 
								 | 
							
											cmd[index]["flag_skip"] = this->skipped;
							 | 
						||
| 
								 | 
							
											cmd[index]["flag_negated"] = this->negated;
							 | 
						||
| 
								 | 
							
											index++;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											logError(0, "Could not write snapshot permission! Invalid version. ({})", version);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								std::shared_ptr<TSServer> ServerManager::createServerFromSnapshot(shared_ptr<TSServer> old, std::string host,
							 | 
						||
| 
								 | 
							
								                                                                  uint16_t port, const ts::Command &arguments,
							 | 
						||
| 
								 | 
							
								                                                                  std::string &error) {
							 | 
						||
| 
								 | 
							
									ServerId serverId = 0;
							 | 
						||
| 
								 | 
							
									map<ClientDbId, map<ChannelId, ClientDbId>> channelGroupRelations; //cid is the new cgid
							 | 
						||
| 
								 | 
							
									map<GroupId, GroupId> channelGroupMapping;
							 | 
						||
| 
								 | 
							
									map<ClientDbId, vector<GroupId>> serverGroupRelations; //gid is the new gid
							 | 
						||
| 
								 | 
							
									map<GroupId, GroupId> serverGroupMapping;
							 | 
						||
| 
								 | 
							
									map<ClientDbId, ClientDbId> db_mapping_client_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									deque<pair<std::string, threads::Future<sql::result>>> futures;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									bool sid_success = false;
							 | 
						||
| 
								 | 
							
									serverId = this->next_available_server_id(sid_success);
							 | 
						||
| 
								 | 
							
									if(!sid_success) {
							 | 
						||
| 
								 | 
							
										error = "failed to generate new sid";
							 | 
						||
| 
								 | 
							
										return nullptr;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									ServerId log_server_id = old ? old->getServerId() : serverId;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									futures.push_back({
							 | 
						||
| 
								 | 
							
										"server id register",
							 | 
						||
| 
								 | 
							
										sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", host}, variable{":port", port}).executeLater()
							 | 
						||
| 
								 | 
							
									});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_channel(this->handle->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid)", variable{":sid", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_group_assignment(this->handle->getSql(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", variable{":sid", serverId}, variable{":until", 0});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_permission(this->handle->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)",
							 | 
						||
| 
								 | 
							
									                               variable{":serverId", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_update_permission_grant(this->handle->getSql(), "UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key", variable{":sid", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_property(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)", variable{":sid", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_bot(this->handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner)", variable{":server_id", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_playlist(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", variable{":server_id", serverId});
							 | 
						||
| 
								 | 
							
									sql::model sql_insert_playlist_song(this->handle->getSql(), "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", variable{":server_id", serverId});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int index = 0;
							 | 
						||
| 
								 | 
							
									auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0;
							 | 
						||
| 
								 | 
							
									debugMessage(0, "Got server snapshot with version {}", snapshot_version);
							 | 
						||
| 
								 | 
							
									while(true){
							 | 
						||
| 
								 | 
							
										for(auto &key : arguments[index].keys()){
							 | 
						||
| 
								 | 
							
											if(key == "end_virtualserver") continue;
							 | 
						||
| 
								 | 
							
											if(key == "begin_virtualserver") continue;
							 | 
						||
| 
								 | 
							
											if(snapshot_version == 0) {
							 | 
						||
| 
								 | 
							
												if(key == "virtualserver_download_quota" || key == "virtualserver_upload_quota" || key == "virtualserver_max_download_total_bandwidth" || key == "virtualserver_max_upload_total_bandwidth") {
							 | 
						||
| 
								 | 
							
													auto value = arguments[index][key].string();
							 | 
						||
| 
								 | 
							
													try {
							 | 
						||
| 
								 | 
							
														arguments[index][key] = to_string((int64_t) std::stoull(value));
							 | 
						||
| 
								 | 
							
													} catch(const std::exception& ex) {
							 | 
						||
| 
								 | 
							
														continue;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											debugMessage(LOG_GENERAL, PREFIX + " Having server key {} = {}", key, arguments[index][key].as<std::string>());
							 | 
						||
| 
								 | 
							
											futures.push_back({
							 | 
						||
| 
								 | 
							
												"server property import proprty=" + key,
							 | 
						||
| 
								 | 
							
												sql_insert_property.command().values(
							 | 
						||
| 
								 | 
							
														variable{":type", property::PropertyType::PROP_TYPE_SERVER},
							 | 
						||
| 
								 | 
							
														variable{":id", 0},
							 | 
						||
| 
								 | 
							
														variable{":key", key},
							 | 
						||
| 
								 | 
							
														variable{":value", arguments[index][key].string()}
							 | 
						||
| 
								 | 
							
												).executeLater()
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(arguments[index].has("end_virtualserver")) break;
							 | 
						||
| 
								 | 
							
										index++;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * sql::command(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)",
							 | 
						||
| 
								 | 
							
												                               variable{":server_id", this->ref_server()->getServerId()},
							 | 
						||
| 
								 | 
							
												                               variable{":playlist_id", playlist_id}
							 | 
						||
| 
								 | 
							
												)
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									index++;
							 | 
						||
| 
								 | 
							
									for(;index < arguments.bulkCount(); index++) {
							 | 
						||
| 
								 | 
							
										if (arguments[index].has("begin_channels")) {
							 | 
						||
| 
								 | 
							
											while (true) {
							 | 
						||
| 
								 | 
							
												if (arguments[index].has("end_channels")) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												auto channel_id = arguments[index]["channel_id"].string();
							 | 
						||
| 
								 | 
							
												futures.push_back({
							 | 
						||
| 
								 | 
							
													"channel register cid=" + channel_id,
							 | 
						||
| 
								 | 
							
													sql_insert_channel.command().values(variable{":id", channel_id}, variable{":type", -1}, variable{":pid", arguments[index]["channel_pid"].string()}).executeLater()
							 | 
						||
| 
								 | 
							
												}); //TODO findout type!
							 | 
						||
| 
								 | 
							
												for (const auto &key : arguments[index].keys()) {
							 | 
						||
| 
								 | 
							
													if(key == "channel_id") continue;
							 | 
						||
| 
								 | 
							
													if(key == "channel_pid") continue;
							 | 
						||
| 
								 | 
							
													if(key == "channel_security_salt") continue;
							 | 
						||
| 
								 | 
							
													if(key == "channel_filepath") continue; //TODO implement channel_filepath?
							 | 
						||
| 
								 | 
							
													if(key == "begin_channels") continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													debugMessage(log_server_id, PREFIX + "Having channel key " + key + "=" + arguments[index][key].as<std::string>() + " for channel " + arguments[index]["channel_id"].as<std::string>());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													futures.push_back({
							 | 
						||
| 
								 | 
							
													                  "channel prop register cid=" + channel_id + " property=" + key,
							 | 
						||
| 
								 | 
							
													                  sql_insert_property.command().values(variable{":sid", serverId},
							 | 
						||
| 
								 | 
							
													                                                       variable{":type", property::PropertyType::PROP_TYPE_CHANNEL},
							 | 
						||
| 
								 | 
							
													                                                       variable{":id", channel_id},
							 | 
						||
| 
								 | 
							
													                                                       variable{":key", key},
							 | 
						||
| 
								 | 
							
													                                                       variable{":value", arguments[index][key].as<std::string>()}
							 | 
						||
| 
								 | 
							
													                  ).executeLater()
							 | 
						||
| 
								 | 
							
								                  });
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if (arguments[index].has("begin_clients")) {
							 | 
						||
| 
								 | 
							
											while (true) {
							 | 
						||
| 
								 | 
							
												if (arguments[index].has("end_clients")) break;
							 | 
						||
| 
								 | 
							
												debugMessage(log_server_id, PREFIX + "Create client {}/{} -> end: {}",arguments[index]["client_id"].as<std::string>(), arguments[index]["client_nickname"].as<std::string>(), to_string(arguments[index].has("end_clients")));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												ClientDbId oldId = 0;
							 | 
						||
| 
								 | 
							
												auto res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0", variable{":uid", arguments[index]["client_unique_id"].as<std::string>()}).query([](ClientDbId* ptr, int, char** v, char**){ *ptr = stoll(v[0]); return 0;}, &oldId);
							 | 
						||
| 
								 | 
							
												LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
												if(oldId == 0){
							 | 
						||
| 
								 | 
							
													res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){
							 | 
						||
| 
								 | 
							
														*ptr = static_cast<ClientDbId>(stoll(values[0]));
							 | 
						||
| 
								 | 
							
														return 0;
							 | 
						||
| 
								 | 
							
													}, &oldId);
							 | 
						||
| 
								 | 
							
													LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													oldId += 1;
							 | 
						||
| 
								 | 
							
													//Create a new client
							 | 
						||
| 
								 | 
							
													sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)",
							 | 
						||
| 
								 | 
							
													             variable{":sid", 0}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as<std::string>()},
							 | 
						||
| 
								 | 
							
													             variable{":firstConnect", arguments[index]["client_created"].as<std::string>()},
							 | 
						||
| 
								 | 
							
													             variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as<std::string>()}).execute(); //TODO log error
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												db_mapping_client_id[arguments[index]["client_id"].as<ClientDbId>()] = oldId;
							 | 
						||
| 
								 | 
							
												sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)",
							 | 
						||
| 
								 | 
							
												             variable{":sid", serverId}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as<std::string>()},
							 | 
						||
| 
								 | 
							
												             variable{":firstConnect", arguments[index]["client_created"].as<std::string>()},
							 | 
						||
| 
								 | 
							
												             variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as<std::string>()}).execute(); //TODO log error
							 | 
						||
| 
								 | 
							
												//TODO here
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if (arguments[index].has("begin_permissions")) {
							 | 
						||
| 
								 | 
							
											while (true) {
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
												if (arguments[index].has("end_permissions")) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												else if (arguments[index].has("server_groups")) {
							 | 
						||
| 
								 | 
							
													GroupId currentGroupId = 1;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_groups")) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(!arguments[index].has("id")){ //new group
							 | 
						||
| 
								 | 
							
															logError(0, "Invalid server group permission entry! Missing group id!");
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														currentGroupId = static_cast<GroupId>(GroupManager::generateGroupId(this->handle->getSql()));
							 | 
						||
| 
								 | 
							
														debugMessage(log_server_id, PREFIX + "Insert group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as<string>());
							 | 
						||
| 
								 | 
							
														serverGroupMapping[arguments[index]["id"].as<GroupId>()] = currentGroupId;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
							 | 
						||
| 
								 | 
							
														                           variable{":sid", serverId},
							 | 
						||
| 
								 | 
							
														                           variable{":gid", currentGroupId},
							 | 
						||
| 
								 | 
							
														                           variable{":target", GROUPTARGET_SERVER},
							 | 
						||
| 
								 | 
							
														                           variable{":type", GroupType::GROUP_TYPE_NORMAL},
							 | 
						||
| 
								 | 
							
														                           variable{":name", arguments[index]["name"].as<string>()})
							 | 
						||
| 
								 | 
							
																              .execute());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"});
							 | 
						||
| 
								 | 
							
														for(const auto& entry : permissions) {
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"server group permission sgid=" + to_string(currentGroupId) + " permId=" + entry->type->name,
							 | 
						||
| 
								 | 
							
																sql_insert_permission.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":id", currentGroupId},
							 | 
						||
| 
								 | 
							
																		variable{":type", permission::SQL_PERM_GROUP},
							 | 
						||
| 
								 | 
							
																		variable{":chId", 0},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
																		variable{":permId", entry->type->name},
							 | 
						||
| 
								 | 
							
																		variable{":value", entry->value},
							 | 
						||
| 
								 | 
							
																		variable{":grant", entry->grant},
							 | 
						||
| 
								 | 
							
																		variable{":flag_skip", entry->skipped},
							 | 
						||
| 
								 | 
							
																		variable{":flag_negate", entry->negated}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													//Relations for sgroups after list
							 | 
						||
| 
								 | 
							
													index++;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_relations")) break;
							 | 
						||
| 
								 | 
							
														serverGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]].push_back(serverGroupMapping[arguments[index]["gid"].as<GroupId>()]);
							 | 
						||
| 
								 | 
							
														futures.push_back({
							 | 
						||
| 
								 | 
							
																"server group relation",
							 | 
						||
| 
								 | 
							
														        sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]}, variable{":gid", serverGroupMapping[arguments[index]["gid"].as<GroupId>()]}, variable{":chid", "0"}).executeLater()
							 | 
						||
| 
								 | 
							
														});
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if(arguments[index].has("channel_groups")){
							 | 
						||
| 
								 | 
							
													GroupId currentGroupId = 1;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_groups")) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(!arguments[index].has("id")){ //new group
							 | 
						||
| 
								 | 
							
															logError(0, "Invalid server group permission entry! Missing group id!");
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														currentGroupId = static_cast<GroupId>(GroupManager::generateGroupId(this->handle->getSql()));
							 | 
						||
| 
								 | 
							
														debugMessage(log_server_id, PREFIX + "Insert channel group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as<string>());
							 | 
						||
| 
								 | 
							
														channelGroupMapping[arguments[index]["id"].as<GroupId>()] = currentGroupId;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)",
							 | 
						||
| 
								 | 
							
														                           variable{":sid", serverId},
							 | 
						||
| 
								 | 
							
														                           variable{":gid", currentGroupId},
							 | 
						||
| 
								 | 
							
														                           variable{":target", GROUPTARGET_CHANNEL},
							 | 
						||
| 
								 | 
							
														                           variable{":type", GroupType::GROUP_TYPE_NORMAL},
							 | 
						||
| 
								 | 
							
														                           variable{":name", arguments[index]["name"].as<string>()})
							 | 
						||
| 
								 | 
							
																              .execute());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"});
							 | 
						||
| 
								 | 
							
														for(const auto& entry : permissions) {
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"channel group property insert cgid=" + to_string(currentGroupId) + " property=" + entry->type->name,
							 | 
						||
| 
								 | 
							
																sql_insert_permission.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":id", currentGroupId},
							 | 
						||
| 
								 | 
							
																		variable{":type", permission::SQL_PERM_GROUP},
							 | 
						||
| 
								 | 
							
																		variable{":chId", 0},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
																		variable{":permId", entry->type->name},
							 | 
						||
| 
								 | 
							
																		variable{":value", entry->value},
							 | 
						||
| 
								 | 
							
																		variable{":grant", entry->grant},
							 | 
						||
| 
								 | 
							
																		variable{":flag_skip", entry->skipped},
							 | 
						||
| 
								 | 
							
																		variable{":flag_negate", entry->negated}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													//Relations for sgroups after list
							 | 
						||
| 
								 | 
							
													ChannelId chId = 0;
							 | 
						||
| 
								 | 
							
													index++;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_relations")) break;
							 | 
						||
| 
								 | 
							
														if(arguments[index].has("iid")) chId = arguments[index]["iid"];
							 | 
						||
| 
								 | 
							
														channelGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]][chId] = channelGroupMapping[arguments[index]["gid"].as<GroupId>()];
							 | 
						||
| 
								 | 
							
														futures.push_back({
							 | 
						||
| 
								 | 
							
															"channel group relation",
							 | 
						||
| 
								 | 
							
															sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as<ClientDbId>()]}, variable{":gid", channelGroupMapping[arguments[index]["gid"].as<GroupId>()]}, variable{":chid", chId}).executeLater()
							 | 
						||
| 
								 | 
							
														});
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if (arguments[index].has("client_flat")) {
							 | 
						||
| 
								 | 
							
													ClientDbId currentId = 0;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_flat")) break;
							 | 
						||
| 
								 | 
							
														if(arguments[index].has("id1"))
							 | 
						||
| 
								 | 
							
															currentId = arguments[index]["id1"];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "end_flat"}, true);
							 | 
						||
| 
								 | 
							
														for(const auto& entry : permissions) {
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"client permission insert clientId=" + to_string(currentId) + " permId=" + entry->type->name,
							 | 
						||
| 
								 | 
							
																sql_insert_permission.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":id", currentId},
							 | 
						||
| 
								 | 
							
																		variable{":type", permission::SQL_PERM_USER},
							 | 
						||
| 
								 | 
							
																		variable{":chId", 0},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
																		variable{":permId", entry->type->name},
							 | 
						||
| 
								 | 
							
																		variable{":value", entry->value},
							 | 
						||
| 
								 | 
							
																		variable{":grant", entry->grant},
							 | 
						||
| 
								 | 
							
																		variable{":flag_skip", entry->skipped},
							 | 
						||
| 
								 | 
							
																		variable{":flag_negate", entry->negated}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if (arguments[index].has("channel_flat")) {
							 | 
						||
| 
								 | 
							
													ChannelId currentChannelId = 0;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_flat")) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("id1"))
							 | 
						||
| 
								 | 
							
															currentChannelId = arguments[index]["id1"]; //We also have id2 unknown for what this is. Maybe parent?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CHANNEL, snapshot_version, {"id1", "end_flat"}, true);
							 | 
						||
| 
								 | 
							
														for(const auto& entry : permissions) {
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"channel permission insert channelId=" + to_string(currentChannelId) + " permId=" + entry->type->name,
							 | 
						||
| 
								 | 
							
																sql_insert_permission.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":id", 0},
							 | 
						||
| 
								 | 
							
																		variable{":type", permission::SQL_PERM_CHANNEL},
							 | 
						||
| 
								 | 
							
																		variable{":chId", currentChannelId},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
																		variable{":permId", entry->type->name},
							 | 
						||
| 
								 | 
							
																		variable{":value", entry->value},
							 | 
						||
| 
								 | 
							
																		variable{":grant", entry->grant},
							 | 
						||
| 
								 | 
							
																		variable{":flag_skip", entry->skipped},
							 | 
						||
| 
								 | 
							
																		variable{":flag_negate", entry->negated}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if (arguments[index].has("channel_client_flat")) {
							 | 
						||
| 
								 | 
							
													ClientDbId currentClientId = 0;
							 | 
						||
| 
								 | 
							
													ChannelId currentChannelId = 0;
							 | 
						||
| 
								 | 
							
													while (true) {
							 | 
						||
| 
								 | 
							
														if (arguments[index].has("end_flat")) break;
							 | 
						||
| 
								 | 
							
														//id1 = channel id
							 | 
						||
| 
								 | 
							
														//id2 = client id
							 | 
						||
| 
								 | 
							
														if(arguments[index].has("id1"))
							 | 
						||
| 
								 | 
							
															currentChannelId = arguments[index]["id1"];
							 | 
						||
| 
								 | 
							
														if(arguments[index].has("id2"))
							 | 
						||
| 
								 | 
							
															currentClientId = db_mapping_client_id[arguments[index]["id2"]];
							 | 
						||
| 
								 | 
							
														if(currentChannelId > 0 && currentClientId > 0){
							 | 
						||
| 
								 | 
							
															auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "id2", "end_flat"}, true);
							 | 
						||
| 
								 | 
							
															for(const auto& entry : permissions) {
							 | 
						||
| 
								 | 
							
																futures.push_back({
							 | 
						||
| 
								 | 
							
																	"client channel permission insert",
							 | 
						||
| 
								 | 
							
																	sql_insert_permission.command().values(
							 | 
						||
| 
								 | 
							
																			variable{":id", currentClientId},
							 | 
						||
| 
								 | 
							
																			variable{":type", permission::SQL_PERM_CHANNEL},
							 | 
						||
| 
								 | 
							
																			variable{":chId", currentChannelId},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
																			variable{":permId", entry->type->name},
							 | 
						||
| 
								 | 
							
																			variable{":value", entry->value},
							 | 
						||
| 
								 | 
							
																			variable{":grant", entry->grant},
							 | 
						||
| 
								 | 
							
																			variable{":flag_skip", entry->skipped},
							 | 
						||
| 
								 | 
							
																			variable{":flag_negate", entry->negated}
							 | 
						||
| 
								 | 
							
																	).executeLater()
							 | 
						||
| 
								 | 
							
																});
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														} else index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else {
							 | 
						||
| 
								 | 
							
													error = "Invalid tag in permissions. Available: " + avArguments(arguments, index);
							 | 
						||
| 
								 | 
							
													return nullptr;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if(arguments[index].has("begin_music")) {
							 | 
						||
| 
								 | 
							
											PlaylistId playlist_index = 0;
							 | 
						||
| 
								 | 
							
											PlaylistId playlist_error = 0xFFFFFFFF;
							 | 
						||
| 
								 | 
							
											map<PlaylistId, PlaylistId> db_mapping_playlist;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											/*
							 | 
						||
| 
								 | 
							
											//gather static info (not needed because its a new server)
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												auto sql_result = sql::command(this->handle->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC", variable{":server_id", serverId}).query([&](int length, string* values, string* names){
							 | 
						||
| 
								 | 
							
													if(length != 1) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													try {
							 | 
						||
| 
								 | 
							
														playlist_index = (PlaylistId) stoll(values[0]);
							 | 
						||
| 
								 | 
							
													} catch(const std::exception& ex) {
							 | 
						||
| 
								 | 
							
														error = "Failed to parse playlist id from database. ID: " + values[0];
							 | 
						||
| 
								 | 
							
														return;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												});
							 | 
						||
| 
								 | 
							
												LOG_SQL_CMD(sql_result);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if(playlist_index == 0) {
							 | 
						||
| 
								 | 
							
													error = "failed to gather info";
							 | 
						||
| 
								 | 
							
													return nullptr;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											while(!arguments[index].has("end_music")) {
							 | 
						||
| 
								 | 
							
												if(arguments[index].has("begin_bots")) {
							 | 
						||
| 
								 | 
							
													while(!arguments[index].has("end_bots")) {
							 | 
						||
| 
								 | 
							
														ClientDbId bot_id = arguments[index]["bot_id"];
							 | 
						||
| 
								 | 
							
														if(db_mapping_client_id.find(bot_id) == db_mapping_client_id.end()) {
							 | 
						||
| 
								 | 
							
															logError(log_server_id, PREFIX + "Failed to insert music bot within id {}. (Mapping not found)", bot_id);
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														ClientDbId new_bot_id = db_mapping_client_id[bot_id];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														//begin_bots
							 | 
						||
| 
								 | 
							
														auto sql_result = sql_insert_bot.command().values(
							 | 
						||
| 
								 | 
							
																variable{":bot_id", new_bot_id},
							 | 
						||
| 
								 | 
							
																variable{":unique_id", arguments[index]["bot_unique_id"].string()},
							 | 
						||
| 
								 | 
							
																variable{":owner", arguments[index]["bot_owner_id"].string()}
							 | 
						||
| 
								 | 
							
														).execute();
							 | 
						||
| 
								 | 
							
														if(!sql_result) {
							 | 
						||
| 
								 | 
							
															logError(log_server_id, PREFIX + "Failed to insert music bot with id {} (old: {}). ({})", new_bot_id, bot_id, sql_result.fmtStr());
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														for(const auto& key : arguments[index].keys()) {
							 | 
						||
| 
								 | 
							
															if(key == "begin_music") continue;
							 | 
						||
| 
								 | 
							
															if(key == "begin_bots") continue;
							 | 
						||
| 
								 | 
							
															if(key == "bot_unique_id") continue;
							 | 
						||
| 
								 | 
							
															if(key == "bot_owner_id") continue;
							 | 
						||
| 
								 | 
							
															if(key == "bot_id") continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
															const auto& property = property::info<property::ClientProperties>(key);
							 | 
						||
| 
								 | 
							
															if(property->property_index == property::CLIENT_UNDEFINED) {
							 | 
						||
| 
								 | 
							
																debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string());
							 | 
						||
| 
								 | 
							
																continue;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"music bot insert",
							 | 
						||
| 
								 | 
							
																sql_insert_property.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":type", property::PropertyType::PROP_TYPE_CLIENT},
							 | 
						||
| 
								 | 
							
																		variable{":id", new_bot_id},
							 | 
						||
| 
								 | 
							
																		variable{":key", key},
							 | 
						||
| 
								 | 
							
																		variable{":value", arguments[index][key].string()}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if(arguments[index].has("begin_playlist")) {
							 | 
						||
| 
								 | 
							
													while(!arguments[index].has("end_playlist")) {
							 | 
						||
| 
								 | 
							
														PlaylistId playlist_id = arguments[index]["playlist_id"];
							 | 
						||
| 
								 | 
							
														db_mapping_playlist[++playlist_index] = playlist_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto sql_result = sql_insert_playlist.command().values(variable{":playlist_id", playlist_index}).execute();
							 | 
						||
| 
								 | 
							
														if(!sql_result) {
							 | 
						||
| 
								 | 
							
															db_mapping_playlist[playlist_index] = playlist_error;
							 | 
						||
| 
								 | 
							
															logError(log_server_id, PREFIX + "Failed to insert playlist with id {} (old: {}). ({})", playlist_index, playlist_id, sql_result.fmtStr());
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														for(const auto& key : arguments[index].keys()) {
							 | 
						||
| 
								 | 
							
															if(key == "begin_music") continue;
							 | 
						||
| 
								 | 
							
															if(key == "begin_playlist") continue;
							 | 
						||
| 
								 | 
							
															if(key == "playlist_id") continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
															const auto& property = property::info<property::ClientProperties>(key);
							 | 
						||
| 
								 | 
							
															if(property->property_index == property::CLIENT_UNDEFINED) {
							 | 
						||
| 
								 | 
							
																debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string());
							 | 
						||
| 
								 | 
							
																continue;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
															futures.push_back({
							 | 
						||
| 
								 | 
							
																"playlist insert",
							 | 
						||
| 
								 | 
							
																sql_insert_property.command().values(
							 | 
						||
| 
								 | 
							
																		variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST},
							 | 
						||
| 
								 | 
							
																		variable{":id", playlist_index},
							 | 
						||
| 
								 | 
							
																		variable{":key", key},
							 | 
						||
| 
								 | 
							
																		variable{":value", arguments[index][key].string()}
							 | 
						||
| 
								 | 
							
																).executeLater()
							 | 
						||
| 
								 | 
							
															});
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												} else if(arguments[index].has("begin_playlist_songs")) {
							 | 
						||
| 
								 | 
							
													PlaylistId current_playlist = 0;
							 | 
						||
| 
								 | 
							
													PlaylistId current_playlist_mapped = 0;
							 | 
						||
| 
								 | 
							
													while(!arguments[index].has("end_playlist_songs")) {
							 | 
						||
| 
								 | 
							
														if(arguments[index].has("song_playlist_id")) {
							 | 
						||
| 
								 | 
							
															current_playlist = arguments[index]["song_playlist_id"];
							 | 
						||
| 
								 | 
							
															current_playlist_mapped = (PlaylistId) db_mapping_client_id[current_playlist];
							 | 
						||
| 
								 | 
							
															if(current_playlist_mapped == 0) {
							 | 
						||
| 
								 | 
							
																logError(log_server_id, PREFIX + "Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped", arguments[index]["song_id"].value(), current_playlist);
							 | 
						||
| 
								 | 
							
																current_playlist_mapped = playlist_error;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(current_playlist_mapped == playlist_error) { /* current list is an error */
							 | 
						||
| 
								 | 
							
															index++;
							 | 
						||
| 
								 | 
							
															continue;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														auto sql_future = sql_insert_playlist_song.command().values(
							 | 
						||
| 
								 | 
							
																variable{":playlist_id", current_playlist_mapped},
							 | 
						||
| 
								 | 
							
																variable{":song_id", arguments[index]["song_id"].value()},
							 | 
						||
| 
								 | 
							
																variable{":order_id", arguments[index]["song_order"].value()},
							 | 
						||
| 
								 | 
							
																variable{":invoker_dbid", arguments[index]["song_invoker"].value()},
							 | 
						||
| 
								 | 
							
																variable{":url", arguments[index]["song_url"].value()},
							 | 
						||
| 
								 | 
							
																variable{":url_loader", arguments[index]["song_url_loader"].value()},
							 | 
						||
| 
								 | 
							
																variable{":loaded", arguments[index]["song_loaded"].value()},
							 | 
						||
| 
								 | 
							
																variable{":metadata", arguments[index]["song_metadata"].value()}
							 | 
						||
| 
								 | 
							
														).executeLater();
							 | 
						||
| 
								 | 
							
														futures.push_back({
							 | 
						||
| 
								 | 
							
															"song insert",
							 | 
						||
| 
								 | 
							
															sql_future
							 | 
						||
| 
								 | 
							
														});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														index++;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											error = "Invalid root tag. Available: " + avArguments(arguments, index);
							 | 
						||
| 
								 | 
							
											return nullptr;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									debugMessage("Wait for success!");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for(const auto& future : futures) {
							 | 
						||
| 
								 | 
							
										auto result = future.second.waitAndGet({-1, "timeout"}, chrono::system_clock::now() + chrono::seconds(5));
							 | 
						||
| 
								 | 
							
										if(!result) {
							 | 
						||
| 
								 | 
							
											logError(serverId, "Failed to execute snapshotdeploy command {}: {}", future.first, result.fmtStr());
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if(old)
							 | 
						||
| 
								 | 
							
										this->deleteServer(old);
							 | 
						||
| 
								 | 
							
									//Now we load the server
							 | 
						||
| 
								 | 
							
									auto server = make_shared<TSServer>(serverId, this->handle->getSql());
							 | 
						||
| 
								 | 
							
									server->self = server;
							 | 
						||
| 
								 | 
							
									if(!server->initialize(false)) {
							 | 
						||
| 
								 | 
							
										//FIXME Error handling!
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_HOST] = host;
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_PORT] = port;
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										threads::MutexLock l(this->instanceLock);
							 | 
						||
| 
								 | 
							
										this->instances.push_back(server);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									this->adjust_executor_threads();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = serverGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as<GroupId>()];
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as<GroupId>()];
							 | 
						||
| 
								 | 
							
									server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as<GroupId>()];
							 | 
						||
| 
								 | 
							
									server->ensureValidDefaultGroups();
							 | 
						||
| 
								 | 
							
									return server;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct CommandTuple {
							 | 
						||
| 
								 | 
							
									Command& cmd;
							 | 
						||
| 
								 | 
							
									int& index;
							 | 
						||
| 
								 | 
							
									int version;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct PermissionCommandTuple {
							 | 
						||
| 
								 | 
							
									Command& cmd;
							 | 
						||
| 
								 | 
							
									int& index;
							 | 
						||
| 
								 | 
							
									int version;
							 | 
						||
| 
								 | 
							
									ClientDbId client;
							 | 
						||
| 
								 | 
							
									ChannelId channel;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inline bool writePermissions(const shared_ptr<permission::v2::PermissionManager>& manager, Command& cmd, int& index, int version, permission::teamspeak::GroupType type, std::string& error) {
							 | 
						||
| 
								 | 
							
									for(const auto& permission_container : manager->permissions()) {
							 | 
						||
| 
								 | 
							
										auto permission = get<1>(permission_container);
							 | 
						||
| 
								 | 
							
										SnapshotPermissionEntry{
							 | 
						||
| 
								 | 
							
												permission::resolvePermissionData(get<0>(permission_container)),
							 | 
						||
| 
								 | 
							
												permission.flags.value_set ? permission.values.value : permNotGranted,
							 | 
						||
| 
								 | 
							
												permission.flags.grant_set ? permission.values.grant : permNotGranted,
							 | 
						||
| 
								 | 
							
												permission.flags.negate,
							 | 
						||
| 
								 | 
							
												permission.flags.skip
							 | 
						||
| 
								 | 
							
										}.write(cmd, index, type, version);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inline void writeRelations(const shared_ptr<TSServer>& server, GroupTarget type, Command& cmd, int& index, int version) {
							 | 
						||
| 
								 | 
							
									PermissionCommandTuple parm{cmd, index, version, 0, 0};
							 | 
						||
| 
								 | 
							
									auto res = sql::command(server->getSql(), "SELECT `cldbid`, `groups`.`groupId`, `channelId`, `until` FROM `assignedGroups` INNER JOIN `groups` ON `groups`.`serverId` = `assignedGroups`.`serverId` AND `groups`.`groupId` = `assignedGroups`.`groupId` WHERE `groups`.`serverId` = :sid AND `groups`.target = :type",
							 | 
						||
| 
								 | 
							
									                        variable{":sid", server->getServerId()},
							 | 
						||
| 
								 | 
							
									                        variable{":type", type}
							 | 
						||
| 
								 | 
							
									).query([](PermissionCommandTuple* commandIndex, int length, char** value, char** name) {
							 | 
						||
| 
								 | 
							
										ClientDbId cldbid = 0;
							 | 
						||
| 
								 | 
							
										ChannelId channelId = 0;
							 | 
						||
| 
								 | 
							
										GroupId gid = 0;
							 | 
						||
| 
								 | 
							
										int64_t until = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for(int idx = 0; idx < length; idx++) {
							 | 
						||
| 
								 | 
							
											try {
							 | 
						||
| 
								 | 
							
												if(strcmp(name[idx], "cldbid") == 0)
							 | 
						||
| 
								 | 
							
													cldbid = stoul(value[idx]);
							 | 
						||
| 
								 | 
							
												else if(strcmp(name[idx], "groupId") == 0)
							 | 
						||
| 
								 | 
							
													gid = stoul(value[idx]);
							 | 
						||
| 
								 | 
							
												else if(strcmp(name[idx], "channelId") == 0)
							 | 
						||
| 
								 | 
							
													channelId = stoul(value[idx]);
							 | 
						||
| 
								 | 
							
												else if(strcmp(name[idx], "until") == 0)
							 | 
						||
| 
								 | 
							
													until = stoll(value[idx]);
							 | 
						||
| 
								 | 
							
											} catch (std::exception& ex) {
							 | 
						||
| 
								 | 
							
												logError(0, "Failed to write snapshot group relation (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]);
							 | 
						||
| 
								 | 
							
												return 0;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if(commandIndex->channel != channelId) {
							 | 
						||
| 
								 | 
							
											commandIndex->channel = channelId;
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["iid"] = channelId;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										commandIndex->cmd[commandIndex->index]["gid"] = gid;
							 | 
						||
| 
								 | 
							
										commandIndex->cmd[commandIndex->index]["until"] = until;
							 | 
						||
| 
								 | 
							
										commandIndex->cmd[commandIndex->index++]["cldbid"] = cldbid;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return 0;
							 | 
						||
| 
								 | 
							
									}, &parm);
							 | 
						||
| 
								 | 
							
									LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct DatabaseMusicbot {
							 | 
						||
| 
								 | 
							
									ClientDbId bot_id;
							 | 
						||
| 
								 | 
							
									ClientDbId bot_owner_id;
							 | 
						||
| 
								 | 
							
									std::string bot_unique_id;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool ServerManager::createServerSnapshot(Command &cmd, shared_ptr<TSServer> server, int version, std::string &error) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int index = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if(version == -1) version = 2; //Auto versioned
							 | 
						||
| 
								 | 
							
									if(version < 0 || version > 2) {
							 | 
						||
| 
								 | 
							
										error = "Invalid snapshot version!";
							 | 
						||
| 
								 | 
							
										return false;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if(version > 0) cmd[index++]["snapshot_version"] = version;
							 | 
						||
| 
								 | 
							
									//Server
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										cmd[index]["begin_virtualserver"] = "";
							 | 
						||
| 
								 | 
							
										for(const auto& serverProperty : server->properties().list_properties(property::FLAG_SNAPSHOT)) {
							 | 
						||
| 
								 | 
							
											if(version == 0) {
							 | 
						||
| 
								 | 
							
												switch (serverProperty.type().property_index) {
							 | 
						||
| 
								 | 
							
													case property::VIRTUALSERVER_DOWNLOAD_QUOTA:
							 | 
						||
| 
								 | 
							
													case property::VIRTUALSERVER_UPLOAD_QUOTA:
							 | 
						||
| 
								 | 
							
													case property::VIRTUALSERVER_MAX_DOWNLOAD_TOTAL_BANDWIDTH:
							 | 
						||
| 
								 | 
							
													case property::VIRTUALSERVER_MAX_UPLOAD_TOTAL_BANDWIDTH:
							 | 
						||
| 
								 | 
							
														cmd[index][serverProperty.type().name] = (uint64_t) serverProperty.as_save<int64_t>();
							 | 
						||
| 
								 | 
							
													default:
							 | 
						||
| 
								 | 
							
														break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											cmd[index][serverProperty.type().name] = serverProperty.value();
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										cmd[index++]["end_virtualserver"] = "";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//Channels
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										cmd[index]["begin_channels"] = "";
							 | 
						||
| 
								 | 
							
										for(const auto& channel : server->getChannelTree()->channels()) {
							 | 
						||
| 
								 | 
							
											for(const auto& channelProperty : channel->properties().list_properties(property::FLAG_SNAPSHOT)) {
							 | 
						||
| 
								 | 
							
												if(channelProperty.type() == property::CHANNEL_ID)
							 | 
						||
| 
								 | 
							
													cmd[index]["channel_id"] = channelProperty.as<string>();
							 | 
						||
| 
								 | 
							
												else if(channelProperty.type() == property::CHANNEL_PID)
							 | 
						||
| 
								 | 
							
													cmd[index]["channel_pid"] = channelProperty.as<string>();
							 | 
						||
| 
								 | 
							
												else
							 | 
						||
| 
								 | 
							
													cmd[index][channelProperty.type().name] = channelProperty.as<string>();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											index++;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										cmd[index++]["end_channels"] = "";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//Clients
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										cmd[index]["begin_clients"] = "";
							 | 
						||
| 
								 | 
							
										CommandTuple parm{cmd, index, version};
							 | 
						||
| 
								 | 
							
										auto res = sql::command(server->getSql(), "SELECT `cldbid`, `clientUid`, `firstConnect`, `lastName` FROM `clients` WHERE `serverId` = :sid",
							 | 
						||
| 
								 | 
							
										                        variable{":sid", server->getServerId()}
							 | 
						||
| 
								 | 
							
										).query([&](CommandTuple* commandIndex, int length, char** value, char** name) {
							 | 
						||
| 
								 | 
							
											ClientDbId cldbid = 0;
							 | 
						||
| 
								 | 
							
											int64_t clientCreated = 0;
							 | 
						||
| 
								 | 
							
											string clientUid;
							 | 
						||
| 
								 | 
							
											string lastName;
							 | 
						||
| 
								 | 
							
											string description; //TODO description
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for(int idx = 0; idx < length; idx++) {
							 | 
						||
| 
								 | 
							
												try {
							 | 
						||
| 
								 | 
							
													if(strcmp(name[idx], "cldbid") == 0)
							 | 
						||
| 
								 | 
							
														cldbid = value[idx] && strlen(value[idx]) > 0 ? stoul(value[idx]) : 0;
							 | 
						||
| 
								 | 
							
													else if(strcmp(name[idx], "clientUid") == 0)
							 | 
						||
| 
								 | 
							
														clientUid = value[idx];
							 | 
						||
| 
								 | 
							
													else if(strcmp(name[idx], "firstConnect") == 0)
							 | 
						||
| 
								 | 
							
														clientCreated = value[idx] && strlen(value[idx]) > 0 ? stoll(value[idx]) : 0L;
							 | 
						||
| 
								 | 
							
													else if(strcmp(name[idx], "lastName") == 0)
							 | 
						||
| 
								 | 
							
														lastName = value[idx];
							 | 
						||
| 
								 | 
							
												} catch (std::exception& ex) {
							 | 
						||
| 
								 | 
							
													logError(0, "Failed to write snapshot client (Skipping it)! Message: {} @ {} => {}", ex.what(), name[idx], value[idx]);
							 | 
						||
| 
								 | 
							
													return 0;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["client_id"] = cldbid;
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["client_unique_id"] = clientUid;
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["client_nickname"] = lastName;
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["client_created"] = clientCreated;
							 | 
						||
| 
								 | 
							
											commandIndex->cmd[commandIndex->index]["client_description"] = description;
							 | 
						||
| 
								 | 
							
											if(commandIndex->version == 0)
							 | 
						||
| 
								 | 
							
												commandIndex->cmd[commandIndex->index]["client_unread_messages"] = 0;
							 | 
						||
| 
								 | 
							
											commandIndex->index++;
							 | 
						||
| 
								 | 
							
											return 0;
							 | 
						||
| 
								 | 
							
										}, &parm);
							 | 
						||
| 
								 | 
							
										LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
										cmd[index++]["end_clients"] = "";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//music and music bots
							 | 
						||
| 
								 | 
							
									if(version >= 2) {
							 | 
						||
| 
								 | 
							
										cmd[index]["begin_music"] = "";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* music bots */
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["begin_bots"] = "";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											deque<DatabaseMusicbot> music_bots;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											auto sql_result = sql::command(server->getSql(), "SELECT `botId`, `uniqueId`, `owner` FROM `musicbots` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) {
							 | 
						||
| 
								 | 
							
												DatabaseMusicbot data;
							 | 
						||
| 
								 | 
							
												for(int column = 0; column < length; column++) {
							 | 
						||
| 
								 | 
							
													try {
							 | 
						||
| 
								 | 
							
														if(names[column] == "botId")
							 | 
						||
| 
								 | 
							
															data.bot_id = stoll(values[column]);
							 | 
						||
| 
								 | 
							
														else if(names[column] == "uniqueId")
							 | 
						||
| 
								 | 
							
															data.bot_unique_id = values[column];
							 | 
						||
| 
								 | 
							
														else if(names[column] == "owner")
							 | 
						||
| 
								 | 
							
															data.bot_owner_id = stoll(values[column]);
							 | 
						||
| 
								 | 
							
													} catch(std::exception& ex) {
							 | 
						||
| 
								 | 
							
														return;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												music_bots.emplace_back(data);
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
											if(!sql_result)
							 | 
						||
| 
								 | 
							
												logError(server->getServerId(), PREFIX + "Failed to write music bots to snapshot. {}", sql_result.fmtStr());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for(const auto& music_bot : music_bots) {
							 | 
						||
| 
								 | 
							
												auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_CLIENT, music_bot.bot_id);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												cmd[index]["bot_unique_id"] = music_bot.bot_unique_id;
							 | 
						||
| 
								 | 
							
												cmd[index]["bot_owner_id"] = music_bot.bot_owner_id;
							 | 
						||
| 
								 | 
							
												cmd[index]["bot_id"] = music_bot.bot_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for(const auto& property : properties) {
							 | 
						||
| 
								 | 
							
													if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue;
							 | 
						||
| 
								 | 
							
													if(property->value == property->type->default_value) continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													cmd[index][property->type->name] = property->value;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_bots"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* playlists */
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["begin_playlist"] = "";
							 | 
						||
| 
								 | 
							
											deque<PlaylistId> playlist_ids;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											auto sql_result = sql::command(server->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :sid", variable{":sid", server->getServerId()}).query([&](int length, string* values, string* names) {
							 | 
						||
| 
								 | 
							
												try {
							 | 
						||
| 
								 | 
							
													playlist_ids.push_back(stoll(values[0]));
							 | 
						||
| 
								 | 
							
												} catch(std::exception& ex) {
							 | 
						||
| 
								 | 
							
													return;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
											if(!sql_result)
							 | 
						||
| 
								 | 
							
												logError(server->getServerId(), PREFIX + "Failed to write playlists to snapshot. {}", sql_result.fmtStr());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											for(const auto& playlist : playlist_ids) {
							 | 
						||
| 
								 | 
							
												auto properties = serverInstance->databaseHelper()->query_properties(server->getServerId(), property::PROP_TYPE_PLAYLIST, playlist);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												cmd[index]["playlist_id"] = playlist;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for(const auto& property : properties) {
							 | 
						||
| 
								 | 
							
													if((property->type->flags & (property::FLAG_SAVE_MUSIC | property::FLAG_SAVE)) == 0) continue;
							 | 
						||
| 
								 | 
							
													if(property->value == property->type->default_value) continue;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													cmd[index][property->type->name] = property->value;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_playlist"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* playlist info */
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["begin_playlist_songs"] = "";
							 | 
						||
| 
								 | 
							
											//playlist_songs => `serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT, `loaded` BOOL, `metadata` TEXT
							 | 
						||
| 
								 | 
							
											PlaylistId current_playlist = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											auto sql_result = sql::command(server->getSql(),
							 | 
						||
| 
								 | 
							
													"SELECT `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata` FROM `playlist_songs` WHERE `serverId` = :sid ORDER BY `playlist_id`", variable{":sid", server->getServerId()}
							 | 
						||
| 
								 | 
							
											).query([&](int length, string* values, string* names) {
							 | 
						||
| 
								 | 
							
												for(int column = 0; column < length; column++) {
							 | 
						||
| 
								 | 
							
													if(names[column] == "song_id")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_id"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "order_id")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_order"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "invoker_dbid")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_invoker"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "url")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_url"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "url_loader")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_url_loader"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "loaded")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_loaded"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "metadata")
							 | 
						||
| 
								 | 
							
														cmd[index]["song_metadata"] = values[column];
							 | 
						||
| 
								 | 
							
													else if(names[column] == "playlist_id") {
							 | 
						||
| 
								 | 
							
														try {
							 | 
						||
| 
								 | 
							
															auto playlist_id = stoll(values[column]);
							 | 
						||
| 
								 | 
							
															if(current_playlist != playlist_id) {
							 | 
						||
| 
								 | 
							
																cmd[index]["song_playlist_id"] = values[column]; /* song_playlist_id will be only set if the playlist id had changed */
							 | 
						||
| 
								 | 
							
																current_playlist = playlist_id;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														} catch(std::exception& ex) {
							 | 
						||
| 
								 | 
							
															logError(server->getServerId(), PREFIX + "Failed to parse playlist id. value: {}, message: {}", values[column], ex.what());
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												index++;
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
											if(!sql_result)
							 | 
						||
| 
								 | 
							
												logError(server->getServerId(), PREFIX + "Failed to write playlist songs to snapshot. {}", sql_result.fmtStr());
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_playlist_songs"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cmd[index++]["end_music"] = "";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//Permissions
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										cmd[index++]["begin_permissions"] = "";
							 | 
						||
| 
								 | 
							
										//Server groups
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											//List groups
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												cmd[index]["server_groups"] = "";
							 | 
						||
| 
								 | 
							
												for(const auto& group : server->getGroupManager()->availableServerGroups(false)) {
							 | 
						||
| 
								 | 
							
													cmd[index]["id"] = group->groupId();
							 | 
						||
| 
								 | 
							
													cmd[index]["name"] = group->name();
							 | 
						||
| 
								 | 
							
													if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
							 | 
						||
| 
								 | 
							
													cmd[index++]["end_group"] = "";
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												cmd[index++]["end_groups"] = "";
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											//List relations
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												cmd[index]["begin_relations"] = "";
							 | 
						||
| 
								 | 
							
												writeRelations(server, GROUPTARGET_SERVER, cmd, index, version);
							 | 
						||
| 
								 | 
							
												cmd[index++]["end_relations"] = "";
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//Channel groups
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											//List groups
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												cmd[index]["channel_groups"] = "";
							 | 
						||
| 
								 | 
							
												for(const auto& group : server->getGroupManager()->availableChannelGroups(false)) {
							 | 
						||
| 
								 | 
							
													cmd[index]["id"] = group->groupId();
							 | 
						||
| 
								 | 
							
													cmd[index]["name"] = group->name();
							 | 
						||
| 
								 | 
							
													if(!writePermissions(group->permissions(), cmd, index, version, permission::teamspeak::SERVER, error)) break;
							 | 
						||
| 
								 | 
							
													cmd[index++]["end_group"] = "";
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												cmd[index++]["end_groups"] = "";
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											//List relations
							 | 
						||
| 
								 | 
							
											{
							 | 
						||
| 
								 | 
							
												cmd[index]["begin_relations"] = "";
							 | 
						||
| 
								 | 
							
												writeRelations(server, GROUPTARGET_CHANNEL, cmd, index, version);
							 | 
						||
| 
								 | 
							
												cmd[index++]["end_relations"] = "";
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//Client rights
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["client_flat"] = "";
							 | 
						||
| 
								 | 
							
											PermissionCommandTuple parm{cmd, index, version, 0, 0};
							 | 
						||
| 
								 | 
							
											auto res = sql::command(server->getSql(), "SELECT `id`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` = 0 ORDER BY `id`",
							 | 
						||
| 
								 | 
							
											                        variable{":sid", server->getServerId()},
							 | 
						||
| 
								 | 
							
											                        variable{":type", permission::SQL_PERM_USER}
							 | 
						||
| 
								 | 
							
											).query([&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){
							 | 
						||
| 
								 | 
							
												auto type = permission::resolvePermissionData(permission::unknown);
							 | 
						||
| 
								 | 
							
												permission::PermissionValue value = 0, grant = 0;
							 | 
						||
| 
								 | 
							
												bool skipped = false, negated = false;
							 | 
						||
| 
								 | 
							
												ClientDbId cldbid = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												for(int idx = 0; idx < length; idx++) {
							 | 
						||
| 
								 | 
							
													try {
							 | 
						||
| 
								 | 
							
														if(strcmp(names[idx], "id") == 0)
							 | 
						||
| 
								 | 
							
															cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0");
							 | 
						||
| 
								 | 
							
														else if(strcmp(names[idx], "value") == 0)
							 | 
						||
| 
								 | 
							
															value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
														else if(strcmp(names[idx], "grant") == 0)
							 | 
						||
| 
								 | 
							
															grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
														else if(strcmp(names[idx], "permId") == 0) {
							 | 
						||
| 
								 | 
							
															type = permission::resolvePermissionData(values[idx]);
							 | 
						||
| 
								 | 
							
															if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
																logError(0, "Could not parse client permission for snapshot (Invalid type {})! Skipping it!", values[idx]);
							 | 
						||
| 
								 | 
							
																return 0;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														else if(strcmp(names[idx], "flag_skip") == 0)
							 | 
						||
| 
								 | 
							
															skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
														else if(strcmp(names[idx], "flag_negate") == 0)
							 | 
						||
| 
								 | 
							
															negated = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
													} catch (std::exception& ex) {
							 | 
						||
| 
								 | 
							
														logError(0, "Failed to write snapshot client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]);
							 | 
						||
| 
								 | 
							
														return 0;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
													logError(0, "Could not parse client permission for snapshot (Missing type)! Skipping it!");
							 | 
						||
| 
								 | 
							
													return 0;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												if(cldbid == 0) {
							 | 
						||
| 
								 | 
							
													logError(0, "Could not parse client permission for snapshot (Missing cldbid)! Skipping it!");
							 | 
						||
| 
								 | 
							
													return 0;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if(commandIndex->client != cldbid) {
							 | 
						||
| 
								 | 
							
													commandIndex->client = cldbid;
							 | 
						||
| 
								 | 
							
													commandIndex->cmd[commandIndex->index]["id1"] = cldbid;
							 | 
						||
| 
								 | 
							
													commandIndex->cmd[commandIndex->index]["id2"] = 0;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version);
							 | 
						||
| 
								 | 
							
												return 0;
							 | 
						||
| 
								 | 
							
											}, &parm);
							 | 
						||
| 
								 | 
							
											LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_flat"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//Channel rights
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["channel_flat"] = "";
							 | 
						||
| 
								 | 
							
											PermissionCommandTuple parm{cmd, index, version, 0, 0};
							 | 
						||
| 
								 | 
							
											auto res = sql::command(server->getSql(), "SELECT `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type",
							 | 
						||
| 
								 | 
							
											                        variable{":sid", server->getServerId()},
							 | 
						||
| 
								 | 
							
											                        variable{":type", permission::SQL_PERM_CHANNEL}).query(
							 | 
						||
| 
								 | 
							
													[&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){
							 | 
						||
| 
								 | 
							
														auto type = permission::resolvePermissionData(permission::unknown);
							 | 
						||
| 
								 | 
							
														permission::PermissionValue value = 0, grant = 0;
							 | 
						||
| 
								 | 
							
														bool skipped = false, negated = false;
							 | 
						||
| 
								 | 
							
														ChannelId chid = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														for(int idx = 0; idx < length; idx++) {
							 | 
						||
| 
								 | 
							
															try {
							 | 
						||
| 
								 | 
							
																if(strcmp(names[idx], "channelId") == 0)
							 | 
						||
| 
								 | 
							
																	chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0");
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "value") == 0)
							 | 
						||
| 
								 | 
							
																	value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "grant") == 0)
							 | 
						||
| 
								 | 
							
																	grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "permId") == 0) {
							 | 
						||
| 
								 | 
							
																	type = permission::resolvePermissionData(values[idx]);
							 | 
						||
| 
								 | 
							
																	if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
																		logError(0, "Could not parse channel permission for snapshot (Invalid type {})! Skipping it!", values[idx]);
							 | 
						||
| 
								 | 
							
																		return 0;
							 | 
						||
| 
								 | 
							
																	}
							 | 
						||
| 
								 | 
							
																}
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "flag_skip") == 0)
							 | 
						||
| 
								 | 
							
																	skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "flag_negate") == 0)
							 | 
						||
| 
								 | 
							
																	negated = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
															} catch (std::exception& ex) {
							 | 
						||
| 
								 | 
							
																logError(0, "Failed to write snapshot channel permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]);
							 | 
						||
| 
								 | 
							
																return 0;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
															logError(0, "Could not parse channel permission for snapshot (Missing type)! Skipping it!");
							 | 
						||
| 
								 | 
							
															return 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(chid == 0) {
							 | 
						||
| 
								 | 
							
															logError(0, "Could not parse channel permission for snapshot (Missing channel id)! Skipping it!");
							 | 
						||
| 
								 | 
							
															return 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(commandIndex->channel != chid) {
							 | 
						||
| 
								 | 
							
															commandIndex->channel = chid;
							 | 
						||
| 
								 | 
							
															commandIndex->cmd[commandIndex->index]["id1"] = chid;
							 | 
						||
| 
								 | 
							
															commandIndex->cmd[commandIndex->index]["id2"] = 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CHANNEL, commandIndex->version);
							 | 
						||
| 
								 | 
							
														return 0;
							 | 
						||
| 
								 | 
							
													}, &parm);
							 | 
						||
| 
								 | 
							
											LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_flat"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//Client channel rights
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											cmd[index]["channel_client_flat"] = "";
							 | 
						||
| 
								 | 
							
											PermissionCommandTuple parm{cmd, index, version, 0, 0};
							 | 
						||
| 
								 | 
							
											auto res = sql::command(server->getSql(), "SELECT `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND `type` = :type AND `channelId` != 0",
							 | 
						||
| 
								 | 
							
											                        variable{":sid", server->getServerId()},
							 | 
						||
| 
								 | 
							
											                        variable{":type", permission::SQL_PERM_USER}).query(
							 | 
						||
| 
								 | 
							
													[&](PermissionCommandTuple* commandIndex, int length, char** values, char**names){
							 | 
						||
| 
								 | 
							
														auto type = permission::resolvePermissionData(permission::unknown);
							 | 
						||
| 
								 | 
							
														permission::PermissionValue value = 0, grant = 0;
							 | 
						||
| 
								 | 
							
														bool skipped = false, negated = false;
							 | 
						||
| 
								 | 
							
														ChannelId chid = 0;
							 | 
						||
| 
								 | 
							
														ClientDbId cldbid = 0;
							 | 
						||
| 
								 | 
							
														for(int idx = 0; idx < length; idx++) {
							 | 
						||
| 
								 | 
							
															try {
							 | 
						||
| 
								 | 
							
																if(strcmp(names[idx], "channelId") == 0)
							 | 
						||
| 
								 | 
							
																	chid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0");
							 | 
						||
| 
								 | 
							
																if(strcmp(names[idx], "id") == 0)
							 | 
						||
| 
								 | 
							
																	cldbid = stoul(values[idx] && strlen(values[idx]) > 0 ? values[idx] : "0");
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "value") == 0)
							 | 
						||
| 
								 | 
							
																	value = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "grant") == 0)
							 | 
						||
| 
								 | 
							
																	grant = values[idx] && strlen(values[idx]) > 0 ? stoi(values[idx]) : permNotGranted;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "permId") == 0) {
							 | 
						||
| 
								 | 
							
																	type = permission::resolvePermissionData(values[idx]);
							 | 
						||
| 
								 | 
							
																	if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
																		logError(0, "Could not parse channel client permission for snapshot (Invalid type {})! Skipping it!", values[idx]);
							 | 
						||
| 
								 | 
							
																		return 0;
							 | 
						||
| 
								 | 
							
																	}
							 | 
						||
| 
								 | 
							
																}
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "flag_skip") == 0)
							 | 
						||
| 
								 | 
							
																	skipped = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
																else if(strcmp(names[idx], "flag_negate") == 0)
							 | 
						||
| 
								 | 
							
																	negated = values[idx] ? strcmp(values[idx], "1") == 0 : false;
							 | 
						||
| 
								 | 
							
															} catch (std::exception& ex) {
							 | 
						||
| 
								 | 
							
																logError(0, "Failed to write snapshot channel client permission (Skipping it)! Message: {} @ {} => {}", ex.what(), names[idx], values[idx]);
							 | 
						||
| 
								 | 
							
																return 0;
							 | 
						||
| 
								 | 
							
															}
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(type->type == permission::unknown) {
							 | 
						||
| 
								 | 
							
															logError(0, "Could not parse channel client permission for snapshot (Missing type)! Skipping it!");
							 | 
						||
| 
								 | 
							
															return 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(chid == 0) {
							 | 
						||
| 
								 | 
							
															logError(0, "Could not parse channel client permission for snapshot (Missing channel id)! Skipping it!");
							 | 
						||
| 
								 | 
							
															return 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(cldbid == 0) {
							 | 
						||
| 
								 | 
							
															logError(0, "Could not parse channel client permission for snapshot (Missing cldbid)! Skipping it!");
							 | 
						||
| 
								 | 
							
															return 0;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														if(value == permNotGranted && grant == permNotGranted && !skipped && !negated) return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
														if(commandIndex->channel != chid || commandIndex->client != cldbid) {
							 | 
						||
| 
								 | 
							
															commandIndex->channel = chid;
							 | 
						||
| 
								 | 
							
															commandIndex->client = cldbid;
							 | 
						||
| 
								 | 
							
															commandIndex->cmd[commandIndex->index]["id1"] = chid;
							 | 
						||
| 
								 | 
							
															commandIndex->cmd[commandIndex->index]["id2"] = cldbid;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
														SnapshotPermissionEntry{type, value, grant, negated, skipped}.write(commandIndex->cmd, commandIndex->index, permission::teamspeak::CLIENT, commandIndex->version);
							 | 
						||
| 
								 | 
							
														return 0;
							 | 
						||
| 
								 | 
							
													}, &parm);
							 | 
						||
| 
								 | 
							
											LOG_SQL_CMD(res);
							 | 
						||
| 
								 | 
							
											cmd[index++]["end_flat"] = "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										cmd[index++]["end_permissions"] = "";
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return true;
							 | 
						||
| 
								 | 
							
								}
							 |