using System; using System.Collections.Generic; using System.Linq; using Jackett.Common.Services.Interfaces; using Jackett.Common.Utils; using Newtonsoft.Json.Linq; namespace Jackett.Common.Models.IndexerConfig { public class ConfigurationData { private const string PASSWORD_REPLACEMENT = "|||%%PREVJACKPASSWD%%|||"; protected Dictionary dynamics = new Dictionary(); // list for dynamic items public enum ItemType { InputString, InputBool, InputCheckbox, InputSelect, DisplayImage, DisplayInfo, HiddenData, Recaptcha } public HiddenItem CookieHeader { get; private set; } = new HiddenItem { Name = "CookieHeader" }; public HiddenItem LastError { get; private set; } = new HiddenItem { Name = "LastError" }; public StringItem SiteLink { get; private set; } = new StringItem { Name = "Site Link" }; public ConfigurationData() { } public ConfigurationData(JToken json, IProtectionService ps) => LoadValuesFromJson(json, ps); public void LoadValuesFromJson(JToken json, IProtectionService ps = null) { if (json == null) return; var arr = (JArray)json; // transistion from alternatelink to sitelink var alternatelinkItem = arr.FirstOrDefault(f => f.Value("id") == "alternatelink"); if (alternatelinkItem != null && !string.IsNullOrEmpty(alternatelinkItem.Value("value"))) { //SiteLink.Value = alternatelinkItem.Value("value"); } foreach (var item in GetItems(forDisplay: false)) { var arrItem = arr.FirstOrDefault(f => f.Value("id") == item.ID); if (arrItem == null) continue; switch (item.ItemType) { case ItemType.InputString: var sItem = (StringItem)item; var newValue = arrItem.Value("value"); if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase)) { if (newValue != PASSWORD_REPLACEMENT) { sItem.Value = newValue; if (ps != null) sItem.Value = ps.UnProtect(newValue); } } else { sItem.Value = newValue; } break; case ItemType.HiddenData: ((HiddenItem)item).Value = arrItem.Value("value"); break; case ItemType.InputBool: ((BoolItem)item).Value = arrItem.Value("value"); break; case ItemType.InputCheckbox: var values = arrItem.Value("values"); if (values != null) ((CheckboxItem)item).Values = values.Values().ToArray(); break; case ItemType.InputSelect: ((SelectItem)item).Value = arrItem.Value("value"); break; case ItemType.Recaptcha: ((RecaptchaItem)item).Value = arrItem.Value("value"); ((RecaptchaItem)item).Cookie = arrItem.Value("cookie"); ((RecaptchaItem)item).Version = arrItem.Value("version"); ((RecaptchaItem)item).Challenge = arrItem.Value("challenge"); break; } } } public JToken ToJson(IProtectionService ps, bool forDisplay = true) { var items = GetItems(forDisplay); var jArray = new JArray(); foreach (var item in items) { var jObject = new JObject(); jObject["id"] = item.ID; jObject["type"] = item.ItemType.ToString().ToLower(); jObject["name"] = item.Name; switch (item.ItemType) { case ItemType.Recaptcha: jObject["sitekey"] = ((RecaptchaItem)item).SiteKey; jObject["version"] = ((RecaptchaItem)item).Version; break; case ItemType.InputString: case ItemType.HiddenData: case ItemType.DisplayInfo: var value = ((StringItem)item).Value; if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase)) // if we chagne this logic we've to change the MigratedFromDPAPI() logic too, #2114 is realted { if (string.IsNullOrEmpty(value)) value = string.Empty; else if (forDisplay) value = PASSWORD_REPLACEMENT; else if (ps != null) value = ps.Protect(value); } jObject["value"] = value; break; case ItemType.InputBool: jObject["value"] = ((BoolItem)item).Value; break; case ItemType.InputCheckbox: jObject["values"] = new JArray(((CheckboxItem)item).Values); jObject["options"] = new JObject(); foreach (var option in ((CheckboxItem)item).Options) { jObject["options"][option.Key] = option.Value; } break; case ItemType.InputSelect: jObject["value"] = ((SelectItem)item).Value; jObject["options"] = new JObject(); foreach (var option in ((SelectItem)item).Options) { jObject["options"][option.Key] = option.Value; } break; case ItemType.DisplayImage: var dataUri = DataUrlUtils.BytesToDataUrl(((ImageItem)item).Value, "image/jpeg"); jObject["value"] = dataUri; break; } jArray.Add(jObject); } return jArray; } private Item[] GetItems(bool forDisplay) { var properties = GetType() .GetProperties() .Where(p => p.CanRead) .Where(p => p.PropertyType.IsSubclassOf(typeof(Item))) .Select(p => (Item)p.GetValue(this)).ToList(); // remove/insert Site Link manualy to make sure it shows up first properties.Remove(SiteLink); properties.Insert(0, SiteLink); properties.AddRange(dynamics.Values); if (!forDisplay) { properties = properties .Where(p => p.ItemType == ItemType.HiddenData || p.ItemType == ItemType.InputBool || p.ItemType == ItemType.InputString || p.ItemType == ItemType.InputCheckbox || p.ItemType == ItemType.InputSelect || p.ItemType == ItemType.Recaptcha || p.ItemType == ItemType.DisplayInfo) .ToList(); } return properties.ToArray(); } public void AddDynamic(string ID, Item item) => dynamics[ID] = item; // TODO Convert to TryGetValue to avoid throwing exception public Item GetDynamic(string ID) { try { return dynamics[ID]; } catch (KeyNotFoundException) { return null; } } public Item GetDynamicByName(string Name) => dynamics.Values.FirstOrDefault(i => string.Equals(i.Name, Name, StringComparison.InvariantCultureIgnoreCase)); public class Item { public ItemType ItemType { get; set; } public string Name { get; set; } public string ID => Name.Replace(" ", "").ToLower(); } public class HiddenItem : StringItem { public HiddenItem(string value = "") { Value = value; ItemType = ItemType.HiddenData; } } public class DisplayItem : StringItem { public DisplayItem(string value) { Value = value; ItemType = ItemType.DisplayInfo; } } public class StringItem : Item { public string SiteKey { get; set; } public string Value { get; set; } public string Cookie { get; set; } public StringItem() => ItemType = ItemType.InputString; } public class RecaptchaItem : StringItem { public string Version { get; set; } public string Challenge { get; set; } public RecaptchaItem() { Version = "2"; ItemType = ItemType.Recaptcha; } } public class BoolItem : Item { public bool Value { get; set; } public BoolItem() => ItemType = ItemType.InputBool; } public class ImageItem : Item { public byte[] Value { get; set; } public ImageItem() => ItemType = ItemType.DisplayImage; } public class CheckboxItem : Item { public string[] Values { get; set; } public Dictionary Options { get; } public CheckboxItem(Dictionary options) { ItemType = ItemType.InputCheckbox; Options = options; } } public class SelectItem : Item { public string Value { get; set; } public Dictionary Options { get; } public SelectItem(Dictionary options) { ItemType = ItemType.InputSelect; Options = options; } } //public abstract Item[] GetItems(); } }