// (c) Copyright Ascensio System SIA 2010-2022 // // This program is a free software product. // You can redistribute it and/or modify it under the terms // of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software // Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see // the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html // // You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. // // The interactive user interfaces in modified source and object code versions of the Program must // display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. // // Pursuant to Section 7(b) of the License you must retain the original Product logo when // distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under // trademark law for use of our trademarks. // // All the Product's GUI elements, including illustrations and icon sets, as well as technical writing // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode /* * * (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 System; using System.Collections.Generic; using System.IO; using ASC.Common; using ASC.Common.Utils; using ASC.Core; using ASC.Web.Core.Calendars; using Microsoft.Extensions.Logging; namespace ASC.Calendar.iCalParser { [Scope] public class DDayICalParser { public IServiceProvider ServiceProvider { get; } private readonly ILogger Log; public TenantManager TenantManager { get; } public TimeZoneConverter TimeZoneConverter { get; } public AuthContext AuthContext { get; } public iCalendar ICalendar { get; } public DDayICalParser( IServiceProvider serviceProvider, TenantManager tenantManager, TimeZoneConverter timeZoneConverter, AuthContext authContext, ILogger options) { ServiceProvider = serviceProvider; TenantManager = tenantManager; TimeZoneConverter = timeZoneConverter; AuthContext = authContext; Log = options; } public Ical.Net.CalendarCollection DeserializeCalendar(string iCalCalendarString) { if (string.IsNullOrEmpty(iCalCalendarString)) return null; try { using (var stringReader = new StringReader(iCalCalendarString)) { return Ical.Net.CalendarCollection.Load(stringReader); } } catch (Exception ex) { Log.LogError(ex, "DeserializeCalendar"); return null; } } public Ical.Net.CalendarCollection DeserializeCalendar(TextReader reader) { try { return Ical.Net.CalendarCollection.Load(reader); } catch (Exception ex) { Log.LogError(ex, "DeserializeCalendar"); return null; } } public string SerializeCalendar(Ical.Net.Calendar calendar) { try { var serializer = new Ical.Net.Serialization.CalendarSerializer(); return serializer.SerializeToString(calendar); } catch (Exception ex) { Log.LogError(ex, "SerializeCalendar"); return null; } } public Ical.Net.CalendarComponents.CalendarEvent DeserializeEvent(string iCalEventString) { if (string.IsNullOrEmpty(iCalEventString)) return null; try { using (var stringReader = new StringReader(iCalEventString)) { var serializer = new Ical.Net.Serialization.EventSerializer(); return (Ical.Net.CalendarComponents.CalendarEvent)serializer.Deserialize(stringReader); } } catch (Exception ex) { Log.LogError(ex, "DeserializeEvent"); return null; } } public Ical.Net.CalendarComponents.CalendarEvent DeserializeEvent(TextReader stringReader) { try { var serializer = new Ical.Net.Serialization.EventSerializer(); return (Ical.Net.CalendarComponents.CalendarEvent)serializer.Deserialize(stringReader); } catch (Exception ex) { Log.LogError(ex, "DeserializeEvent"); return null; } } public string SerializeEvent(Ical.Net.CalendarComponents.CalendarEvent eventObj) { try { var serializer = new Ical.Net.Serialization.EventSerializer(); return serializer.SerializeToString(eventObj); } catch (Exception ex) { Log.LogError(ex, "SerializeEvent"); return null; } } public Ical.Net.DataTypes.RecurrencePattern DeserializeRecurrencePattern(string iCalRecurrencePatternString) { if (string.IsNullOrEmpty(iCalRecurrencePatternString)) return null; try { using (var stringReader = new StringReader(iCalRecurrencePatternString)) { var serializer = new Ical.Net.Serialization.DataTypes.RecurrencePatternSerializer(); return (Ical.Net.DataTypes.RecurrencePattern)serializer.Deserialize(stringReader); } } catch (Exception ex) { Log.LogError(ex, "DeserializeRecurrencePattern"); return null; } } public Ical.Net.DataTypes.RecurrencePattern DeserializeRecurrencePattern(TextReader stringReader) { try { var serializer = new Ical.Net.Serialization.DataTypes.RecurrencePatternSerializer(); return (Ical.Net.DataTypes.RecurrencePattern)serializer.Deserialize(stringReader); } catch (Exception ex) { Log.LogError(ex, "DeserializeRecurrencePattern"); return null; } } public string SerializeRecurrencePattern(Ical.Net.DataTypes.RecurrencePattern recurrencePattern) { try { var serializer = new Ical.Net.Serialization.DataTypes.RecurrencePatternSerializer(); return serializer.SerializeToString(recurrencePattern); } catch (Exception ex) { Log.LogError(ex, "SerializeRecurrencePattern"); return null; } } /* public BaseCalendar ConvertCalendar(Ical.Net.Calendar calandarObj) { if (calandarObj == null) return null; var icalendar = new iCalendar(AuthContext, TimeZoneConverter, TenantManager); var result = new BusinessObjects.Calendar(AuthContext, TimeZoneConverter, icalendar, DataProvider); result.Name = string.IsNullOrEmpty(calandarObj.Name) ? calandarObj.Properties.ContainsKey("X-WR-CALNAME") ? calandarObj.Properties["X-WR-CALNAME"].Value.ToString() : string.Empty : calandarObj.Name; result.Description = calandarObj.Properties.ContainsKey("X-WR-CALDESC") ? calandarObj.Properties["X-WR-CALDESC"].Value.ToString() : string.Empty; var tzids = calandarObj.TimeZones.Select(x => x.TzId).Where(x => !string.IsNullOrEmpty(x)).ToList(); result.TimeZone = tzids.Any() ? TimeZoneConverter.GetTimeZone(tzids.First()) : (calandarObj.Properties.ContainsKey("X-WR-TIMEZONE") ? TimeZoneConverter.GetTimeZone( calandarObj.Properties["X-WR-TIMEZONE"].Value.ToString()) : TimeZoneConverter.GetTimeZone(TenantManager.GetCurrentTenant().TimeZone)); return result; }*/ public Ical.Net.Calendar ConvertCalendar(BaseCalendar calandarObj) { if (calandarObj == null) return null; var result = new Ical.Net.Calendar(); result.Method = Ical.Net.CalendarMethods.Publish; result.Scale = Ical.Net.CalendarScales.Gregorian; result.Version = Ical.Net.LibraryMetadata.Version; result.ProductId = "-//Ascensio System//OnlyOffice Calendar//EN"; if (!string.IsNullOrEmpty(calandarObj.Name)) { result.AddProperty("X-WR-CALNAME", calandarObj.Name); } if (!string.IsNullOrEmpty(calandarObj.Description)) { result.AddProperty("X-WR-CALDESC", calandarObj.Description); } if (calandarObj.TimeZone == null) calandarObj.TimeZone = TimeZoneConverter.GetTimeZone(TenantManager.GetCurrentTenant().TimeZone); var olsonTzId = TimeZoneConverter.WindowsTzId2OlsonTzId(calandarObj.TimeZone.Id); var olsonTz = olsonTzId == calandarObj.TimeZone.Id ? calandarObj.TimeZone : TimeZoneInfo.CreateCustomTimeZone(olsonTzId, calandarObj.TimeZone.GetOffset(true), calandarObj.TimeZone.DisplayName, calandarObj.TimeZone.StandardName); result.AddTimeZone(Ical.Net.CalendarComponents.VTimeZone.FromSystemTimeZone(olsonTz)); result.AddProperty("X-WR-TIMEZONE", olsonTzId); return result; } /*public BaseEvent ConvertEvent(Ical.Net.CalendarComponents.CalendarEvent eventObj) { if (eventObj == null) return null; var result = new BusinessObjects.Event(AuthContext, TimeZoneConverter, ICalendar, DataProvider); result.Name = eventObj.Summary; result.Description = eventObj.Description; result.AllDayLong = eventObj.IsAllDay; result.Uid = eventObj.Uid; result.UtcStartDate = ToUtc(eventObj.Start); result.UtcEndDate = ToUtc(eventObj.End); result.UtcUpdateDate = ToUtc(eventObj.Created); var recurrenceRuleStr = string.Empty; if (eventObj.RecurrenceRules != null && eventObj.RecurrenceRules.Any()) { var recurrenceRules = eventObj.RecurrenceRules.ToList(); recurrenceRuleStr = SerializeRecurrencePattern(recurrenceRules.First()); } result.RecurrenceRule = RecurrenceRule.Parse(recurrenceRuleStr); if (eventObj.ExceptionDates != null && eventObj.ExceptionDates.Any()) { result.RecurrenceRule.ExDates = new List(); var exceptionDates = eventObj.ExceptionDates.ToList(); foreach (var periodList in exceptionDates.First()) { var start = ToUtc(periodList.StartTime); result.RecurrenceRule.ExDates.Add(new RecurrenceRule.ExDate { Date = start, isDateTime = start != start.Date }); } } result.Status = ConvertEventStatus(eventObj.Status); return result; }*/ public Ical.Net.CalendarComponents.CalendarEvent ConvertEvent(BaseEvent eventObj) { if (eventObj == null) return null; var result = new Ical.Net.CalendarComponents.CalendarEvent(); result.Summary = eventObj.Name; result.Location = string.Empty; result.Description = eventObj.Description; result.IsAllDay = eventObj.AllDayLong; result.Uid = eventObj.Uid; result.Start = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(eventObj.UtcStartDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); result.End = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(eventObj.UtcEndDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); result.Created = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(eventObj.UtcUpdateDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); result.RecurrenceRules = new List(); var rrule = eventObj.RecurrenceRule.ToString(true); if (!string.IsNullOrEmpty(rrule)) { result.RecurrenceRules.Add(new Ical.Net.DataTypes.RecurrencePattern(rrule)); } result.Status = ConvertEventStatus(eventObj.Status); return result; } public EventStatus ConvertEventStatus(string status) { switch (status) { case Ical.Net.EventStatus.Tentative: return EventStatus.Tentative; case Ical.Net.EventStatus.Confirmed: return EventStatus.Confirmed; case Ical.Net.EventStatus.Cancelled: return EventStatus.Cancelled; } return EventStatus.Tentative; } public string ConvertEventStatus(EventStatus status) { switch (status) { case EventStatus.Tentative: return Ical.Net.EventStatus.Tentative; case EventStatus.Confirmed: return Ical.Net.EventStatus.Confirmed; case EventStatus.Cancelled: return Ical.Net.EventStatus.Cancelled; } return Ical.Net.EventStatus.Tentative; } public Ical.Net.CalendarComponents.CalendarEvent CreateEvent(string name, string description, DateTime startUtcDate, DateTime endUtcDate, string repeatType, bool isAllDayLong, EventStatus status) { var evt = new Ical.Net.CalendarComponents.CalendarEvent { Summary = name, Location = string.Empty, Description = description, IsAllDay = isAllDayLong, DtStamp = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc), TimeZoneInfo.Utc.Id), Start = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(startUtcDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id), End = new Ical.Net.DataTypes.CalDateTime(DateTime.SpecifyKind(endUtcDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id), RecurrenceRules = new List(), Status = ConvertEventStatus(status) }; var rrule = RecurrenceRule.Parse(repeatType).ToString(true); if (!string.IsNullOrEmpty(rrule)) { evt.RecurrenceRules.Add(new Ical.Net.DataTypes.RecurrencePattern(rrule)); } return evt; } public DateTime ToUtc(Ical.Net.DataTypes.IDateTime dateTime) { if (dateTime.IsUtc || string.IsNullOrEmpty(dateTime.TzId) || dateTime.TzId.Equals("UTC", StringComparison.InvariantCultureIgnoreCase)) return dateTime.Value; if (dateTime.AsUtc != dateTime.Value) return dateTime.AsUtc; var timeZone = TimeZoneConverter.GetTimeZone(dateTime.TzId); var utcOffse = timeZone.GetOffset(); return dateTime.Value - utcOffse; } } }