/* * * (c) Copyright Ascensio System Limited 2010-2018 * * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. * * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html * * You can contact Ascensio System SIA by email at sales@onlyoffice.com * * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. * * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains * relevant author attributions when distributing the software. If the display of the logo in its graphic * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" * in every copy of the program you distribute. * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. * */ using ASC.Common; using ASC.Common.Logging; using ASC.Core.Common.Settings; using ASC.CRM.Core; using ASC.CRM.Core.Dao; using ASC.Web.CRM.Core; using Autofac; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; namespace ASC.Web.CRM.Classes { [Scope] public class CurrencyProvider { private readonly ILog _log; private readonly object _syncRoot = new object(); private readonly Dictionary _currencies; private Dictionary _exchangeRates; private DateTime _publisherDate; private const String _formatDate = "yyyy-MM-ddTHH:mm:ss.fffffffK"; public CurrencyProvider(IOptionsMonitor logger, IConfiguration configuration, SettingsManager settingsManager, DaoFactory daoFactory) { _log = logger.Get("ASC"); Configuration = configuration; SettingsManager = settingsManager; var daocur = daoFactory.GetCurrencyInfoDao(); var currencies = daocur.GetAll(); if (currencies == null || currencies.Count == 0) { currencies = new List { new CurrencyInfo("Currency_UnitedStatesDollar", "USD", "$", "US", true, true) }; } _currencies = currencies.ToDictionary(c => c.Abbreviation); } public IConfiguration Configuration { get; } public SettingsManager SettingsManager { get; } public DateTime GetPublisherDate { get { TryToReadPublisherDate(GetExchangesTempPath()); return _publisherDate; } } public CurrencyInfo Get(string currencyAbbreviation) { if (!_currencies.ContainsKey(currencyAbbreviation)) return null; return _currencies[currencyAbbreviation]; } public List GetAll() { return _currencies.Values.OrderBy(v => v.Abbreviation).ToList(); } public List GetBasic() { return _currencies.Values.Where(c => c.IsBasic).OrderBy(v => v.Abbreviation).ToList(); } public List GetOther() { return _currencies.Values.Where(c => !c.IsBasic).OrderBy(v => v.Abbreviation).ToList(); } public Dictionary MoneyConvert(CurrencyInfo baseCurrency) { if (baseCurrency == null) throw new ArgumentNullException("baseCurrency"); if (!_currencies.ContainsKey(baseCurrency.Abbreviation)) throw new ArgumentOutOfRangeException("baseCurrency", "Not found."); var result = new Dictionary(); var rates = GetExchangeRates(); foreach (var ci in GetAll()) { if (baseCurrency.Title == ci.Title) { result.Add(ci, 1); continue; } var key = String.Format("{1}/{0}", baseCurrency.Abbreviation, ci.Abbreviation); if (!rates.ContainsKey(key)) continue; result.Add(ci, rates[key]); } return result; } public bool IsConvertable(String abbreviation) { var findedItem = _currencies.Keys.ToList().Find(item => String.Compare(abbreviation, item) == 0); if (findedItem == null) throw new ArgumentException(abbreviation); return _currencies[findedItem].IsConvertable; } public Decimal MoneyConvert(decimal amount, string from, string to) { if (string.IsNullOrEmpty(from) || string.IsNullOrEmpty(to) || string.Compare(from, to, true) == 0) return amount; var rates = GetExchangeRates(); if (from.Contains('-')) from = new RegionInfo(from).ISOCurrencySymbol; if (to.Contains('-')) to = new RegionInfo(to).ISOCurrencySymbol; var key = string.Format("{0}/{1}", to, from); return Math.Round(rates[key] * amount, 4, MidpointRounding.AwayFromZero); } public decimal MoneyConvertToDefaultCurrency(decimal amount, string from) { return MoneyConvert(amount, from, SettingsManager.Load().DefaultCurrency.Abbreviation); } private bool ObsoleteData() { return _exchangeRates == null || (DateTime.UtcNow.Date.Subtract(_publisherDate.Date).Days > 0); } private string GetExchangesTempPath() { return Path.Combine(Path.GetTempPath(), Path.Combine("onlyoffice", "exchanges")); } private Regex CurRateRegex = new Regex("[a-zA-Z]{3})\">(?[\\d\\.]+)"); private Dictionary GetExchangeRates() { if (ObsoleteData()) { lock (_syncRoot) { if (ObsoleteData()) { try { _exchangeRates = new Dictionary(); var tmppath = GetExchangesTempPath(); TryToReadPublisherDate(tmppath); var updateEnable = Configuration["crm:update:currency:info:enable"] != "false"; var ratesUpdatedFlag = false; foreach (var ci in _currencies.Values.Where(c => c.IsConvertable)) { var filepath = Path.Combine(tmppath, ci.Abbreviation + ".html"); if (updateEnable && 0 < (DateTime.UtcNow.Date - _publisherDate.Date).TotalDays || !File.Exists(filepath)) { var filepath_temp = Path.Combine(tmppath, ci.Abbreviation + "_temp.html"); DownloadCurrencyPage(ci.Abbreviation, filepath_temp); if (File.Exists(filepath_temp)) { if (TryGetRatesFromFile(filepath_temp, ci)) { ratesUpdatedFlag = true; File.Copy(filepath_temp, filepath, true); } File.Delete(filepath_temp); continue; } } if (File.Exists(filepath) && TryGetRatesFromFile(filepath, ci)) { ratesUpdatedFlag = true; } } if (ratesUpdatedFlag) { _publisherDate = DateTime.UtcNow; WritePublisherDate(tmppath); } } catch (Exception error) { _log.Error(error); _publisherDate = DateTime.UtcNow; } } } } return _exchangeRates; } private bool TryGetRatesFromFile(string filepath, CurrencyInfo curCI) { var success = false; var currencyLines = File.ReadAllLines(filepath); for (var i = 0; i < currencyLines.Length; i++) { var line = currencyLines[i]; if (line.Contains("id=\"major-currency-table\"") || line.Contains("id=\"minor-currency-table\"") || line.Contains("id=\"exotic-currency-table\"")) { var currencyInfos = CurRateRegex.Matches(line); if (currencyInfos.Count > 0) { foreach (var curInfo in currencyInfos) { _exchangeRates.Add( String.Format("{0}/{1}", (curInfo as Match).Groups["Currency"].Value.Trim(), curCI.Abbreviation), Convert.ToDecimal((curInfo as Match).Groups["Rate"].Value.Trim(), CultureInfo.InvariantCulture.NumberFormat)); success = true; } } } } return success; } private void TryToReadPublisherDate(string tmppath) { if (_publisherDate == default(DateTime)) { try { var timefile = Path.Combine(tmppath, "last.time"); if (File.Exists(timefile)) { var dateFromFile = File.ReadAllText(timefile); _publisherDate = DateTime.ParseExact(dateFromFile, _formatDate, null); } } catch (Exception err) { _log.Error(err); } } } private void WritePublisherDate(string tmppath) { try { var timefile = Path.Combine(tmppath, "last.time"); File.WriteAllText(timefile, _publisherDate.ToString(_formatDate)); } catch (Exception err) { _log.Error(err); } } private void DownloadCurrencyPage(string currency, string filepath) { try { var dir = Path.GetDirectoryName(filepath); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } var destinationURI = new Uri(String.Format("https://themoneyconverter.com/{0}/{0}.aspx", currency)); var request = (HttpWebRequest)WebRequest.Create(destinationURI); request.Method = "GET"; request.AllowAutoRedirect = true; request.MaximumAutomaticRedirections = 2; request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0"; request.UseDefaultCredentials = true; using (var response = (HttpWebResponse)request.GetResponse()) using (var responseStream = new StreamReader(response.GetResponseStream())) { var data = responseStream.ReadToEnd(); File.WriteAllText(filepath, data); } } catch (Exception error) { _log.Error(error); } } } }